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