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