]> git.proxmox.com Git - mirror_novnc.git/blame - vnc.js
Give better exception feedback in message handler.
[mirror_novnc.git] / vnc.js
CommitLineData
97bfe5ba 1/*
71d2426a 2 * noVNC: HTML5 VNC client
af6b17ce
JM
3 * Copyright (C) 2010 Joel Martin
4 * Licensed under LGPL-3 (see LICENSE.LGPL-3)
ded9dfae
JM
5 *
6 * See README.md for usage and integration instructions.
97bfe5ba 7 */
c4164bda
JM
8"use strict";
9
91308399 10/*global window, WebSocket, $, Browser, Canvas, VNC_uri_prefix Base64, DES */
c4164bda
JM
11
12// Globals defined here
56ec48be 13var VNC_native_ws, RFB;
97bfe5ba 14
97bfe5ba
JM
15/*
16 * Load supporting scripts
17 */
c4164bda
JM
18(function () {
19 var extra, start, end;
91308399
JM
20
21 if (typeof VNC_uri_prefix === "undefined") { VNC_uri_prefix=""; }
c4164bda 22 extra = "";
91308399 23 start = "<script src='" + VNC_uri_prefix;
c4164bda
JM
24 end = "'><\/script>";
25
26 // Uncomment to activate firebug lite
27 //extra += start + "http://getfirebug.com/releases/lite/1.2/" +
28 // firebug-lite-compressed.js" + end;
29
30 extra += start + "include/mootools.js" + end;
31 extra += start + "include/base64.js" + end;
32 extra += start + "include/des.js" + end;
33 extra += start + "include/util.js" + end;
f9583f1f 34 extra += start + "include/canvas.js" + end;
c4164bda
JM
35
36 /* If no builtin websockets then load web_socket.js */
37 if (window.WebSocket) {
38 VNC_native_ws = true;
39 } else {
40 VNC_native_ws = false;
41 extra += start + "include/web-socket-js/swfobject.js" + end;
42 extra += start + "include/web-socket-js/FABridge.js" + end;
43 extra += start + "include/web-socket-js/web_socket.js" + end;
44 }
45 document.write(extra);
46}());
97bfe5ba 47
65e27ddd 48/*
cc0410a3 49 * RFB namespace
65e27ddd
JM
50 */
51
64ab5c4d
JM
52RFB = {
53
91308399
JM
54/*
55 * External interface variables and methods
56 */
57clipboardFocus : false,
58
59setUpdateState: function(externalUpdateState) {
60 RFB.externalUpdateState = externalUpdateState;
61},
62
63setClipboardReceive: function(clipReceive) {
64 RFB.clipboardCopyTo = clipReceive;
65},
66
67setCanvasID: function(canvasID) {
68 RFB.canvasID = canvasID;
69},
70
71setPassword: function(passwd) {
72 RFB.password = passwd;
73 RFB.state = "Authentication";
74 setTimeout(RFB.init_msg, 1);
75},
76
77load: function () {
78 //console.log(">> load");
79
80 /* Load web-socket-js if no builtin WebSocket support */
81 if (VNC_native_ws) {
82 console.log("Using native WebSockets");
83 RFB.updateState('disconnected', 'Disconnected');
84 } else {
85 console.log("Using web-socket-js flash bridge");
86 if ((! Browser.Plugins.Flash) ||
87 (Browser.Plugins.Flash.version < 9)) {
88 RFB.updateState('failed', "WebSockets or Adobe Flash is required");
89 } else if (location.href.substr(0, 7) === "file://") {
90 RFB.updateState('failed',
91 "'file://' URL is incompatible with Adobe Flash");
92 } else {
93 WebSocket.__swfLocation = VNC_uri_prefix +
94 "include/web-socket-js/WebSocketMain.swf";
95 WebSocket.__initialize();
96 RFB.use_seq = true;
97 RFB.updateState('disconnected', 'Disconnected');
98 }
99 }
100
101 //console.log("<< load");
102},
103
104connect: function (host, port, password, encrypt, true_color) {
105 console.log(">> connect");
106
107 RFB.host = host;
108 RFB.port = port;
109 RFB.password = (password !== undefined) ? password : "";
110 RFB.encrypt = (encrypt !== undefined) ? encrypt : true;
111 if ((RFB.encrypt === "0") ||
112 (RFB.encrypt === "no") ||
113 (RFB.encrypt === "false")) {
114 RFB.encrypt = false;
115 }
116 RFB.true_color = (true_color !== undefined) ? true_color: true;
117 if ((RFB.true_color === "0") ||
118 (RFB.true_color === "no") ||
119 (RFB.true_color === "false")) {
120 RFB.true_color = false;
121 }
122
123 if ((!RFB.host) || (!RFB.port)) {
124 updateState('disconnected', "Must set host and port");
125 return;
126 }
127
128 RFB.init_vars();
129
130 if ((RFB.ws) && (RFB.ws.readyState === WebSocket.OPEN)) {
131 RFB.ws.close();
132 }
133 RFB.init_ws();
134
135 RFB.updateState('ProtocolVersion');
136 console.log("<< connect");
137
138},
139
140disconnect: function () {
141 console.log(">> disconnect");
142 if ((RFB.ws) && (RFB.ws.readyState === WebSocket.OPEN)) {
143 RFB.updateState('closed');
144 RFB.ws.onmessage = function (e) { return; };
145 RFB.ws.close();
146 }
147 if (Canvas.ctx) {
148 Canvas.stop();
149 if (! /__debug__$/i.test(document.location.href)) {
150 Canvas.clear();
151 }
152 }
153
154 RFB.updateState('disconnected', 'Disconnected');
155 console.log("<< disconnect");
156},
157
158clipboardPasteFrom: function (text) {
159 if (RFB.state !== "normal") { return; }
160 //console.log(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
161 RFB.send_array(RFB.clientCutText(text));
162 //console.log("<< clipboardPasteFrom");
163},
164
165
166/*
167 * Private variables and methods
168 */
169
56ec48be
JM
170ws : null, // Web Socket object
171sendID : null,
172use_seq : false,
507b473a 173b64encode : true,
56ec48be
JM
174
175// Receive and send queues
176RQ : [], // Receive Queue
177RQ_reorder : [], // Receive Queue re-order list
178RQ_seq_num : 0, // Expected sequence number
179SQ : "", // Send Queue
180
181// Frame buffer update state
182FBU : {
183 rects : 0,
184 subrects : 0, // RRE and HEXTILE
185 lines : 0, // RAW
186 tiles : 0, // HEXTILE
187 bytes : 0,
188 x : 0,
189 y : 0,
190 width : 0,
191 height : 0,
192 encoding : 0,
193 subencoding : -1,
194 background : null
195},
cc0410a3 196
d41c33e4
JM
197true_color : false,
198fb_Bpp : 4,
199fb_depth : 3,
200
56ec48be
JM
201max_version : 3.8,
202version : 0,
203auth_scheme : '',
204state : 'disconnected',
205cuttext : 'none', // ServerCutText wait state
206ct_length : 0,
30059bdf 207
56ec48be
JM
208shared : 1,
209check_rate : 217,
210req_rate : 1413,
211last_req : 0,
64ab5c4d 212
56ec48be
JM
213host : '',
214port : 5900,
215password : '',
cc0410a3 216
91308399 217canvasID : 'VNC_canvas',
56ec48be
JM
218fb_width : 0,
219fb_height : 0,
220fb_name : "",
56ec48be 221rre_chunk : 100,
cc0410a3 222
56ec48be
JM
223timing : {
224 last_fbu : 0,
225 fbu_total : 0,
753bde8f
JM
226 fbu_total_cnt : 0,
227 full_fbu_total : 0,
c3996e24
JM
228 full_fbu_cnt : 0,
229
230 fbu_rt_start : 0,
231 fbu_rt_total : 0,
232 fbu_rt_cnt : 0,
753bde8f
JM
233},
234
97bfe5ba
JM
235/* Mouse state */
236mouse_buttonmask : 0,
237mouse_arr : [],
238
cc0410a3
JM
239/*
240 * Server message handlers
241 */
242
65e27ddd 243/* RFB/VNC initialisation */
484a4696 244init_msg: function () {
753bde8f 245 console.log(">> init_msg [RFB.state '" + RFB.state + "']");
ef764d3b 246
67b24a90
JM
247 var RQ = RFB.RQ, strlen, reason, reason_len,
248 sversion, cversion, types, num_types, challenge, response,
c4164bda
JM
249 bpp, depth, big_endian, true_color, name_length;
250
56ec48be 251 //console.log("RQ (" + RQ.length + ") " + RQ);
64ab5c4d 252 switch (RFB.state) {
65e27ddd
JM
253
254 case 'ProtocolVersion' :
ef764d3b 255 if (RQ.length < 12) {
c4164bda
JM
256 RFB.updateState('failed',
257 "Disconnected: incomplete protocol version");
ef764d3b
JM
258 return;
259 }
67b24a90
JM
260 sversion = RQ.shiftStr(12).substr(4,7);
261 console.log("Server ProtocolVersion: " + sversion);
262 switch (sversion) {
263 case "003.003": RFB.version = 3.3; break;
264 case "003.007": RFB.version = 3.7; break;
265 case "003.008": RFB.version = 3.8; break;
266 default:
267 RFB.updateState('failed',
268 "Invalid server version " + sversion);
269 return;
270 }
271 if (RFB.version > RFB.max_version) {
272 RFB.version = RFB.max_version;
65e27ddd 273 }
67b24a90
JM
274
275 cversion = "00" + parseInt(RFB.version,10) +
276 ".00" + ((RFB.version * 10) % 10);
277 RFB.send_string("RFB " + cversion + "\n");
278 RFB.updateState('Security', "Sent ProtocolVersion: " + sversion);
65e27ddd
JM
279 break;
280
ef764d3b 281 case 'Security' :
67b24a90 282 if (RFB.version >= 3.7) {
c4164bda
JM
283 num_types = RQ.shift8();
284 if (num_types === 0) {
285 strlen = RQ.shift32();
286 reason = RQ.shiftStr(strlen);
287 RFB.updateState('failed',
288 "Disconnected: security failure: " + reason);
ef764d3b
JM
289 return;
290 }
c4164bda 291 types = RQ.shiftBytes(num_types);
2cec49d4 292
91308399
JM
293 RFB.auth_scheme = types[0];
294 if ((RFB.auth_scheme !== 1) && (RFB.auth_scheme !== 2)) {
c4164bda
JM
295 RFB.updateState('failed',
296 "Disconnected: invalid security types list: " + types);
ef764d3b
JM
297 return;
298 }
2cec49d4 299
ef764d3b 300 RFB.send_array([RFB.auth_scheme]);
67b24a90 301 } else {
c539e4dc
JM
302 if (RQ.length < 4) {
303 RFB.updateState('failed', "Invalid security frame");
304 return;
305 }
306 RFB.auth_scheme = RQ.shift32();
65e27ddd 307 }
c4164bda
JM
308 RFB.updateState('Authentication',
309 "Authenticating using scheme: " + RFB.auth_scheme);
ef764d3b
JM
310 // Fall through
311
312 case 'Authentication' :
313 console.log("Security auth scheme: " + RFB.auth_scheme);
314 switch (RFB.auth_scheme) {
65e27ddd 315 case 0: // connection failed
ef764d3b
JM
316 if (RQ.length < 4) {
317 console.log(" waiting for auth reason bytes");
318 return;
319 }
c4164bda
JM
320 strlen = RQ.shift32();
321 reason = RQ.shiftStr(strlen);
322 RFB.updateState('failed',
323 "Disconnected: auth failure: " + reason);
65e27ddd
JM
324 return;
325 case 1: // no authentication
2cec49d4
KC
326 // RFB.send_array([RFB.shared]); // ClientInitialisation
327 RFB.updateState('SecurityResult');
65e27ddd
JM
328 break;
329 case 2: // VNC authentication
91308399
JM
330 if (RFB.password.length === 0) {
331 RFB.updateState('password', "Password Required");
332 return;
333 }
ef764d3b
JM
334 if (RQ.length < 16) {
335 console.log(" waiting for auth challenge bytes");
336 return;
337 }
c4164bda 338 challenge = RQ.shiftBytes(16);
753bde8f 339 //console.log("Password: " + RFB.password);
c4164bda
JM
340 //console.log("Challenge: " + challenge +
341 // " (" + challenge.length + ")");
342 response = RFB.DES(RFB.password, challenge);
343 //console.log("Response: " + response +
344 // " (" + response.length + ")");
753bde8f
JM
345
346 console.log("Sending DES encrypted auth response");
8580b989 347 RFB.send_array(response);
8759ea6f 348 RFB.updateState('SecurityResult');
65e27ddd 349 break;
484a4696 350 default:
c4164bda
JM
351 RFB.updateState('failed',
352 "Disconnected: unsupported auth scheme: " +
353 RFB.auth_scheme);
484a4696 354 return;
65e27ddd
JM
355 }
356 break;
357
64ab5c4d 358 case 'SecurityResult' :
c539e4dc
JM
359 if (RQ.length < 4) {
360 RFB.updateState('failed', "Invalid VNC auth response");
65e27ddd
JM
361 return;
362 }
c4164bda 363 switch (RQ.shift32()) {
65e27ddd 364 case 0: // OK
8759ea6f 365 RFB.updateState('ServerInitialisation', "Authentication OK");
65e27ddd
JM
366 break;
367 case 1: // failed
67b24a90 368 if (RFB.version >= 3.8) {
c4164bda
JM
369 reason_len = RQ.shift32();
370 reason = RQ.shiftStr(reason_len);
c539e4dc 371 RFB.updateState('failed', reason);
67b24a90 372 } else {
c539e4dc
JM
373 RFB.updateState('failed', "Authentication failed");
374 }
65e27ddd
JM
375 return;
376 case 2: // too-many
c4164bda
JM
377 RFB.updateState('failed',
378 "Disconnected: too many auth attempts");
65e27ddd
JM
379 return;
380 }
64ab5c4d 381 RFB.send_array([RFB.shared]); // ClientInitialisation
65e27ddd
JM
382 break;
383
384 case 'ServerInitialisation' :
5d8e7ec0 385 if (RQ.length < 24) {
c539e4dc 386 RFB.updateState('failed', "Invalid server initialisation");
65e27ddd
JM
387 return;
388 }
489d1676
JM
389
390 /* Screen size */
5d8e7ec0
JM
391 RFB.fb_width = RQ.shift16();
392 RFB.fb_height = RQ.shift16();
489d1676 393
489d1676 394 /* PIXEL_FORMAT */
c4164bda
JM
395 bpp = RQ.shift8();
396 depth = RQ.shift8();
397 big_endian = RQ.shift8();
398 true_color = RQ.shift8();
489d1676 399
753bde8f
JM
400 console.log("Screen: " + RFB.fb_width + "x" + RFB.fb_height +
401 ", bpp: " + bpp + ", depth: " + depth +
402 ", big_endian: " + big_endian +
403 ", true_color: " + true_color);
489d1676
JM
404
405 /* Connection name/title */
5d8e7ec0 406 RQ.shiftStr(12);
c4164bda 407 name_length = RQ.shift32();
5d8e7ec0 408 RFB.fb_name = RQ.shiftStr(name_length);
489d1676 409
91308399 410 Canvas.init(RFB.canvasID, RFB.fb_width, RFB.fb_height, RFB.true_color,
a575a383
JM
411 RFB.keyDown, RFB.keyUp, RFB.mouseDown, RFB.mouseUp,
412 RFB.mouseMove, RFB.mouseWheel);
64ab5c4d 413
d41c33e4
JM
414 if (RFB.true_color) {
415 RFB.fb_Bpp = 4;
416 RFB.fb_depth = 3;
417 } else {
418 RFB.fb_Bpp = 1;
419 RFB.fb_depth = 1;
420 }
421
c4164bda
JM
422 response = RFB.pixelFormat();
423 response = response.concat(RFB.encodings());
424 response = response.concat(RFB.fbUpdateRequest(0));
c3996e24 425 RFB.timing.fbu_rt_start = (new Date()).getTime();
c4164bda 426 RFB.send_array(response);
9f4af5a7 427
d064769c 428 /* Start pushing/polling */
8cf20615 429 RFB.checkEvents.delay(RFB.check_rate);
489d1676 430
8759ea6f 431 RFB.updateState('normal', "Connected to: " + RFB.fb_name);
65e27ddd
JM
432 break;
433 }
753bde8f 434 //console.log("<< init_msg");
64ab5c4d 435},
65e27ddd 436
28a5f293
JM
437
438/* Normal RFB/VNC server messages */
439normal_msg: function () {
440 //console.log(">> normal_msg");
c4164bda 441
c3996e24 442 var RQ = RFB.RQ, FBU = RFB.FBU, now, fbu_rt_diff,
d41c33e4
JM
443 ret = true, msg_type, msg,
444 c, first_colour, num_colours, red, green, blue;
56ec48be 445
1098b5bf 446 if (FBU.rects > 0) {
c4164bda
JM
447 msg_type = 0;
448 } else if (RFB.cuttext !== 'none') {
449 msg_type = 3;
28a5f293 450 } else {
c4164bda 451 msg_type = RQ.shift8();
28a5f293
JM
452 }
453 switch (msg_type) {
454 case 0: // FramebufferUpdate
c4164bda 455 if (FBU.rects === 0) {
5d8e7ec0
JM
456 if (RQ.length < 3) {
457 RQ.unshift(msg_type);
28a5f293 458 console.log(" waiting for FBU header bytes");
5d8e7ec0 459 return false;
28a5f293 460 }
5d8e7ec0
JM
461 RQ.shift8();
462 FBU.rects = RQ.shift16();
28a5f293 463 //console.log("FramebufferUpdate, rects:" + FBU.rects);
753bde8f 464 RFB.timing.cur_fbu = 0;
28a5f293
JM
465 FBU.bytes = 0;
466 }
467
5d8e7ec0 468 while ((FBU.rects > 0) && (RQ.length >= FBU.bytes)) {
c4164bda 469 if (FBU.bytes === 0) {
5d8e7ec0 470 if (RQ.length < 12) {
28a5f293 471 console.log(" waiting for rect header bytes");
5d8e7ec0 472 return false;
28a5f293
JM
473 }
474 /* New FramebufferUpdate */
5d8e7ec0
JM
475 FBU.x = RQ.shift16();
476 FBU.y = RQ.shift16();
477 FBU.width = RQ.shift16();
478 FBU.height = RQ.shift16();
479 FBU.encoding = parseInt(RQ.shift32(), 10);
1098b5bf
JM
480
481 // Debug:
482 /*
c4164bda
JM
483 msg = "FramebufferUpdate rects:" + FBU.rects +
484 " encoding:" + FBU.encoding
28a5f293 485 switch (FBU.encoding) {
1098b5bf
JM
486 case 0: msg += "(RAW)"; break;
487 case 1: msg += "(COPY-RECT)"; break;
488 case 2: msg += "(RRE)"; break;
489 case 5: msg += "(HEXTILE " + FBU.tiles + " tiles)"; break;
28a5f293 490 default:
c4164bda
JM
491 RFB.updateState('failed',
492 "Disconnected: unsupported encoding " +
493 FBU.encoding);
5d8e7ec0 494 return false;
28a5f293 495 }
5d8e7ec0 496 msg += ", RQ.length: " + RQ.length
1098b5bf
JM
497 console.log(msg);
498 */
28a5f293
JM
499 }
500
c4164bda 501 RFB.timing.last_fbu = (new Date()).getTime();
1098b5bf 502 switch (FBU.encoding) {
5d8e7ec0
JM
503 case 0: ret = RFB.display_raw(); break; // Raw
504 case 1: ret = RFB.display_copy_rect(); break; // Copy-Rect
505 case 2: ret = RFB.display_rre(); break; // RRE
506 case 5: ret = RFB.display_hextile(); break; // hextile
28a5f293 507 }
c3996e24
JM
508 now = (new Date()).getTime();
509 RFB.timing.cur_fbu += (now - RFB.timing.last_fbu);
c4164bda
JM
510 if (FBU.rects === 0) {
511 if ((FBU.width === RFB.fb_width) &&
512 (FBU.height === RFB.fb_height)) {
753bde8f
JM
513 RFB.timing.full_fbu_total += RFB.timing.cur_fbu;
514 RFB.timing.full_fbu_cnt += 1;
c4164bda
JM
515 console.log("Timing of full FBU, cur: " +
516 RFB.timing.cur_fbu + ", total: " +
517 RFB.timing.full_fbu_total + ", cnt: " +
518 RFB.timing.full_fbu_cnt + ", avg: " +
519 (RFB.timing.full_fbu_total /
520 RFB.timing.full_fbu_cnt));
c3996e24
JM
521 if (RFB.timing.fbu_rt_start > 0) {
522 fbu_rt_diff = now - RFB.timing.fbu_rt_start;
523 RFB.timing.fbu_rt_total += fbu_rt_diff;
524 RFB.timing.fbu_rt_cnt += 1;
525 console.log("full FBU round-trip, cur: " +
526 fbu_rt_diff + ", total: " +
527 RFB.timing.fbu_rt_total + ", cnt: " +
528 RFB.timing.fbu_rt_cnt + ", avg: " +
529 (RFB.timing.fbu_rt_total /
530 RFB.timing.fbu_rt_cnt));
531 RFB.timing.fbu_rt_start = 0;
532 }
753bde8f
JM
533 }
534 }
c4164bda 535 if (RFB.state !== "normal") { return true; }
28a5f293
JM
536 }
537
28a5f293
JM
538 break;
539 case 1: // SetColourMapEntries
d41c33e4 540 console.log("SetColourMapEntries");
5d8e7ec0 541 RQ.shift8(); // Padding
d41c33e4 542 first_colour = RQ.shift16(); // First colour
c4164bda 543 num_colours = RQ.shift16();
d41c33e4
JM
544 for (c=0; c < num_colours; c++) {
545 red = RQ.shift16();
546 //console.log("red before: " + red);
547 red = parseInt(red / 256, 10);
548 //console.log("red after: " + red);
549 green = parseInt(RQ.shift16() / 256, 10);
550 blue = parseInt(RQ.shift16() / 256, 10);
551 Canvas.colourMap[first_colour + c] = [red, green, blue];
552 }
553 console.log("Registered " + num_colours + " colourMap entries");
554 //console.log("colourMap: " + Canvas.colourMap);
28a5f293
JM
555 break;
556 case 2: // Bell
557 console.log("Bell (unsupported)");
558 break;
559 case 3: // ServerCutText
560 console.log("ServerCutText");
5d8e7ec0 561 console.log("RQ:" + RQ.slice(0,20));
c4164bda 562 if (RFB.cuttext === 'none') {
30059bdf
JM
563 RFB.cuttext = 'header';
564 }
c4164bda 565 if (RFB.cuttext === 'header') {
5d8e7ec0 566 if (RQ.length < 7) {
30059bdf 567 console.log("waiting for ServerCutText header");
5d8e7ec0 568 return false;
30059bdf 569 }
5d8e7ec0
JM
570 RQ.shiftBytes(3); // Padding
571 RFB.ct_length = RQ.shift32();
30059bdf
JM
572 }
573 RFB.cuttext = 'bytes';
5d8e7ec0 574 if (RQ.length < RFB.ct_length) {
30059bdf 575 console.log("waiting for ServerCutText bytes");
5d8e7ec0 576 return false;
30059bdf 577 }
5d8e7ec0 578 RFB.clipboardCopyTo(RQ.shiftStr(RFB.ct_length));
30059bdf 579 RFB.cuttext = 'none';
28a5f293
JM
580 break;
581 default:
c4164bda
JM
582 RFB.updateState('failed',
583 "Disconnected: illegal server message type " + msg_type);
5d8e7ec0 584 console.log("RQ.slice(0,30):" + RQ.slice(0,30));
28a5f293
JM
585 break;
586 }
587 //console.log("<< normal_msg");
5d8e7ec0 588 return ret;
28a5f293
JM
589},
590
591
592/*
593 * FramebufferUpdate encodings
594 */
595
ed7e776d 596display_raw: function () {
1098b5bf 597 //console.log(">> display_raw");
c4164bda 598
56ec48be 599 var RQ = RFB.RQ, FBU = RFB.FBU, cur_y, cur_height;
c4164bda
JM
600
601 if (FBU.lines === 0) {
1098b5bf
JM
602 FBU.lines = FBU.height;
603 }
604 FBU.bytes = FBU.width * RFB.fb_Bpp; // At least a line
5d8e7ec0 605 if (RQ.length < FBU.bytes) {
c4164bda
JM
606 //console.log(" waiting for " +
607 // (FBU.bytes - RQ.length) + " RAW bytes");
1098b5bf
JM
608 return;
609 }
c4164bda
JM
610 cur_y = FBU.y + (FBU.height - FBU.lines);
611 cur_height = Math.min(FBU.lines,
612 Math.floor(RQ.length/(FBU.width * RFB.fb_Bpp)));
d41c33e4 613 Canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, RQ, 0);
5d8e7ec0 614 RQ.shiftBytes(FBU.width * cur_height * RFB.fb_Bpp);
1098b5bf
JM
615 FBU.lines -= cur_height;
616
617 if (FBU.lines > 0) {
618 FBU.bytes = FBU.width * RFB.fb_Bpp; // At least another line
619 } else {
620 FBU.rects --;
621 FBU.bytes = 0;
622 }
ed7e776d
JM
623},
624
625display_copy_rect: function () {
1098b5bf 626 //console.log(">> display_copy_rect");
c4164bda 627
56ec48be 628 var RQ = RFB.RQ, FBU = RFB.FBU, old_x, old_y;
c4164bda 629
5d8e7ec0 630 if (RQ.length < 4) {
c4164bda
JM
631 //console.log(" waiting for " +
632 // (FBU.bytes - RQ.length) + " COPY-RECT bytes");
1098b5bf
JM
633 return;
634 }
c4164bda
JM
635 old_x = RQ.shift16();
636 old_y = RQ.shift16();
ed7e776d
JM
637 Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
638 FBU.rects --;
1098b5bf 639 FBU.bytes = 0;
ed7e776d
JM
640},
641
642display_rre: function () {
56ec48be
JM
643 //console.log(">> display_rre (" + RFB.RQ.length + " bytes)");
644 var RQ = RFB.RQ, FBU = RFB.FBU, color, x, y, width, height, chunk;
c4164bda 645 if (FBU.subrects === 0) {
5d8e7ec0 646 if (RQ.length < 4 + RFB.fb_Bpp) {
c4164bda
JM
647 //console.log(" waiting for " +
648 // (4 + RFB.fb_Bpp - RQ.length) + " RRE bytes");
1098b5bf
JM
649 return;
650 }
5d8e7ec0 651 FBU.subrects = RQ.shift32();
c4164bda 652 color = RQ.shiftBytes(RFB.fb_Bpp); // Background
48ebcdb1 653 Canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
f7618085 654 }
5d8e7ec0 655 while ((FBU.subrects > 0) && (RQ.length >= (RFB.fb_Bpp + 8))) {
c4164bda
JM
656 color = RQ.shiftBytes(RFB.fb_Bpp);
657 x = RQ.shift16();
658 y = RQ.shift16();
659 width = RQ.shift16();
660 height = RQ.shift16();
48ebcdb1 661 Canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
1098b5bf 662 FBU.subrects --;
ed7e776d 663 }
c4164bda
JM
664 //console.log(" display_rre: rects: " + FBU.rects +
665 // ", FBU.subrects: " + FBU.subrects);
ed7e776d
JM
666
667 if (FBU.subrects > 0) {
c4164bda 668 chunk = Math.min(RFB.rre_chunk, FBU.subrects);
f7618085 669 FBU.bytes = (RFB.fb_Bpp + 8) * chunk;
ed7e776d 670 } else {
6dab56f9 671 FBU.rects --;
1098b5bf 672 FBU.bytes = 0;
ed7e776d 673 }
b7ec5487 674 //console.log("<< display_rre, FBU.bytes: " + FBU.bytes);
ed7e776d
JM
675},
676
0f628064 677display_hextile: function() {
8759ea6f 678 //console.log(">> display_hextile");
56ec48be
JM
679 var RQ = RFB.RQ, FBU = RFB.FBU,
680 subencoding, subrects, idx, tile, color, cur_tile,
681 tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh;
b7ec5487 682
c4164bda 683 if (FBU.tiles === 0) {
1098b5bf
JM
684 FBU.tiles_x = Math.ceil(FBU.width/16);
685 FBU.tiles_y = Math.ceil(FBU.height/16);
686 FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
687 FBU.tiles = FBU.total_tiles;
688 }
689
5d8e7ec0 690 /* FBU.bytes comes in as 1, RQ.length at least 1 */
1098b5bf
JM
691 while (FBU.tiles > 0) {
692 FBU.bytes = 1;
5d8e7ec0 693 if (RQ.length < FBU.bytes) {
1098b5bf
JM
694 console.log(" waiting for HEXTILE subencoding byte");
695 return;
696 }
5d8e7ec0 697 subencoding = RQ[0]; // Peek
1098b5bf 698 if (subencoding > 30) { // Raw
c4164bda
JM
699 RFB.updateState('failed',
700 "Disconnected: illegal hextile subencoding " + subencoding);
5d8e7ec0 701 console.log("RQ.slice(0,30):" + RQ.slice(0,30));
1098b5bf
JM
702 return;
703 }
704 subrects = 0;
9f4af5a7
JM
705 cur_tile = FBU.total_tiles - FBU.tiles;
706 tile_x = cur_tile % FBU.tiles_x;
707 tile_y = Math.floor(cur_tile / FBU.tiles_x);
708 x = FBU.x + tile_x * 16;
709 y = FBU.y + tile_y * 16;
c4164bda
JM
710 w = Math.min(16, (FBU.x + FBU.width) - x);
711 h = Math.min(16, (FBU.y + FBU.height) - y);
b7ec5487 712
1098b5bf
JM
713 /* Figure out how much we are expecting */
714 if (subencoding & 0x01) { // Raw
715 //console.log(" Raw subencoding");
716 FBU.bytes += w * h * RFB.fb_Bpp;
717 } else {
718 if (subencoding & 0x02) { // Background
719 FBU.bytes += RFB.fb_Bpp;
720 }
721 if (subencoding & 0x04) { // Foreground
722 FBU.bytes += RFB.fb_Bpp;
723 }
724 if (subencoding & 0x08) { // AnySubrects
725 FBU.bytes++; // Since we aren't shifting it off
5d8e7ec0 726 if (RQ.length < FBU.bytes) {
1098b5bf
JM
727 /* Wait for subrects byte */
728 console.log(" waiting for hextile subrects header byte");
729 return;
b7ec5487 730 }
5d8e7ec0 731 subrects = RQ[FBU.bytes-1]; // Peek
1098b5bf
JM
732 if (subencoding & 0x10) { // SubrectsColoured
733 FBU.bytes += subrects * (RFB.fb_Bpp + 2);
734 } else {
735 FBU.bytes += subrects * 2;
b7ec5487
JM
736 }
737 }
738 }
739
c4164bda
JM
740 //console.log(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
741 // ", subencoding:" + subencoding +
742 // "(last: " + FBU.lastsubencoding + "), subrects:" +
743 // subrects + ", tile:" + tile_x + "," + tile_y +
744 // " [" + x + "," + y + "]@" + w + "x" + h +
745 // ", d.length:" + RQ.length + ", bytes:" + FBU.bytes +
746 // " last:" + RQ.slice(FBU.bytes-10, FBU.bytes) +
747 // " next:" + RQ.slice(FBU.bytes-1, FBU.bytes+10));
5d8e7ec0 748 if (RQ.length < FBU.bytes) {
c4164bda
JM
749 //console.log(" waiting for " +
750 // (FBU.bytes - RQ.length) + " hextile bytes");
b7ec5487
JM
751 return;
752 }
753
1098b5bf 754 /* We know the encoding and have a whole tile */
7f4f41b0
JM
755 FBU.subencoding = RQ[0];
756 idx = 1;
c4164bda 757 if (FBU.subencoding === 0) {
1098b5bf
JM
758 if (FBU.lastsubencoding & 0x01) {
759 /* Weird: ignore blanks after RAW */
760 console.log(" Ignoring blank after RAW");
7f4f41b0
JM
761 } else {
762 Canvas.fillRect(x, y, w, h, FBU.background);
1098b5bf 763 }
1098b5bf 764 } else if (FBU.subencoding & 0x01) { // Raw
d41c33e4 765 Canvas.blitImage(x, y, w, h, RQ, idx);
1098b5bf 766 } else {
1098b5bf 767 if (FBU.subencoding & 0x02) { // Background
5d8e7ec0 768 FBU.background = RQ.slice(idx, idx + RFB.fb_Bpp);
1098b5bf 769 idx += RFB.fb_Bpp;
1098b5bf
JM
770 }
771 if (FBU.subencoding & 0x04) { // Foreground
5d8e7ec0 772 FBU.foreground = RQ.slice(idx, idx + RFB.fb_Bpp);
1098b5bf 773 idx += RFB.fb_Bpp;
1098b5bf 774 }
3875f847 775
c4164bda 776 tile = Canvas.getTile(x, y, w, h, FBU.background);
1098b5bf 777 if (FBU.subencoding & 0x08) { // AnySubrects
5d8e7ec0 778 subrects = RQ[idx];
1098b5bf 779 idx++;
c4164bda 780 for (s = 0; s < subrects; s ++) {
1098b5bf 781 if (FBU.subencoding & 0x10) { // SubrectsColoured
5d8e7ec0 782 color = RQ.slice(idx, idx + RFB.fb_Bpp);
1098b5bf
JM
783 idx += RFB.fb_Bpp;
784 } else {
785 color = FBU.foreground;
b7ec5487 786 }
5d8e7ec0 787 xy = RQ[idx];
1098b5bf 788 idx++;
3875f847
JM
789 sx = (xy >> 4);
790 sy = (xy & 0x0f);
1098b5bf 791
5d8e7ec0 792 wh = RQ[idx];
1098b5bf
JM
793 idx++;
794 sw = (wh >> 4) + 1;
795 sh = (wh & 0x0f) + 1;
796
3875f847 797 Canvas.setTile(tile, sx, sy, sw, sh, color);
b7ec5487
JM
798 }
799 }
3875f847 800 Canvas.putTile(tile);
b7ec5487 801 }
5d8e7ec0 802 RQ.shiftBytes(FBU.bytes);
1098b5bf
JM
803 FBU.lastsubencoding = FBU.subencoding;
804 FBU.bytes = 0;
805 FBU.tiles --;
0f628064
JM
806 }
807
c4164bda 808 if (FBU.tiles === 0) {
b7ec5487
JM
809 FBU.rects --;
810 }
811
8759ea6f 812 //console.log("<< display_hextile");
0f628064
JM
813},
814
ed7e776d 815
65e27ddd
JM
816
817/*
818 * Client message routines
819 */
820
9f4af5a7 821pixelFormat: function () {
753bde8f 822 //console.log(">> setPixelFormat");
9f4af5a7
JM
823 var arr;
824 arr = [0]; // msg-type
64ab5c4d
JM
825 arr.push8(0); // padding
826 arr.push8(0); // padding
827 arr.push8(0); // padding
828
cc0410a3 829 arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
d41c33e4 830 arr.push8(RFB.fb_depth * 8); // depth
64ab5c4d 831 arr.push8(0); // little-endian
d41c33e4 832 arr.push8(RFB.true_color); // true-color
64ab5c4d
JM
833
834 arr.push16(255); // red-max
835 arr.push16(255); // green-max
836 arr.push16(255); // blue-max
9f4af5a7 837 arr.push8(0); // red-shift
64ab5c4d 838 arr.push8(8); // green-shift
9f4af5a7 839 arr.push8(16); // blue-shift
64ab5c4d 840
9f4af5a7
JM
841 arr.push8(0); // padding
842 arr.push8(0); // padding
843 arr.push8(0); // padding
753bde8f 844 //console.log("<< setPixelFormat");
9f4af5a7 845 return arr;
64ab5c4d
JM
846},
847
848fixColourMapEntries: function () {
849},
850
9f4af5a7 851encodings: function () {
753bde8f 852 //console.log(">> setEncodings");
9f4af5a7
JM
853 var arr;
854 arr = [2]; // msg-type
64ab5c4d 855 arr.push8(0); // padding
b7ec5487
JM
856
857 //arr.push16(3); // encoding count
858 arr.push16(4); // encoding count
859 arr.push32(5); // hextile encoding
860
ed7e776d 861 arr.push32(2); // RRE encoding
64ab5c4d
JM
862 arr.push32(1); // copy-rect encoding
863 arr.push32(0); // raw encoding
753bde8f 864 //console.log("<< setEncodings");
9f4af5a7 865 return arr;
64ab5c4d 866},
65e27ddd 867
64ab5c4d 868fbUpdateRequest: function (incremental, x, y, xw, yw) {
410960ba 869 //console.log(">> fbUpdateRequest");
c4164bda
JM
870 if (!x) { x = 0; }
871 if (!y) { y = 0; }
872 if (!xw) { xw = RFB.fb_width; }
873 if (!yw) { yw = RFB.fb_height; }
9f4af5a7
JM
874 var arr;
875 arr = [3]; // msg-type
64ab5c4d
JM
876 arr.push8(incremental);
877 arr.push16(x);
878 arr.push16(y);
879 arr.push16(xw);
880 arr.push16(yw);
b7ec5487 881 //console.log("<< fbUpdateRequest");
9f4af5a7 882 return arr;
64ab5c4d 883},
65e27ddd 884
d9cbdc7d 885keyEvent: function (keysym, down) {
410960ba 886 //console.log(">> keyEvent, keysym: " + keysym + ", down: " + down);
9f4af5a7
JM
887 var arr;
888 arr = [4]; // msg-type
64ab5c4d
JM
889 arr.push8(down);
890 arr.push16(0);
d9cbdc7d 891 arr.push32(keysym);
b7ec5487 892 //console.log("<< keyEvent");
d064769c 893 return arr;
64ab5c4d 894},
65e27ddd 895
d064769c 896pointerEvent: function (x, y) {
c4164bda
JM
897 //console.log(">> pointerEvent, x,y: " + x + "," + y +
898 // " , mask: " + RFB.mouse_buttonMask);
d064769c
JM
899 var arr;
900 arr = [5]; // msg-type
97bfe5ba 901 arr.push8(RFB.mouse_buttonMask);
d064769c
JM
902 arr.push16(x);
903 arr.push16(y);
410960ba 904 //console.log("<< pointerEvent");
d064769c 905 return arr;
64ab5c4d 906},
65e27ddd 907
30059bdf 908clientCutText: function (text) {
91308399 909 //console.log(">> clientCutText");
30059bdf
JM
910 var arr;
911 arr = [6]; // msg-type
912 arr.push8(0); // padding
913 arr.push8(0); // padding
914 arr.push8(0); // padding
915 arr.push32(text.length);
916 arr.pushStr(text);
91308399 917 //console.log("<< clientCutText:" + arr);
30059bdf 918 return arr;
64ab5c4d
JM
919},
920
921
922/*
923 * Utility routines
924 */
925
507b473a
JM
926encode_message: function(arr) {
927 if (RFB.b64encode) {
928 RFB.SQ = RFB.SQ + Base64.encode(arr);
929 } else {
930 RFB.SQ = RFB.SQ + arr.map(function (num) {
931 return String.fromCharCode(num); } ).join('');
932 }
933},
934
935decode_message: function(data, offset) {
936 //console.log(">> decode_message: " + data);
937 if (RFB.b64encode) {
938 RFB.RQ = RFB.RQ.concat(Base64.decode(data, offset));
939 } else {
940 RFB.RQ = RFB.RQ.concat(data.split('').slice(offset).
941 map(function (chr) {
942 return (chr.charCodeAt(0) % 256); }));
943 }
944 //console.log(">> decode_message, RQ: " + RFB.RQ);
945},
946
07287cfd
JM
947recv_message: function(e) {
948 //console.log(">> recv_message");
07287cfd 949
753bde8f
JM
950 try {
951 if (RFB.use_seq) {
952 RFB.recv_message_reorder(e);
953 } else {
507b473a 954 RFB.decode_message(e.data, 0);
753bde8f
JM
955
956 RFB.handle_message();
957 }
c4164bda 958 } catch (exc) {
14355cb2
JM
959 if (typeof exc.stack !== 'undefined') {
960 console.log("recv_message, caught exception: " + exc.stack);
961 } else if (typeof exc.description !== 'undefined') {
962 console.log("recv_message, caught exception: " + exc.description);
963 } else {
964 console.log("recv_message, caught exception:" + exc);
965 }
c4164bda
JM
966 if (typeof exc.name !== 'undefined') {
967 RFB.updateState('failed', exc.name + ": " + exc.message);
753bde8f 968 } else {
c4164bda 969 RFB.updateState('failed', exc);
753bde8f
JM
970 }
971 }
07287cfd
JM
972 //console.log("<< recv_message");
973},
974
975recv_message_reorder: function(e) {
976 //console.log(">> recv_message_reorder");
977
56ec48be 978 var RQ_reorder = RFB.RQ_reorder, offset, seq_num, i;
c4164bda
JM
979
980 offset = e.data.indexOf(":") + 1;
981 seq_num = parseInt(e.data.substr(0, offset-1), 10);
56ec48be 982 if (RFB.RQ_seq_num === seq_num) {
507b473a 983 RFB.decode_message(e.data, offset);
56ec48be 984 RFB.RQ_seq_num++;
07287cfd 985 } else {
c4164bda 986 console.warn("sequence number mismatch: expected " +
56ec48be
JM
987 RFB.RQ_seq_num + ", got " + seq_num);
988 if (RFB.RQ_reorder.length > 20) {
07287cfd
JM
989 RFB.updateState('failed', "Re-order queue too long");
990 } else {
56ec48be 991 RFB.RQ_reorder = RFB.RQ_reorder.concat(e.data.substr(0));
c4164bda 992 i = 0;
56ec48be
JM
993 while (i < RFB.RQ_reorder.length) {
994 offset = RFB.RQ_reorder[i].indexOf(":") + 1;
995 seq_num = parseInt(RFB.RQ_reorder[i].substr(0, offset-1), 10);
c4164bda
JM
996 //console.log("Searching reorder list item " +
997 // i + ", seq_num " + seq_num);
56ec48be 998 if (seq_num === RFB.RQ_seq_num) {
07287cfd
JM
999 /* Remove it from reorder queue, decode it and
1000 * add it to the receive queue */
1001 console.log("Found re-ordered packet seq_num " + seq_num);
507b473a 1002 RFB.decode_message(RFB.RQ_reorder.splice(i, 1)[0], offset);
56ec48be 1003 RFB.RQ_seq_num++;
07287cfd
JM
1004 i = 0; // Start search again for next one
1005 } else {
1006 i++;
1007 }
1008 }
1009
1010 }
1011 }
1012
97763d0e
JM
1013 if (RFB.RQ.length > 0) {
1014 RFB.handle_message();
1015 }
07287cfd
JM
1016 //console.log("<< recv_message_reorder");
1017},
1018
1019handle_message: function () {
1020 switch (RFB.state) {
1021 case 'disconnected':
1022 console.error("Got data while disconnected");
1023 break;
07287cfd
JM
1024 case 'failed':
1025 console.log("Giving up!");
1026 RFB.disconnect();
1027 break;
1028 case 'normal':
1029 RFB.normal_msg();
1030 /*
56ec48be 1031 while (RFB.RQ.length > 0) {
c4164bda 1032 if (RFB.normal_msg() && RFB.state === 'normal') {
07287cfd
JM
1033 console.log("More to process");
1034 } else {
1035 break;
1036 }
1037 }
1038 */
1039 break;
1040 default:
1041 RFB.init_msg();
1042 break;
1043 }
1044},
1045
64ab5c4d 1046send_string: function (str) {
b7ec5487 1047 //console.log(">> send_string: " + str);
9f4af5a7 1048 RFB.send_array(str.split('').map(
c4164bda 1049 function (chr) { return chr.charCodeAt(0); } ) );
64ab5c4d
JM
1050},
1051
1052send_array: function (arr) {
1098b5bf 1053 //console.log(">> send_array: " + arr);
507b473a 1054 RFB.encode_message(arr);
c4164bda 1055 if (RFB.ws.bufferedAmount === 0) {
56ec48be
JM
1056 RFB.ws.send(RFB.SQ);
1057 RFB.SQ = "";
5d8e7ec0
JM
1058 } else {
1059 console.log("Delaying send");
1060 }
64ab5c4d
JM
1061},
1062
c539e4dc 1063DES: function (password, challenge) {
c4164bda
JM
1064 var i, passwd, response;
1065 passwd = [];
1066 response = challenge.slice();
1067 for (i=0; i < password.length; i++) {
c539e4dc 1068 passwd.push(password.charCodeAt(i));
532a9fd9 1069 }
c539e4dc
JM
1070
1071 DES.setKeys(passwd);
1072 DES.encrypt(response, 0, response, 0);
1073 DES.encrypt(response, 8, response, 8);
1074 return response;
532a9fd9
JM
1075},
1076
d064769c 1077flushClient: function () {
97bfe5ba
JM
1078 if (RFB.mouse_arr.length > 0) {
1079 //RFB.send_array(RFB.mouse_arr.concat(RFB.fbUpdateRequest(1)));
c4164bda 1080 RFB.send_array(RFB.mouse_arr);
5d8e7ec0
JM
1081 setTimeout(function() {
1082 RFB.send_array(RFB.fbUpdateRequest(1));
1083 }, 50);
1084
97bfe5ba 1085 RFB.mouse_arr = [];
8cf20615 1086 return true;
d064769c 1087 } else {
8cf20615 1088 return false;
d064769c
JM
1089 }
1090},
1091
8cf20615 1092checkEvents: function () {
c4164bda
JM
1093 var now;
1094 if (RFB.state === 'normal') {
8cf20615 1095 if (! RFB.flushClient()) {
c4164bda 1096 now = new Date().getTime();
8cf20615
JM
1097 if (now > RFB.last_req + RFB.req_rate) {
1098 RFB.last_req = now;
1099 RFB.send_array(RFB.fbUpdateRequest(1));
1100 }
1101 }
64ab5c4d 1102 }
5d8e7ec0 1103 RFB.checkEvents.delay(RFB.check_rate);
64ab5c4d
JM
1104},
1105
c4164bda
JM
1106keyX: function (e, down) {
1107 var arr;
30059bdf
JM
1108 if (RFB.clipboardFocus) {
1109 return true;
1110 }
64ab5c4d 1111 e.stop();
c4164bda 1112 arr = RFB.keyEvent(Canvas.getKeysym(e), down);
d064769c
JM
1113 arr = arr.concat(RFB.fbUpdateRequest(1));
1114 RFB.send_array(arr);
64ab5c4d
JM
1115},
1116
30059bdf
JM
1117keyDown: function (e) {
1118 //console.log(">> keyDown: " + Canvas.getKeysym(e));
c4164bda 1119 RFB.keyX(e, 1);
30059bdf
JM
1120},
1121
64ab5c4d 1122keyUp: function (e) {
1098b5bf 1123 //console.log(">> keyUp: " + Canvas.getKeysym(e));
c4164bda 1124 RFB.keyX(e, 0);
d064769c
JM
1125},
1126
1127mouseDown: function(e) {
c4164bda
JM
1128 var evt, x, y;
1129 evt = e.event || window.event;
d064769c
JM
1130 x = (evt.clientX - Canvas.c_x);
1131 y = (evt.clientY - Canvas.c_y);
c4164bda
JM
1132 //console.log(">> mouseDown " + evt.which + "/" + evt.button +
1133 // " " + x + "," + y);
97bfe5ba
JM
1134 RFB.mouse_buttonMask |= 1 << evt.button;
1135 RFB.mouse_arr = RFB.mouse_arr.concat( RFB.pointerEvent(x, y) );
d064769c
JM
1136
1137 RFB.flushClient();
64ab5c4d 1138},
65e27ddd 1139
d064769c 1140mouseUp: function(e) {
c4164bda
JM
1141 var evt, x, y;
1142 evt = e.event || window.event;
d064769c
JM
1143 x = (evt.clientX - Canvas.c_x);
1144 y = (evt.clientY - Canvas.c_y);
c4164bda
JM
1145 //console.log(">> mouseUp " + evt.which + "/" + evt.button +
1146 // " " + x + "," + y);
97bfe5ba
JM
1147 RFB.mouse_buttonMask ^= 1 << evt.button;
1148 RFB.mouse_arr = RFB.mouse_arr.concat( RFB.pointerEvent(x, y) );
d064769c
JM
1149
1150 RFB.flushClient();
1151},
1152
1153mouseMove: function(e) {
c4164bda
JM
1154 var evt, x, y;
1155 evt = e.event || window.event;
8cf20615
JM
1156 x = (evt.clientX - Canvas.c_x);
1157 y = (evt.clientY - Canvas.c_y);
1098b5bf 1158 //console.log('>> mouseMove ' + x + "," + y);
97bfe5ba 1159 RFB.mouse_arr = RFB.mouse_arr.concat( RFB.pointerEvent(x, y) );
d064769c
JM
1160},
1161
a575a383
JM
1162mouseWheel: function (e) {
1163 var evt, wheelData, bmask;
1164 evt = e.event || window.event;
1165 //e = e ? e : window.event;
1166 x = (evt.clientX - Canvas.c_x);
1167 y = (evt.clientY - Canvas.c_y);
1168 wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
1169 //console.log('>> mouseWheel ' + wheelData +
1170 // " " + x + "," + y);
1171
1172 if (wheelData > 0) {
1173 bmask = 1 << 3;
1174 } else {
1175 bmask = 1 << 4;
1176 }
1177 RFB.mouse_buttonMask |= bmask;
1178 RFB.mouse_arr = RFB.mouse_arr.concat( RFB.pointerEvent(x, y) );
1179 RFB.mouse_buttonMask ^= bmask;
1180 RFB.mouse_arr = RFB.mouse_arr.concat( RFB.pointerEvent(x, y) );
1181 RFB.flushClient();
1182},
1183
1184
30059bdf 1185clipboardCopyTo: function (text) {
91308399
JM
1186 console.log(">> clipboardCopyTo stub");
1187 // Stub
30059bdf
JM
1188},
1189
91308399
JM
1190externalUpdateState: function(state, msg) {
1191 console.log(">> externalUpdateState stub");
1192 // Stub
30059bdf 1193},
d064769c 1194
8759ea6f 1195updateState: function(state, statusMsg) {
91308399 1196 var func, cmsg;
c4164bda 1197 func = function(msg) { console.log(msg); };
8759ea6f
JM
1198 switch (state) {
1199 case 'failed':
c4164bda 1200 func = function(msg) { console.error(msg); };
8759ea6f
JM
1201 break;
1202 case 'normal':
8759ea6f
JM
1203 break;
1204 case 'disconnected':
8759ea6f
JM
1205 break;
1206 default:
c4164bda 1207 func = function(msg) { console.warn(msg); };
8759ea6f
JM
1208 break;
1209 }
1210
91308399
JM
1211 cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
1212 func("New state '" + state + "'." + cmsg);
1213
753bde8f
JM
1214 if ((RFB.state === 'failed') &&
1215 ((state === 'disconnected') || (state === 'closed'))) {
1216 // Leave the failed message
91308399
JM
1217 RFB.externalUpdateState(state)
1218 } else {
1219 RFB.state = state;
1220 RFB.externalUpdateState(state, statusMsg)
8759ea6f
JM
1221 }
1222},
65e27ddd
JM
1223
1224/*
1225 * Setup routines
1226 */
1227
532a9fd9 1228init_ws: function () {
b7ec5487 1229 console.log(">> init_ws");
c4164bda 1230
507b473a 1231 var uri = "", vars = [];
db504ade
JM
1232 if (RFB.encrypt) {
1233 uri = "wss://";
1234 } else {
1235 uri = "ws://";
1236 }
507b473a
JM
1237 uri += RFB.host + ":" + RFB.port + "/";
1238 if (RFB.b64encode) {
1239 vars.push("b64encode");
1240 }
07287cfd 1241 if (RFB.use_seq) {
507b473a
JM
1242 vars.push("seq_num");
1243 }
1244 if (vars.length > 0) {
1245 uri += "?" + vars.join("&");
07287cfd 1246 }
b7ec5487 1247 console.log("connecting to " + uri);
cc0410a3 1248 RFB.ws = new WebSocket(uri);
5d8e7ec0 1249
753bde8f 1250 RFB.ws.onmessage = RFB.recv_message;
cc0410a3 1251 RFB.ws.onopen = function(e) {
8759ea6f
JM
1252 console.log(">> WebSocket.onopen");
1253 RFB.updateState('ProtocolVersion', "Starting VNC handshake");
5d8e7ec0
JM
1254 RFB.sendID = setInterval(function() {
1255 /*
1256 * Send updates either at a rate of one update every 50ms,
1257 * or whatever slower rate the network can handle
1258 */
c4164bda 1259 if (RFB.ws.bufferedAmount === 0) {
56ec48be
JM
1260 if (RFB.SQ) {
1261 RFB.ws.send(RFB.SQ);
1262 RFB.SQ = "";
5d8e7ec0
JM
1263 }
1264 } else {
1265 console.log("Delaying send");
1266 }
1267 }, 50);
8759ea6f 1268 console.log("<< WebSocket.onopen");
65e27ddd 1269 };
cc0410a3 1270 RFB.ws.onclose = function(e) {
8759ea6f 1271 console.log(">> WebSocket.onclose");
5d8e7ec0 1272 clearInterval(RFB.sendID);
753bde8f 1273 RFB.updateState('disconnected', 'VNC disconnected');
8759ea6f 1274 console.log("<< WebSocket.onclose");
5d2c3864
JM
1275 };
1276 RFB.ws.onerror = function(e) {
8759ea6f
JM
1277 console.error(">> WebSocket.onerror");
1278 console.error(" " + e);
1279 console.error("<< WebSocket.onerror");
5d2c3864 1280 };
64ab5c4d 1281
b7ec5487 1282 console.log("<< init_ws");
64ab5c4d 1283},
65e27ddd 1284
5d8e7ec0
JM
1285init_vars: function () {
1286 /* Reset state */
56ec48be
JM
1287 RFB.cuttext = 'none';
1288 RFB.ct_length = 0;
1289 RFB.RQ = [];
1290 RFB.RQ_reorder = [];
1291 RFB.RQ_seq_num = 0;
1292 RFB.SQ = "";
1293 RFB.FBU.rects = 0;
1294 RFB.FBU.subrects = 0; // RRE and HEXTILE
1295 RFB.FBU.lines = 0; // RAW
1296 RFB.FBU.tiles = 0; // HEXTILE
97bfe5ba 1297 RFB.mouse_buttonmask = 0;
56ec48be 1298 RFB.mouse_arr = [];
c4164bda 1299}
ded9dfae 1300
64ab5c4d 1301}; /* End of RFB */