blob: eb8ca9384b41f3565adf08bfa25a18a551353f8d [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 Tkac053597c2010-11-11 13:21:55 +0000437 writeInt(secTypes[i]);
438 return secTypes[i];
439 }
440
441 throw new Exception("No valid VeNCrypt sub-type");
442 }
443
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000444 //
445 // Perform "no authentication".
446 //
447
448 void authenticateNone() throws Exception {
449 if (clientMinor >= 8)
450 readSecurityResult("No authentication");
451 }
452
453 //
454 // Perform standard VNC Authentication.
455 //
456
457 void authenticateVNC(String pw) throws Exception {
458 byte[] challenge = new byte[16];
459 readFully(challenge);
460
461 if (pw.length() > 8)
462 pw = pw.substring(0, 8); // Truncate to 8 chars
463
464 // Truncate password on the first zero byte.
465 int firstZero = pw.indexOf(0);
466 if (firstZero != -1)
467 pw = pw.substring(0, firstZero);
468
469 byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
470 System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
471
472 DesCipher des = new DesCipher(key);
473
474 des.encrypt(challenge, 0, challenge, 0);
475 des.encrypt(challenge, 8, challenge, 8);
476
477 os.write(challenge);
478
479 readSecurityResult("VNC authentication");
480 }
481
Adam Tkac4be9da82010-11-18 14:00:12 +0000482 void authenticateTLS() throws Exception {
483 TLSTunnel tunnel = new TLSTunnel(sock);
484 tunnel.setup (this);
485 }
486
Adam Tkac6a663cd2010-11-11 14:06:42 +0000487 void authenticatePlain(String User, String Password) throws Exception {
488 byte[] user=User.getBytes();
489 byte[] password=Password.getBytes();
490 writeInt(user.length);
491 writeInt(password.length);
492 os.write(user);
493 os.write(password);
494
495 readSecurityResult("Plain authentication");
496 }
497
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000498 //
499 // Read security result.
500 // Throws an exception on authentication failure.
501 //
502
503 void readSecurityResult(String authType) throws Exception {
504 int securityResult = readU32();
505
506 switch (securityResult) {
507 case VncAuthOK:
508 System.out.println(authType + ": success");
509 break;
510 case VncAuthFailed:
511 if (clientMinor >= 8)
512 readConnFailedReason();
513 throw new Exception(authType + ": failed");
514 case VncAuthTooMany:
515 throw new Exception(authType + ": failed, too many tries");
516 default:
517 throw new Exception(authType + ": unknown result " + securityResult);
518 }
519 }
520
521 //
522 // Read the string describing the reason for a connection failure,
523 // and throw an exception.
524 //
525
526 void readConnFailedReason() throws Exception {
527 int reasonLen = readU32();
528 byte[] reason = new byte[reasonLen];
529 readFully(reason);
530 throw new Exception(new String(reason));
531 }
532
533 //
534 // Initialize capability lists (TightVNC protocol extensions).
535 //
536
537 void initCapabilities() {
538 tunnelCaps = new CapsContainer();
539 authCaps = new CapsContainer();
540 serverMsgCaps = new CapsContainer();
541 clientMsgCaps = new CapsContainer();
542 encodingCaps = new CapsContainer();
543
544 // Supported authentication methods
545 authCaps.add(AuthNone, StandardVendor, SigAuthNone,
546 "No authentication");
547 authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
548 "Standard VNC password authentication");
549
550 // Supported non-standard server-to-client messages
551 serverMsgCaps.add(EndOfContinuousUpdates, TightVncVendor,
552 SigEndOfContinuousUpdates,
553 "End of continuous updates notification");
554
555 // Supported non-standard client-to-server messages
556 clientMsgCaps.add(EnableContinuousUpdates, TightVncVendor,
557 SigEnableContinuousUpdates,
558 "Enable/disable continuous updates");
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +0000559 clientMsgCaps.add(VideoRectangleSelection, TightVncVendor,
560 SigVideoRectangleSelection,
561 "Select a rectangle to be treated as video");
enikeyd7653562008-12-25 11:02:56 +0000562 clientMsgCaps.add(VideoFreeze, TightVncVendor,
563 SigVideoFreeze,
564 "Disable/enable video rectangle");
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000565
566 // Supported encoding types
567 encodingCaps.add(EncodingCopyRect, StandardVendor,
568 SigEncodingCopyRect, "Standard CopyRect encoding");
569 encodingCaps.add(EncodingRRE, StandardVendor,
570 SigEncodingRRE, "Standard RRE encoding");
571 encodingCaps.add(EncodingCoRRE, StandardVendor,
572 SigEncodingCoRRE, "Standard CoRRE encoding");
573 encodingCaps.add(EncodingHextile, StandardVendor,
574 SigEncodingHextile, "Standard Hextile encoding");
575 encodingCaps.add(EncodingZRLE, StandardVendor,
576 SigEncodingZRLE, "Standard ZRLE encoding");
577 encodingCaps.add(EncodingZlib, TridiaVncVendor,
578 SigEncodingZlib, "Zlib encoding");
579 encodingCaps.add(EncodingTight, TightVncVendor,
580 SigEncodingTight, "Tight encoding");
581
582 // Supported pseudo-encoding types
583 encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
584 SigEncodingCompressLevel0, "Compression level");
585 encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
586 SigEncodingQualityLevel0, "JPEG quality level");
587 encodingCaps.add(EncodingXCursor, TightVncVendor,
588 SigEncodingXCursor, "X-style cursor shape update");
589 encodingCaps.add(EncodingRichCursor, TightVncVendor,
590 SigEncodingRichCursor, "Rich-color cursor shape update");
591 encodingCaps.add(EncodingPointerPos, TightVncVendor,
592 SigEncodingPointerPos, "Pointer position update");
593 encodingCaps.add(EncodingLastRect, TightVncVendor,
594 SigEncodingLastRect, "LastRect protocol extension");
595 encodingCaps.add(EncodingNewFBSize, TightVncVendor,
596 SigEncodingNewFBSize, "Framebuffer size change");
597 }
598
599 //
600 // Setup tunneling (TightVNC protocol extensions)
601 //
602
603 void setupTunneling() throws IOException {
604 int nTunnelTypes = readU32();
605 if (nTunnelTypes != 0) {
606 readCapabilityList(tunnelCaps, nTunnelTypes);
607
608 // We don't support tunneling yet.
609 writeInt(NoTunneling);
610 }
611 }
612
613 //
614 // Negotiate authentication scheme (TightVNC protocol extensions)
615 //
616
617 int negotiateAuthenticationTight() throws Exception {
618 int nAuthTypes = readU32();
619 if (nAuthTypes == 0)
620 return AuthNone;
621
622 readCapabilityList(authCaps, nAuthTypes);
623 for (int i = 0; i < authCaps.numEnabled(); i++) {
624 int authType = authCaps.getByOrder(i);
625 if (authType == AuthNone || authType == AuthVNC) {
626 writeInt(authType);
627 return authType;
628 }
629 }
630 throw new Exception("No suitable authentication scheme found");
631 }
632
633 //
634 // Read a capability list (TightVNC protocol extensions)
635 //
636
637 void readCapabilityList(CapsContainer caps, int count) throws IOException {
638 int code;
639 byte[] vendor = new byte[4];
640 byte[] name = new byte[8];
641 for (int i = 0; i < count; i++) {
642 code = readU32();
643 readFully(vendor);
644 readFully(name);
645 caps.enable(new CapabilityInfo(code, vendor, name));
646 }
647 }
648
649 //
650 // Write a 32-bit integer into the output stream.
651 //
652
653 void writeInt(int value) throws IOException {
654 byte[] b = new byte[4];
655 b[0] = (byte) ((value >> 24) & 0xff);
656 b[1] = (byte) ((value >> 16) & 0xff);
657 b[2] = (byte) ((value >> 8) & 0xff);
658 b[3] = (byte) (value & 0xff);
659 os.write(b);
660 }
661
662 //
663 // Write the client initialisation message
664 //
665
666 void writeClientInit() throws IOException {
667 if (viewer.options.shareDesktop) {
668 os.write(1);
669 } else {
670 os.write(0);
671 }
672 viewer.options.disableShareDesktop();
673 }
674
675
676 //
677 // Read the server initialisation message
678 //
679
680 String desktopName;
681 int framebufferWidth, framebufferHeight;
682 int bitsPerPixel, depth;
683 boolean bigEndian, trueColour;
684 int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
685
686 void readServerInit() throws IOException {
687 framebufferWidth = readU16();
688 framebufferHeight = readU16();
689 bitsPerPixel = readU8();
690 depth = readU8();
691 bigEndian = (readU8() != 0);
692 trueColour = (readU8() != 0);
693 redMax = readU16();
694 greenMax = readU16();
695 blueMax = readU16();
696 redShift = readU8();
697 greenShift = readU8();
698 blueShift = readU8();
699 byte[] pad = new byte[3];
700 readFully(pad);
701 int nameLength = readU32();
702 byte[] name = new byte[nameLength];
703 readFully(name);
704 desktopName = new String(name);
705
706 // Read interaction capabilities (TightVNC protocol extensions)
707 if (protocolTightVNC) {
708 int nServerMessageTypes = readU16();
709 int nClientMessageTypes = readU16();
710 int nEncodingTypes = readU16();
711 readU16();
712 readCapabilityList(serverMsgCaps, nServerMessageTypes);
713 readCapabilityList(clientMsgCaps, nClientMessageTypes);
714 readCapabilityList(encodingCaps, nEncodingTypes);
715 }
716
717 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
718 viewer.options.disableContUpdates();
719 }
720
721 inNormalProtocol = true;
722 }
723
724
725 //
726 // Create session file and write initial protocol messages into it.
727 //
728
729 void startSession(String fname) throws IOException {
730 rec = new SessionRecorder(fname);
731 rec.writeHeader();
732 rec.write(versionMsg_3_3.getBytes());
733 rec.writeIntBE(SecTypeNone);
734 rec.writeShortBE(framebufferWidth);
735 rec.writeShortBE(framebufferHeight);
736 byte[] fbsServerInitMsg = {
737 32, 24, 0, 1, 0,
738 (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
739 16, 8, 0, 0, 0, 0
740 };
741 rec.write(fbsServerInitMsg);
742 rec.writeIntBE(desktopName.length());
743 rec.write(desktopName.getBytes());
744 numUpdatesInSession = 0;
745
746 // FIXME: If there were e.g. ZRLE updates only, that should not
747 // affect recording of Zlib and Tight updates. So, actually
748 // we should maintain separate flags for Zlib, ZRLE and
749 // Tight, instead of one ``wereZlibUpdates'' variable.
750 //
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000751
752 zlibWarningShown = false;
753 tightWarningShown = false;
754 }
755
756 //
757 // Close session file.
758 //
759
760 void closeSession() throws IOException {
761 if (rec != null) {
762 rec.close();
763 rec = null;
764 }
765 }
766
767
768 //
769 // Set new framebuffer size
770 //
771
772 void setFramebufferSize(int width, int height) {
773 framebufferWidth = width;
774 framebufferHeight = height;
775 }
776
777
778 //
779 // Read the server message type
780 //
781
782 int readServerMessageType() throws IOException {
783 int msgType = readU8();
784
785 // If the session is being recorded:
786 if (rec != null) {
787 if (msgType == Bell) { // Save Bell messages in session files.
788 rec.writeByte(msgType);
789 if (numUpdatesInSession > 0)
790 rec.flush();
791 }
792 }
793
794 return msgType;
795 }
796
797
798 //
799 // Read a FramebufferUpdate message
800 //
801
802 int updateNRects;
803
804 void readFramebufferUpdate() throws IOException {
805 skipBytes(1);
806 updateNRects = readU16();
807
808 // If the session is being recorded:
809 if (rec != null) {
810 rec.writeByte(FramebufferUpdate);
811 rec.writeByte(0);
812 rec.writeShortBE(updateNRects);
813 }
814
815 numUpdatesInSession++;
816 }
817
enikey2f0294e2008-12-24 08:18:54 +0000818 //
819 // Returns true if encoding is not pseudo
820 //
821 // FIXME: Find better way to differ pseudo and real encodings
822 //
823
824 boolean isRealDecoderEncoding(int encoding) {
825 if ((encoding >= 1) && (encoding <= 16)) {
826 return true;
827 }
828 return false;
829 }
830
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000831 // Read a FramebufferUpdate rectangle header
832
833 int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
834
835 void readFramebufferUpdateRectHdr() throws Exception {
836 updateRectX = readU16();
837 updateRectY = readU16();
838 updateRectW = readU16();
839 updateRectH = readU16();
840 updateRectEncoding = readU32();
841
842 if (updateRectEncoding == EncodingZlib ||
843 updateRectEncoding == EncodingZRLE ||
844 updateRectEncoding == EncodingTight)
845 wereZlibUpdates = true;
846
847 // If the session is being recorded:
848 if (rec != null) {
849 if (numUpdatesInSession > 1)
850 rec.flush(); // Flush the output on each rectangle.
851 rec.writeShortBE(updateRectX);
852 rec.writeShortBE(updateRectY);
853 rec.writeShortBE(updateRectW);
854 rec.writeShortBE(updateRectH);
enikey2f0294e2008-12-24 08:18:54 +0000855
856 //
857 // If this is pseudo encoding or CopyRect that write encoding ID
858 // in this place. All real encoding ID will be written to record stream
859 // in decoder classes.
enikey2f0294e2008-12-24 08:18:54 +0000860
enikey7716b712008-12-25 11:45:52 +0000861 if (((!isRealDecoderEncoding(updateRectEncoding))) && (rec != null)) {
enikey2f0294e2008-12-24 08:18:54 +0000862 rec.writeIntBE(updateRectEncoding);
863 }
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000864 }
865
866 if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
867 return;
868
869 if (updateRectX + updateRectW > framebufferWidth ||
870 updateRectY + updateRectH > framebufferHeight) {
871 throw new Exception("Framebuffer update rectangle too large: " +
872 updateRectW + "x" + updateRectH + " at (" +
873 updateRectX + "," + updateRectY + ")");
874 }
875 }
876
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000877 //
878 // Read a ServerCutText message
879 //
880
881 String readServerCutText() throws IOException {
882 skipBytes(3);
883 int len = readU32();
884 byte[] text = new byte[len];
885 readFully(text);
886 return new String(text);
887 }
888
889
890 //
891 // Read an integer in compact representation (1..3 bytes).
892 // Such format is used as a part of the Tight encoding.
893 // Also, this method records data if session recording is active and
894 // the viewer's recordFromBeginning variable is set to true.
895 //
896
897 int readCompactLen() throws IOException {
898 int[] portion = new int[3];
899 portion[0] = readU8();
900 int byteCount = 1;
901 int len = portion[0] & 0x7F;
902 if ((portion[0] & 0x80) != 0) {
903 portion[1] = readU8();
904 byteCount++;
905 len |= (portion[1] & 0x7F) << 7;
906 if ((portion[1] & 0x80) != 0) {
907 portion[2] = readU8();
908 byteCount++;
909 len |= (portion[2] & 0xFF) << 14;
910 }
911 }
912
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000913 return len;
914 }
915
916
917 //
918 // Write a FramebufferUpdateRequest message
919 //
920
921 void writeFramebufferUpdateRequest(int x, int y, int w, int h,
922 boolean incremental)
923 throws IOException
924 {
925 byte[] b = new byte[10];
926
927 b[0] = (byte) FramebufferUpdateRequest;
928 b[1] = (byte) (incremental ? 1 : 0);
929 b[2] = (byte) ((x >> 8) & 0xff);
930 b[3] = (byte) (x & 0xff);
931 b[4] = (byte) ((y >> 8) & 0xff);
932 b[5] = (byte) (y & 0xff);
933 b[6] = (byte) ((w >> 8) & 0xff);
934 b[7] = (byte) (w & 0xff);
935 b[8] = (byte) ((h >> 8) & 0xff);
936 b[9] = (byte) (h & 0xff);
937
938 os.write(b);
939 }
940
941
942 //
943 // Write a SetPixelFormat message
944 //
945
946 void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
947 boolean trueColour,
948 int redMax, int greenMax, int blueMax,
949 int redShift, int greenShift, int blueShift)
950 throws IOException
951 {
952 byte[] b = new byte[20];
953
954 b[0] = (byte) SetPixelFormat;
955 b[4] = (byte) bitsPerPixel;
956 b[5] = (byte) depth;
957 b[6] = (byte) (bigEndian ? 1 : 0);
958 b[7] = (byte) (trueColour ? 1 : 0);
959 b[8] = (byte) ((redMax >> 8) & 0xff);
960 b[9] = (byte) (redMax & 0xff);
961 b[10] = (byte) ((greenMax >> 8) & 0xff);
962 b[11] = (byte) (greenMax & 0xff);
963 b[12] = (byte) ((blueMax >> 8) & 0xff);
964 b[13] = (byte) (blueMax & 0xff);
965 b[14] = (byte) redShift;
966 b[15] = (byte) greenShift;
967 b[16] = (byte) blueShift;
968
969 os.write(b);
970 }
971
972
973 //
974 // Write a FixColourMapEntries message. The values in the red, green and
975 // blue arrays are from 0 to 65535.
976 //
977
978 void writeFixColourMapEntries(int firstColour, int nColours,
979 int[] red, int[] green, int[] blue)
980 throws IOException
981 {
982 byte[] b = new byte[6 + nColours * 6];
983
984 b[0] = (byte) FixColourMapEntries;
985 b[2] = (byte) ((firstColour >> 8) & 0xff);
986 b[3] = (byte) (firstColour & 0xff);
987 b[4] = (byte) ((nColours >> 8) & 0xff);
988 b[5] = (byte) (nColours & 0xff);
989
990 for (int i = 0; i < nColours; i++) {
991 b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff);
992 b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
993 b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
994 b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
995 b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
996 b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
997 }
enikey2f0294e2008-12-24 08:18:54 +0000998
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000999 os.write(b);
1000 }
1001
1002
1003 //
1004 // Write a SetEncodings message
1005 //
1006
1007 void writeSetEncodings(int[] encs, int len) throws IOException {
1008 byte[] b = new byte[4 + 4 * len];
1009
1010 b[0] = (byte) SetEncodings;
1011 b[2] = (byte) ((len >> 8) & 0xff);
1012 b[3] = (byte) (len & 0xff);
1013
1014 for (int i = 0; i < len; i++) {
1015 b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
1016 b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
1017 b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
1018 b[7 + 4 * i] = (byte) (encs[i] & 0xff);
1019 }
1020
1021 os.write(b);
1022 }
1023
1024
1025 //
1026 // Write a ClientCutText message
1027 //
1028
1029 void writeClientCutText(String text) throws IOException {
1030 byte[] b = new byte[8 + text.length()];
1031
1032 b[0] = (byte) ClientCutText;
1033 b[4] = (byte) ((text.length() >> 24) & 0xff);
1034 b[5] = (byte) ((text.length() >> 16) & 0xff);
1035 b[6] = (byte) ((text.length() >> 8) & 0xff);
1036 b[7] = (byte) (text.length() & 0xff);
1037
1038 System.arraycopy(text.getBytes(), 0, b, 8, text.length());
1039
1040 os.write(b);
1041 }
1042
1043
1044 //
1045 // A buffer for putting pointer and keyboard events before being sent. This
enikey2f0294e2008-12-24 08:18:54 +00001046 // is to ensure that multiple RFB events generated from a single Java Event
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001047 // will all be sent in a single network packet. The maximum possible
1048 // length is 4 modifier down events, a single key event followed by 4
1049 // modifier up events i.e. 9 key events or 72 bytes.
1050 //
1051
1052 byte[] eventBuf = new byte[72];
1053 int eventBufLen;
1054
1055
1056 // Useful shortcuts for modifier masks.
1057
1058 final static int CTRL_MASK = InputEvent.CTRL_MASK;
1059 final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
1060 final static int META_MASK = InputEvent.META_MASK;
1061 final static int ALT_MASK = InputEvent.ALT_MASK;
1062
1063
1064 //
1065 // Write a pointer event message. We may need to send modifier key events
1066 // around it to set the correct modifier state.
1067 //
1068
1069 int pointerMask = 0;
1070
1071 void writePointerEvent(MouseEvent evt) throws IOException {
1072 int modifiers = evt.getModifiers();
1073
1074 int mask2 = 2;
1075 int mask3 = 4;
1076 if (viewer.options.reverseMouseButtons2And3) {
1077 mask2 = 4;
1078 mask3 = 2;
1079 }
1080
1081 // Note: For some reason, AWT does not set BUTTON1_MASK on left
1082 // button presses. Here we think that it was the left button if
1083 // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
1084
1085 if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
1086 if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1087 pointerMask = mask2;
1088 modifiers &= ~ALT_MASK;
1089 } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1090 pointerMask = mask3;
1091 modifiers &= ~META_MASK;
1092 } else {
1093 pointerMask = 1;
1094 }
1095 } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
1096 pointerMask = 0;
1097 if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1098 modifiers &= ~ALT_MASK;
1099 } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1100 modifiers &= ~META_MASK;
1101 }
1102 }
1103
1104 eventBufLen = 0;
1105 writeModifierKeyEvents(modifiers);
1106
1107 int x = evt.getX();
1108 int y = evt.getY();
1109
1110 if (x < 0) x = 0;
1111 if (y < 0) y = 0;
1112
1113 eventBuf[eventBufLen++] = (byte) PointerEvent;
1114 eventBuf[eventBufLen++] = (byte) pointerMask;
1115 eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
1116 eventBuf[eventBufLen++] = (byte) (x & 0xff);
1117 eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
1118 eventBuf[eventBufLen++] = (byte) (y & 0xff);
1119
1120 //
1121 // Always release all modifiers after an "up" event
1122 //
1123
1124 if (pointerMask == 0) {
1125 writeModifierKeyEvents(0);
1126 }
1127
1128 os.write(eventBuf, 0, eventBufLen);
1129 }
1130
1131
1132 //
1133 // Write a key event message. We may need to send modifier key events
1134 // around it to set the correct modifier state. Also we need to translate
1135 // from the Java key values to the X keysym values used by the RFB protocol.
1136 //
1137
1138 void writeKeyEvent(KeyEvent evt) throws IOException {
1139
1140 int keyChar = evt.getKeyChar();
1141
1142 //
1143 // Ignore event if only modifiers were pressed.
1144 //
1145
1146 // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
1147 if (keyChar == 0)
1148 keyChar = KeyEvent.CHAR_UNDEFINED;
1149
1150 if (keyChar == KeyEvent.CHAR_UNDEFINED) {
1151 int code = evt.getKeyCode();
1152 if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
1153 code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
1154 return;
1155 }
1156
1157 //
1158 // Key press or key release?
1159 //
1160
1161 boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
1162
1163 int key;
1164 if (evt.isActionKey()) {
1165
1166 //
1167 // An action key should be one of the following.
1168 // If not then just ignore the event.
1169 //
1170
1171 switch(evt.getKeyCode()) {
1172 case KeyEvent.VK_HOME: key = 0xff50; break;
1173 case KeyEvent.VK_LEFT: key = 0xff51; break;
1174 case KeyEvent.VK_UP: key = 0xff52; break;
1175 case KeyEvent.VK_RIGHT: key = 0xff53; break;
1176 case KeyEvent.VK_DOWN: key = 0xff54; break;
1177 case KeyEvent.VK_PAGE_UP: key = 0xff55; break;
1178 case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
1179 case KeyEvent.VK_END: key = 0xff57; break;
1180 case KeyEvent.VK_INSERT: key = 0xff63; break;
1181 case KeyEvent.VK_F1: key = 0xffbe; break;
1182 case KeyEvent.VK_F2: key = 0xffbf; break;
1183 case KeyEvent.VK_F3: key = 0xffc0; break;
1184 case KeyEvent.VK_F4: key = 0xffc1; break;
1185 case KeyEvent.VK_F5: key = 0xffc2; break;
1186 case KeyEvent.VK_F6: key = 0xffc3; break;
1187 case KeyEvent.VK_F7: key = 0xffc4; break;
1188 case KeyEvent.VK_F8: key = 0xffc5; break;
1189 case KeyEvent.VK_F9: key = 0xffc6; break;
1190 case KeyEvent.VK_F10: key = 0xffc7; break;
1191 case KeyEvent.VK_F11: key = 0xffc8; break;
1192 case KeyEvent.VK_F12: key = 0xffc9; break;
1193 default:
1194 return;
1195 }
1196
1197 } else {
1198
1199 //
1200 // A "normal" key press. Ordinary ASCII characters go straight through.
1201 // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
1202 // Backspace, tab, return, escape and delete have special keysyms.
1203 // Anything else we ignore.
1204 //
1205
1206 key = keyChar;
1207
1208 if (key < 0x20) {
1209 if (evt.isControlDown()) {
1210 key += 0x60;
1211 } else {
1212 switch(key) {
1213 case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
1214 case KeyEvent.VK_TAB: key = 0xff09; break;
1215 case KeyEvent.VK_ENTER: key = 0xff0d; break;
1216 case KeyEvent.VK_ESCAPE: key = 0xff1b; break;
1217 }
1218 }
1219 } else if (key == 0x7f) {
1220 // Delete
1221 key = 0xffff;
1222 } else if (key > 0xff) {
1223 // JDK1.1 on X incorrectly passes some keysyms straight through,
1224 // so we do too. JDK1.1.4 seems to have fixed this.
1225 // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
1226 // Also, we pass through foreign currency keysyms (0x20a0..0x20af).
1227 if ((key < 0xff00 || key > 0xffff) &&
1228 !(key >= 0x20a0 && key <= 0x20af))
1229 return;
1230 }
1231 }
1232
1233 // Fake keyPresses for keys that only generates keyRelease events
1234 if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
1235 (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
1236 (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
1237 (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
1238 (key == 0xa3)) { // XK_sterling
1239 // Make sure we do not send keypress events twice on platforms
1240 // with correct JVMs (those that actually report KeyPress for all
enikey2f0294e2008-12-24 08:18:54 +00001241 // keys)
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001242 if (down)
1243 brokenKeyPressed = true;
1244
1245 if (!down && !brokenKeyPressed) {
1246 // We've got a release event for this key, but haven't received
enikey2f0294e2008-12-24 08:18:54 +00001247 // a press. Fake it.
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001248 eventBufLen = 0;
1249 writeModifierKeyEvents(evt.getModifiers());
1250 writeKeyEvent(key, true);
1251 os.write(eventBuf, 0, eventBufLen);
1252 }
1253
1254 if (!down)
enikey2f0294e2008-12-24 08:18:54 +00001255 brokenKeyPressed = false;
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001256 }
1257
1258 eventBufLen = 0;
1259 writeModifierKeyEvents(evt.getModifiers());
1260 writeKeyEvent(key, down);
1261
1262 // Always release all modifiers after an "up" event
1263 if (!down)
1264 writeModifierKeyEvents(0);
1265
1266 os.write(eventBuf, 0, eventBufLen);
1267 }
1268
1269
1270 //
1271 // Add a raw key event with the given X keysym to eventBuf.
1272 //
1273
1274 void writeKeyEvent(int keysym, boolean down) {
1275 eventBuf[eventBufLen++] = (byte) KeyboardEvent;
1276 eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
1277 eventBuf[eventBufLen++] = (byte) 0;
1278 eventBuf[eventBufLen++] = (byte) 0;
1279 eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
1280 eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
1281 eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
1282 eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
1283 }
1284
1285
1286 //
1287 // Write key events to set the correct modifier state.
1288 //
1289
1290 int oldModifiers = 0;
1291
1292 void writeModifierKeyEvents(int newModifiers) {
1293 if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
1294 writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
1295
1296 if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
1297 writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
1298
1299 if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
1300 writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
1301
1302 if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
1303 writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
1304
1305 oldModifiers = newModifiers;
1306 }
1307
1308
1309 //
1310 // Enable continuous updates for the specified area of the screen (but
1311 // only if EnableContinuousUpdates message is supported by the server).
1312 //
1313
1314 void tryEnableContinuousUpdates(int x, int y, int w, int h)
1315 throws IOException
1316 {
1317 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
1318 System.out.println("Continuous updates not supported by the server");
1319 return;
1320 }
1321
1322 if (continuousUpdatesActive) {
1323 System.out.println("Continuous updates already active");
1324 return;
1325 }
1326
1327 byte[] b = new byte[10];
1328
1329 b[0] = (byte) EnableContinuousUpdates;
1330 b[1] = (byte) 1; // enable
1331 b[2] = (byte) ((x >> 8) & 0xff);
1332 b[3] = (byte) (x & 0xff);
1333 b[4] = (byte) ((y >> 8) & 0xff);
1334 b[5] = (byte) (y & 0xff);
1335 b[6] = (byte) ((w >> 8) & 0xff);
1336 b[7] = (byte) (w & 0xff);
1337 b[8] = (byte) ((h >> 8) & 0xff);
1338 b[9] = (byte) (h & 0xff);
1339
1340 os.write(b);
1341
1342 continuousUpdatesActive = true;
1343 System.out.println("Continuous updates activated");
1344 }
1345
1346
1347 //
1348 // Disable continuous updates (only if EnableContinuousUpdates message
1349 // is supported by the server).
1350 //
1351
1352 void tryDisableContinuousUpdates() throws IOException
1353 {
1354 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
1355 System.out.println("Continuous updates not supported by the server");
1356 return;
1357 }
1358
1359 if (!continuousUpdatesActive) {
1360 System.out.println("Continuous updates already disabled");
1361 return;
1362 }
1363
1364 if (continuousUpdatesEnding)
1365 return;
1366
1367 byte[] b = { (byte)EnableContinuousUpdates, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1368 os.write(b);
1369
1370 if (!serverMsgCaps.isEnabled(EndOfContinuousUpdates)) {
1371 // If the server did not advertise support for the
1372 // EndOfContinuousUpdates message (should not normally happen
1373 // when EnableContinuousUpdates is supported), then we clear
1374 // 'continuousUpdatesActive' variable immediately. Normally,
1375 // it will be reset on receiving EndOfContinuousUpdates message
1376 // from the server.
1377 continuousUpdatesActive = false;
1378 } else {
1379 // Indicate that we are waiting for EndOfContinuousUpdates.
1380 continuousUpdatesEnding = true;
1381 }
1382 }
1383
1384
1385 //
1386 // Process EndOfContinuousUpdates message received from the server.
1387 //
1388
1389 void endOfContinuousUpdates()
1390 {
1391 continuousUpdatesActive = false;
1392 continuousUpdatesEnding = false;
1393 }
1394
1395
1396 //
1397 // Check if continuous updates are in effect.
1398 //
1399
1400 boolean continuousUpdatesAreActive()
1401 {
1402 return continuousUpdatesActive;
1403 }
1404
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001405 /**
1406 * Send a rectangle selection to be treated as video by the server (but
1407 * only if VideoRectangleSelection message is supported by the server).
1408 * @param rect specifies coordinates and size of the rectangule.
1409 * @throws java.io.IOException
1410 */
1411 void trySendVideoSelection(Rectangle rect) throws IOException
1412 {
1413 if (!clientMsgCaps.isEnabled(VideoRectangleSelection)) {
1414 System.out.println("Video area selection is not supported by the server");
1415 return;
1416 }
1417
Constantin Kaplinsky4f652852008-09-05 02:41:15 +00001418 // Send zero coordinates if the rectangle is empty.
1419 if (rect.isEmpty()) {
1420 rect = new Rectangle();
1421 }
1422
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001423 int x = rect.x;
1424 int y = rect.y;
1425 int w = rect.width;
1426 int h = rect.height;
enikey2f0294e2008-12-24 08:18:54 +00001427
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001428 byte[] b = new byte[10];
1429
1430 b[0] = (byte) VideoRectangleSelection;
1431 b[1] = (byte) 0; // reserved
1432 b[2] = (byte) ((x >> 8) & 0xff);
1433 b[3] = (byte) (x & 0xff);
1434 b[4] = (byte) ((y >> 8) & 0xff);
1435 b[5] = (byte) (y & 0xff);
1436 b[6] = (byte) ((w >> 8) & 0xff);
1437 b[7] = (byte) (w & 0xff);
1438 b[8] = (byte) ((h >> 8) & 0xff);
1439 b[9] = (byte) (h & 0xff);
1440
1441 os.write(b);
1442
1443 System.out.println("Video rectangle selection message sent");
1444 }
1445
enikeyd7653562008-12-25 11:02:56 +00001446 void trySendVideoFreeze(boolean freeze) throws IOException
1447 {
1448 if (!clientMsgCaps.isEnabled(VideoFreeze)) {
1449 System.out.println("Video freeze is not supported by the server");
1450 return;
1451 }
1452
1453 byte[] b = new byte[2];
1454 byte fb = 0;
1455 if (freeze) {
1456 fb = 1;
1457 }
1458
1459 b[0] = (byte) VideoFreeze;
1460 b[1] = (byte) fb;
1461
1462 os.write(b);
1463
1464 System.out.println("Video freeze selection message sent");
1465 }
1466
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001467 public void startTiming() {
1468 timing = true;
1469
1470 // Carry over up to 1s worth of previous rate for smoothing.
1471
1472 if (timeWaitedIn100us > 10000) {
1473 timedKbits = timedKbits * 10000 / timeWaitedIn100us;
1474 timeWaitedIn100us = 10000;
1475 }
1476 }
1477
1478 public void stopTiming() {
enikey2f0294e2008-12-24 08:18:54 +00001479 timing = false;
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001480 if (timeWaitedIn100us < timedKbits/2)
1481 timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
1482 }
1483
1484 public long kbitsPerSecond() {
1485 return timedKbits * 10000 / timeWaitedIn100us;
1486 }
1487
1488 public long timeWaited() {
1489 return timeWaitedIn100us;
1490 }
1491
1492 //
1493 // Methods for reading data via our DataInputStream member variable (is).
1494 //
1495 // In addition to reading data, the readFully() methods updates variables
1496 // used to estimate data throughput.
1497 //
1498
1499 public void readFully(byte b[]) throws IOException {
1500 readFully(b, 0, b.length);
1501 }
1502
1503 public void readFully(byte b[], int off, int len) throws IOException {
1504 long before = 0;
1505 if (timing)
1506 before = System.currentTimeMillis();
1507
1508 is.readFully(b, off, len);
1509
1510 if (timing) {
1511 long after = System.currentTimeMillis();
1512 long newTimeWaited = (after - before) * 10;
1513 int newKbits = len * 8 / 1000;
1514
1515 // limit rate to between 10kbit/s and 40Mbit/s
1516
1517 if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
1518 if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
1519
1520 timeWaitedIn100us += newTimeWaited;
1521 timedKbits += newKbits;
1522 }
1523
1524 numBytesRead += len;
1525 }
1526
1527 final int available() throws IOException {
1528 return is.available();
1529 }
1530
1531 // FIXME: DataInputStream::skipBytes() is not guaranteed to skip
1532 // exactly n bytes. Probably we don't want to use this method.
1533 final int skipBytes(int n) throws IOException {
1534 int r = is.skipBytes(n);
1535 numBytesRead += r;
1536 return r;
1537 }
1538
1539 final int readU8() throws IOException {
1540 int r = is.readUnsignedByte();
1541 numBytesRead++;
1542 return r;
1543 }
1544
1545 final int readU16() throws IOException {
1546 int r = is.readUnsignedShort();
1547 numBytesRead += 2;
1548 return r;
1549 }
1550
1551 final int readU32() throws IOException {
1552 int r = is.readInt();
1553 numBytesRead += 4;
1554 return r;
1555 }
Adam Tkac4be9da82010-11-18 14:00:12 +00001556
1557 public void setStreams(InputStream is_, OutputStream os_) {
1558 is = new DataInputStream(is_);
1559 os = os_;
1560 }
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001561}