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