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