]> git.proxmox.com Git - mirror_novnc.git/blame - vnc.js
Hextile working. Improve latency by coallescing sends.
[mirror_novnc.git] / vnc.js
CommitLineData
64ab5c4d
JM
1Array.prototype.shift8 = function () {
2 return this.shift();
65e27ddd 3}
64ab5c4d 4Array.prototype.push8 = function (num) {
489d1676 5 this.push(num & 0xFF);
65e27ddd
JM
6}
7
64ab5c4d
JM
8Array.prototype.shift16 = function () {
9 return (this.shift() << 8) +
10 (this.shift() );
65e27ddd 11}
64ab5c4d 12Array.prototype.push16 = function (num) {
489d1676
JM
13 this.push((num >> 8) & 0xFF,
14 (num ) & 0xFF );
65e27ddd
JM
15}
16
489d1676 17
64ab5c4d
JM
18Array.prototype.shift32 = function () {
19 return (this.shift() << 24) +
20 (this.shift() << 16) +
21 (this.shift() << 8) +
22 (this.shift() );
489d1676 23}
64ab5c4d 24Array.prototype.push32 = function (num) {
489d1676
JM
25 this.push((num >> 24) & 0xFF,
26 (num >> 16) & 0xFF,
27 (num >> 8) & 0xFF,
28 (num ) & 0xFF );
65e27ddd 29}
489d1676 30
64ab5c4d
JM
31Array.prototype.shiftStr = function (len) {
32 var arr = this.splice(0, len);
33 return arr.map(function (num) {
34 return String.fromCharCode(num); } ).join('');
35}
36
37Array.prototype.shiftBytes = function (len) {
38 return this.splice(0, len);
65e27ddd
JM
39}
40
cc0410a3
JM
41/*
42 * Pending frame buffer update data
43 */
44var FBU = {
45 rects : 0,
ed7e776d 46 subrects : 0,
0f628064 47 tiles : 0,
cc0410a3
JM
48 bytes : 0,
49 x : 0,
50 y : 0,
51 width : 0,
52 height : 0,
53 encoding : 0,
b7ec5487 54 subencoding : -1,
9f4af5a7 55 background: null,
cc0410a3
JM
56 arr : null};
57
d9cbdc7d 58
65e27ddd 59/*
cc0410a3 60 * RFB namespace
65e27ddd
JM
61 */
62
64ab5c4d
JM
63RFB = {
64
cc0410a3
JM
65ws : null, // Web Socket object
66
67version : "RFB 003.003\n",
68state : 'ProtocolVersion',
69shared : 1,
9f4af5a7 70poll_rate : 1413,
64ab5c4d 71
cc0410a3
JM
72host : '',
73port : 5900,
74password : '',
75
76fb_width : 0,
77fb_height : 0,
78fb_name : "",
79fb_Bpp : 4,
f7618085 80rre_chunk : 100,
cc0410a3
JM
81
82
83/*
84 * Server message handlers
85 */
86
65e27ddd 87/* RFB/VNC initialisation */
64ab5c4d 88init_msg: function (data) {
b7ec5487 89 console.log(">> init_msg: " + RFB.state);
489d1676 90
64ab5c4d 91 switch (RFB.state) {
65e27ddd
JM
92
93 case 'ProtocolVersion' :
65e27ddd 94 if (data.length != 12) {
b7ec5487 95 console.log("Invalid protocol version from server");
64ab5c4d 96 RFB.state = 'reset';
65e27ddd
JM
97 return;
98 }
b7ec5487
JM
99 console.log("Server ProtocolVersion: " + data.shiftStr(11));
100 console.log("Sending ProtocolVersion: " + RFB.version.substr(0,11));
cc0410a3 101 RFB.send_string(RFB.version);
64ab5c4d 102 RFB.state = 'Authentication';
65e27ddd
JM
103 break;
104
105 case 'Authentication' :
64ab5c4d 106 if (data.length < 4) {
b7ec5487 107 console.log("Invalid auth frame");
64ab5c4d 108 RFB.state = 'reset';
65e27ddd
JM
109 return;
110 }
64ab5c4d 111 var scheme = data.shift32();
b7ec5487 112 console.log("Auth scheme: " + scheme);
65e27ddd
JM
113 switch (scheme) {
114 case 0: // connection failed
64ab5c4d
JM
115 var strlen = data.shift32();
116 var reason = data.shiftStr(strlen);
b7ec5487 117 console.log("auth failed: " + reason);
64ab5c4d 118 RFB.state = "failed";
65e27ddd
JM
119 return;
120 case 1: // no authentication
64ab5c4d
JM
121 RFB.send_array([RFB.shared]); // ClientInitialisation
122 RFB.state = "ServerInitialisation";
65e27ddd
JM
123 break;
124 case 2: // VNC authentication
8580b989 125 var challenge = data.shiftBytes(16);
b7ec5487
JM
126 console.log("Password: " + RFB.password);
127 console.log("Challenge: " + challenge + "(" + challenge.length + ")");
cc0410a3 128 passwd = RFB.passwdTwiddle(RFB.password);
b7ec5487 129 //console.log("passwd: " + passwd + "(" + passwd.length + ")");
5aeb9880 130 response = des(passwd, challenge, 1);
b7ec5487 131 //console.log("reponse: " + response + "(" + response.length + ")");
8580b989
JM
132
133 RFB.send_array(response);
134 RFB.state = "SecurityResult";
65e27ddd
JM
135 break;
136 }
137 break;
138
64ab5c4d 139 case 'SecurityResult' :
65e27ddd 140 if (data.length != 4) {
b7ec5487 141 console.log("Invalid server auth response");
64ab5c4d 142 RFB.state = 'reset';
65e27ddd
JM
143 return;
144 }
64ab5c4d 145 var resp = data.shift32();
65e27ddd
JM
146 switch (resp) {
147 case 0: // OK
b7ec5487 148 console.log("Authentication OK");
65e27ddd
JM
149 break;
150 case 1: // failed
b7ec5487 151 console.log("Authentication failed");
64ab5c4d 152 RFB.state = "reset";
65e27ddd
JM
153 return;
154 case 2: // too-many
b7ec5487 155 console.log("Too many authentication attempts");
64ab5c4d 156 RFB.state = "failed";
65e27ddd
JM
157 return;
158 }
64ab5c4d
JM
159 RFB.send_array([RFB.shared]); // ClientInitialisation
160 RFB.state = "ServerInitialisation";
65e27ddd
JM
161 break;
162
163 case 'ServerInitialisation' :
65e27ddd 164 if (data.length < 24) {
b7ec5487 165 console.log("Invalid server initialisation");
64ab5c4d 166 RFB.state = 'reset';
65e27ddd
JM
167 return;
168 }
489d1676
JM
169
170 /* Screen size */
b7ec5487 171 //console.log("data: " + data);
cc0410a3
JM
172 RFB.fb_width = data.shift16();
173 RFB.fb_height = data.shift16();
489d1676 174
b7ec5487 175 console.log("Screen size: " + RFB.fb_width + "x" + RFB.fb_height);
489d1676
JM
176
177 /* PIXEL_FORMAT */
64ab5c4d
JM
178 var bpp = data.shift8();
179 var depth = data.shift8();
180 var big_endian = data.shift8();
181 var true_color = data.shift8();
489d1676 182
b7ec5487
JM
183 console.log("bpp: " + bpp);
184 console.log("depth: " + depth);
185 console.log("big_endian: " + big_endian);
186 console.log("true_color: " + true_color);
489d1676
JM
187
188 /* Connection name/title */
64ab5c4d
JM
189 data.shiftStr(12);
190 var name_length = data.shift32();
cc0410a3 191 RFB.fb_name = data.shiftStr(name_length);
489d1676 192
b7ec5487 193 console.log("Name: " + RFB.fb_name);
cc0410a3 194 $('status').innerHTML = "Connected to: " + RFB.fb_name;
489d1676 195
cc0410a3 196 Canvas.init('vnc', RFB.fb_width, RFB.fb_height, RFB.keyDown, RFB.keyUp);
64ab5c4d 197
9f4af5a7
JM
198 var init = [];
199 init = init.concat(RFB.pixelFormat());
200 init = init.concat(RFB.encodings());
201 init = init.concat(RFB.fbUpdateRequest(0, 0, 0, RFB.fb_width, RFB.fb_height));
202 RFB.send_array(init);
203
204 /* Start polling */
205 RFB.poller.delay(RFB.poll_rate);
489d1676 206
64ab5c4d 207 RFB.state = 'normal';
65e27ddd
JM
208 break;
209 }
b7ec5487 210 console.log("<< init_msg (" + RFB.state + ")");
64ab5c4d 211},
65e27ddd 212
ed7e776d
JM
213/* Framebuffer update display functions */
214display_raw: function () {
b7ec5487 215 console.log(">> display_raw");
ed7e776d 216 Canvas.rfbImage(FBU.x, FBU.y, FBU.width, FBU.height, FBU.arr);
0f628064 217 FBU.arr.splice(0, FBU.width * FBU.height * RFB.fb_Bpp);
b7ec5487 218 FBU.rects --;
ed7e776d
JM
219},
220
221display_copy_rect: function () {
b7ec5487 222 console.log(">> display_copy_rect");
ed7e776d
JM
223 var old_x = FBU.arr.shift16();
224 var old_y = FBU.arr.shift16();
225 Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
226 FBU.rects --;
227},
228
229display_rre: function () {
b7ec5487 230 //console.log(">> display_rre (" + FBU.arr.length + " bytes)");
ed7e776d 231 if (FBU.subrects == 0) {
ed7e776d 232 FBU.subrects = FBU.arr.shift32();
b7ec5487 233 console.log(">> display_rre " + "(" + FBU.subrects + " subrects)");
6dab56f9 234 var color = FBU.arr.shiftBytes(RFB.fb_Bpp); // Background
ed7e776d 235 Canvas.rfbRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
f7618085 236 }
0f628064 237 while ((FBU.subrects > 0) && (FBU.arr.length >= (RFB.fb_Bpp + 8))) {
ed7e776d 238 FBU.subrects --;
6dab56f9
JM
239 var color = FBU.arr.shiftBytes(RFB.fb_Bpp);
240 var x = FBU.arr.shift16();
241 var y = FBU.arr.shift16();
242 var width = FBU.arr.shift16();
243 var height = FBU.arr.shift16();
244 Canvas.rfbRect(FBU.x + x, FBU.y + y, width, height, color);
ed7e776d 245 }
b7ec5487 246 //console.log(" display_rre: rects: " + FBU.rects + ", FBU.subrects: " + FBU.subrects);
ed7e776d
JM
247
248 if (FBU.subrects > 0) {
f7618085
JM
249 var chunk = Math.min(RFB.rre_chunk, FBU.subrects);
250 FBU.bytes = (RFB.fb_Bpp + 8) * chunk;
ed7e776d 251 } else {
6dab56f9 252 FBU.rects --;
ed7e776d 253 }
b7ec5487 254 //console.log("<< display_rre, FBU.bytes: " + FBU.bytes);
ed7e776d
JM
255},
256
0f628064 257display_hextile: function() {
9f4af5a7
JM
258 //console.log(">> display_hextile, tiles: " + FBU.tiles + ", arr.length: " + FBU.arr.length + ", bytes: " + FBU.bytes);
259 var subencoding, subrects, cur_tile, tile_x, x, w, tile_y, y, h;
b7ec5487
JM
260
261 /* FBU.bytes comes in as 0, FBU.arr.length at least 2 */
262 while ((FBU.tiles > 0) && (FBU.arr.length >= FBU.bytes)) {
9f4af5a7
JM
263 cur_tile = FBU.total_tiles - FBU.tiles;
264 tile_x = cur_tile % FBU.tiles_x;
265 tile_y = Math.floor(cur_tile / FBU.tiles_x);
266 x = FBU.x + tile_x * 16;
267 y = FBU.y + tile_y * 16;
268 w = Math.min(16, (FBU.x + FBU.width) - x)
269 h = Math.min(16, (FBU.y + FBU.height) - y)
270 subrects = 0;
b7ec5487
JM
271 if (FBU.subencoding == -1) {
272 /* We enter with at least 2 bytes */
9f4af5a7
JM
273 subencoding = FBU.arr[0]; // Peek
274 //console.log(" display_hextile, subencoding: " + subencoding);
275 FBU.bytes++; // Since we aren't shifting it off
b7ec5487
JM
276 //console.log(" subencoding: " + subencoding);
277 if (subencoding > 30) { // Raw
278 console.log("Illegal subencoding " + subencoding);
279 RFB.state = "failed";
280 return;
281 }
282
283 /* Figure out how much we are expecting */
284 if (subencoding & 0x01) { // Raw
9f4af5a7
JM
285 //console.log(" Raw subencoding");
286 FBU.bytes = w * h * RFB.fb_Bpp;
287 if (FBU.arr[FBU.bytes] == 0) {
288 /* Weird: ignore blanks after RAW */
289 //console.log(" Ignoring blank after RAW");
290 FBU.bytes ++;
b7ec5487 291 }
b7ec5487
JM
292 } else {
293 if (subencoding & 0x02) { // Background
294 FBU.bytes += RFB.fb_Bpp;
295 }
296 if (subencoding & 0x04) { // Foreground
297 FBU.bytes += RFB.fb_Bpp;
298 }
299 if (subencoding & 0x08) { // AnySubrects
9f4af5a7
JM
300 FBU.bytes++; // Since we aren't shifting it off
301 if (FBU.arr.length < FBU.bytes) {
302 /* Wait for subrects byte */
303 //console.log(" waiting for subrects byte");
304 return;
305 }
306 subrects = FBU.arr[FBU.bytes-1]; // Peek
307 if (subencoding & 0x10) { // SubrectsColoured
b7ec5487
JM
308 FBU.bytes += subrects * (RFB.fb_Bpp + 2);
309 } else {
310 FBU.bytes += subrects * 2;
311 }
312 }
313 }
314 }
315
9f4af5a7
JM
316 //console.log(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + ", subencoding:" + subencoding + ", subrects:" + subrects + ", tile:" + tile_x + "," + tile_y + " [" + x + "," + y + "], arr.length:" + FBU.arr.length + ", bytes:" + FBU.bytes);
317 //console.log(" arr[0..30]: " + FBU.arr.slice(0,30));
b7ec5487 318 if (FBU.arr.length < FBU.bytes) {
9f4af5a7 319 //console.log(" waiting for " + (FBU.bytes - FBU.arr.length) + "bytes");
b7ec5487
JM
320 return;
321 }
322
323 if (subencoding > -1) {
324 /* We know the encoding and have a whole tile */
325 FBU.subencoding = FBU.arr.shift();
326 if (FBU.subencoding == 0) {
9f4af5a7 327 Canvas.rfbRect(x, y, w, h, FBU.background);
b7ec5487 328 } else if (FBU.subencoding & 0x01) { // Raw
9f4af5a7
JM
329 Canvas.rfbImage(x, y, w, h, FBU.arr);
330 FBU.arr.splice(0, FBU.bytes - 1);
b7ec5487
JM
331 } else {
332 if (FBU.subencoding & 0x02) { // Background
333 FBU.background = FBU.arr.shiftBytes(RFB.fb_Bpp);
9f4af5a7 334 //console.log(" background: " + FBU.background);
b7ec5487
JM
335 }
336 if (FBU.subencoding & 0x04) { // Foreground
337 FBU.foreground = FBU.arr.shiftBytes(RFB.fb_Bpp);
9f4af5a7 338 //console.log(" foreground: " + FBU.foreground);
b7ec5487 339 }
9f4af5a7 340 Canvas.rfbRect(x, y, w, h, FBU.background);
b7ec5487
JM
341 if (FBU.subencoding & 0x08) { // AnySubrects
342 subrects = FBU.arr.shift8();
343 for (var i = 0; i < subrects; i ++) {
9f4af5a7 344 if (FBU.subencoding & 0x10) { // SubrectsColoured
b7ec5487
JM
345 var color = FBU.arr.shiftBytes(RFB.fb_Bpp);
346 } else {
347 var color = FBU.foreground;
348 }
349 var xy = FBU.arr.shift8();
9f4af5a7
JM
350 var sx = x + (xy >> 4);
351 var sy = y + (xy & 0x0f);
b7ec5487
JM
352
353 var wh = FBU.arr.shift8();
9f4af5a7
JM
354 var sw = (wh >> 4) + 1;
355 var sh = (wh & 0x0f) + 1;
b7ec5487 356
9f4af5a7 357 Canvas.rfbRect(sx, sy, sw, sh, color);
b7ec5487
JM
358 }
359 }
360 }
361 FBU.subencoding = -1;
362 FBU.tiles --;
363 FBU.bytes = 0;
364 }
0f628064
JM
365 }
366
b7ec5487
JM
367 if (FBU.tiles > 0) {
368 FBU.bytes = 2;
369 } else {
370 FBU.background = [255, 255, 0, 0]; // Yellow: invalid
371 FBU.rects --;
372 }
373
9f4af5a7 374 //console.log("<< display_hextile");
0f628064
JM
375},
376
ed7e776d 377
65e27ddd 378/* Normal RFB/VNC messages */
64ab5c4d 379normal_msg: function (data) {
b7ec5487 380 //console.log(">> normal_msg");
cc0410a3 381 if ((FBU.rects > 0) || (FBU.bytes > 0)) {
64ab5c4d 382 var msg_type = 0;
c8460b03 383 } else {
64ab5c4d 384 var msg_type = data.shift8();
489d1676 385 }
65e27ddd
JM
386 switch (msg_type) {
387 case 0: // FramebufferUpdate
cc0410a3 388 if (FBU.rects == 0) {
64ab5c4d 389 data.shift8();
cc0410a3 390 FBU.rects = data.shift16();
b7ec5487 391 console.log("FramebufferUpdate, " + FBU.rects + " rects");
cc0410a3
JM
392 FBU.bytes = 0;
393 FBU.arr = [];
64ab5c4d 394 } else {
b7ec5487 395 //console.log("FramebufferUpdate continuation");
64ab5c4d
JM
396 }
397
0f628064
JM
398 if (data.length > 0 ) {
399 FBU.arr = FBU.arr.concat(data);
400 }
401
402 while (FBU.arr.length > 0) {
cc0410a3 403 if (FBU.bytes == 0) {
0f628064
JM
404 FBU.x = FBU.arr.shift16();
405 FBU.y = FBU.arr.shift16();
406 FBU.width = FBU.arr.shift16();
407 FBU.height = FBU.arr.shift16();
408 FBU.encoding = parseInt(FBU.arr.shift32(), 10);
b7ec5487 409 console.log("encoding: " + FBU.encoding);
cc0410a3 410 switch (FBU.encoding) {
64ab5c4d 411 case 0: // Raw
cc0410a3 412 FBU.bytes = FBU.width * FBU.height * RFB.fb_Bpp;
64ab5c4d
JM
413 break;
414 case 1: // Copy-Rect
48617e27 415 FBU.bytes = 4;
64ab5c4d 416 break;
ed7e776d 417 case 2: // RRE
ed7e776d
JM
418 FBU.bytes = 4 + RFB.fb_Bpp;
419 break;
0f628064 420 case 5: // hextile
b7ec5487
JM
421 FBU.bytes = 2; // No header; get it started
422 FBU.tiles_x = Math.ceil(FBU.width/16);
423 FBU.tiles_y = Math.ceil(FBU.height/16);
424 FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
425 FBU.tiles = FBU.total_tiles;
426 break;
427 default:
428 console.log("Unsupported encoding " + FBU.encoding);
429 RFB.state = "failed";
0f628064 430 break;
64ab5c4d
JM
431 }
432 }
b7ec5487 433 //console.log("FBU.arr.length: " + FBU.arr.length + ", FBU.bytes: " + FBU.bytes);
64ab5c4d 434
0f628064 435 if (FBU.arr.length >= FBU.bytes) {
b7ec5487 436 //console.log('Done rect:');
0f628064
JM
437 FBU.bytes = 0;
438
439 switch (FBU.encoding) {
440 case 0: RFB.display_raw(); break; // Raw
441 case 1: RFB.display_copy_rect(); break; // Copy-Rect
442 case 2: RFB.display_rre(); break; // RRE
443 case 5: RFB.display_hextile(); break; // hextile
444 }
445 } else {
446 /* We don't have enough yet */
447 FBU.bytes = FBU.bytes - data.length;
448 break;
449 }
b7ec5487 450 if (RFB.state != "normal") return;
64ab5c4d 451 }
0f628064 452
b7ec5487 453 //console.log("Finished frame buffer update");
65e27ddd
JM
454 break;
455 case 1: // SetColourMapEntries
b7ec5487 456 console.log("SetColourMapEntries");
65e27ddd
JM
457 break;
458 case 2: // Bell
b7ec5487 459 console.log("Bell");
65e27ddd
JM
460 break;
461 case 3: // ServerCutText
b7ec5487 462 console.log("ServerCutText");
65e27ddd
JM
463 break;
464 default:
b7ec5487 465 console.log("Unknown server message type: " + msg_type);
65e27ddd
JM
466 break;
467 }
b7ec5487 468 //console.log("<< normal_msg");
64ab5c4d 469},
65e27ddd
JM
470
471/*
472 * Client message routines
473 */
474
9f4af5a7 475pixelFormat: function () {
b7ec5487 476 console.log(">> setPixelFormat");
9f4af5a7
JM
477 var arr;
478 arr = [0]; // msg-type
64ab5c4d
JM
479 arr.push8(0); // padding
480 arr.push8(0); // padding
481 arr.push8(0); // padding
482
cc0410a3 483 arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
64ab5c4d
JM
484 arr.push8(24); // depth
485 arr.push8(0); // little-endian
486 arr.push8(1); // true-color
487
488 arr.push16(255); // red-max
489 arr.push16(255); // green-max
490 arr.push16(255); // blue-max
9f4af5a7 491 arr.push8(0); // red-shift
64ab5c4d 492 arr.push8(8); // green-shift
9f4af5a7 493 arr.push8(16); // blue-shift
64ab5c4d 494
9f4af5a7
JM
495 arr.push8(0); // padding
496 arr.push8(0); // padding
497 arr.push8(0); // padding
b7ec5487 498 console.log("<< setPixelFormat");
9f4af5a7 499 return arr;
64ab5c4d
JM
500},
501
502fixColourMapEntries: function () {
503},
504
9f4af5a7 505encodings: function () {
b7ec5487 506 console.log(">> setEncodings");
9f4af5a7
JM
507 var arr;
508 arr = [2]; // msg-type
64ab5c4d 509 arr.push8(0); // padding
b7ec5487
JM
510
511 //arr.push16(3); // encoding count
512 arr.push16(4); // encoding count
513 arr.push32(5); // hextile encoding
514
ed7e776d 515 arr.push32(2); // RRE encoding
64ab5c4d
JM
516 arr.push32(1); // copy-rect encoding
517 arr.push32(0); // raw encoding
b7ec5487 518 console.log("<< setEncodings");
9f4af5a7 519 return arr;
64ab5c4d 520},
65e27ddd 521
64ab5c4d 522fbUpdateRequest: function (incremental, x, y, xw, yw) {
b7ec5487 523 //console.log(">> fbUpdateRequest");
9f4af5a7
JM
524 var arr;
525 arr = [3]; // msg-type
64ab5c4d
JM
526 arr.push8(incremental);
527 arr.push16(x);
528 arr.push16(y);
529 arr.push16(xw);
530 arr.push16(yw);
b7ec5487 531 //console.log("<< fbUpdateRequest");
9f4af5a7 532 return arr;
64ab5c4d 533},
65e27ddd 534
d9cbdc7d 535keyEvent: function (keysym, down) {
b7ec5487 536 console.log(">> keyEvent, keysym: " + keysym + ", down: " + down);
9f4af5a7
JM
537 var arr;
538 arr = [4]; // msg-type
64ab5c4d
JM
539 arr.push8(down);
540 arr.push16(0);
d9cbdc7d 541 arr.push32(keysym);
b7ec5487 542 //console.log("keyEvent array: " + arr);
9f4af5a7 543 arr = arr.concat(RFB.fbUpdateRequest(1, 0, 0, RFB.fb_width, RFB.fb_height));
64ab5c4d 544 RFB.send_array(arr);
b7ec5487 545 //console.log("<< keyEvent");
64ab5c4d 546},
65e27ddd 547
64ab5c4d
JM
548pointerEvent: function () {
549},
65e27ddd 550
64ab5c4d
JM
551clientCutText: function () {
552},
553
554
555/*
556 * Utility routines
557 */
558
559send_string: function (str) {
b7ec5487 560 //console.log(">> send_string: " + str);
9f4af5a7
JM
561 RFB.send_array(str.split('').map(
562 function (chr) { return chr.charCodeAt(0) } ) );
64ab5c4d
JM
563},
564
565send_array: function (arr) {
9f4af5a7 566 //console.log(">> send_array: " + arr);
b7ec5487 567 //console.log(">> send_array: " + Base64.encode_array(arr));
cc0410a3 568 RFB.ws.send(Base64.encode_array(arr));
64ab5c4d
JM
569},
570
532a9fd9
JM
571/* Mirror bits of each character and return as array */
572passwdTwiddle: function (passwd) {
9f4af5a7
JM
573 var arr;
574 arr = [];
532a9fd9
JM
575 for (var i=0; i< passwd.length; i++) {
576 var c = passwd.charCodeAt(i);
577 arr.push( ((c & 0x80) >> 7) +
578 ((c & 0x40) >> 5) +
579 ((c & 0x20) >> 3) +
580 ((c & 0x10) >> 1) +
581 ((c & 0x08) << 1) +
582 ((c & 0x04) << 3) +
583 ((c & 0x02) << 5) +
584 ((c & 0x01) << 7) );
585 }
586 return arr;
587},
588
64ab5c4d
JM
589poller: function () {
590 if (RFB.state == 'normal') {
9f4af5a7 591 RFB.send_array(RFB.fbUpdateRequest(1, 0, 0, RFB.fb_width, RFB.fb_height));
64ab5c4d
JM
592 RFB.poller.delay(RFB.poll_rate);
593 }
594},
595
596keyDown: function (e) {
b7ec5487 597 //console.log(">> keyDown: " + e.key + "(" + e.code + ")");
64ab5c4d 598 e.stop();
d9cbdc7d 599 RFB.keyEvent(Canvas.getKeysym(e), 1);
64ab5c4d
JM
600},
601
602keyUp: function (e) {
b7ec5487 603 //console.log(">> keyUp: " + e.key + "(" + e.code + ")");
64ab5c4d 604 e.stop();
d9cbdc7d 605 RFB.keyEvent(Canvas.getKeysym(e), 0);
64ab5c4d 606},
65e27ddd
JM
607
608
609/*
610 * Setup routines
611 */
612
532a9fd9 613init_ws: function () {
b7ec5487 614 console.log(">> init_ws");
cc0410a3 615 var uri = "ws://" + RFB.host + ":" + RFB.port;
b7ec5487 616 console.log("connecting to " + uri);
cc0410a3
JM
617 RFB.ws = new WebSocket(uri);
618 RFB.ws.onmessage = function(e) {
b7ec5487 619 //console.log(">> onmessage");
489d1676 620 var data = Base64.decode_array(e.data);
b7ec5487 621 //console.log("decoded array: " + data);
64ab5c4d
JM
622 if (RFB.state != 'normal') {
623 RFB.init_msg(data);
65e27ddd 624 } else {
64ab5c4d 625 RFB.normal_msg(data);
65e27ddd 626 }
64ab5c4d 627 if (RFB.state == 'reset') {
65e27ddd 628 /* close and reset connection */
532a9fd9
JM
629 RFB.disconnect();
630 RFB.init_ws();
64ab5c4d 631 } else if (RFB.state == 'failed') {
b7ec5487 632 console.log("Giving up!");
532a9fd9 633 RFB.disconnect();
65e27ddd 634 }
b7ec5487 635 //console.log("<< onmessage");
65e27ddd 636 };
cc0410a3 637 RFB.ws.onopen = function(e) {
b7ec5487 638 console.log(">> onopen");
64ab5c4d 639 RFB.state = "ProtocolVersion";
b7ec5487 640 console.log("<< onopen");
65e27ddd 641 };
cc0410a3 642 RFB.ws.onclose = function(e) {
b7ec5487 643 console.log(">> onclose");
64ab5c4d 644 RFB.state = "closed";
b7ec5487 645 console.log("<< onclose");
65e27ddd 646 }
64ab5c4d 647
b7ec5487 648 console.log("<< init_ws");
64ab5c4d 649},
65e27ddd 650
532a9fd9 651connect: function () {
b7ec5487 652 console.log(">> connect");
cc0410a3
JM
653 RFB.host = $('host').value;
654 RFB.port = $('port').value;
655 RFB.password = $('password').value;
532a9fd9 656 if ((!host) || (!port)) {
b7ec5487 657 console.log("must set host and port");
532a9fd9
JM
658 return;
659 }
cc0410a3
JM
660 if (RFB.ws) {
661 RFB.ws.close();
65e27ddd 662 }
532a9fd9
JM
663 RFB.init_ws();
664 $('connectButton').value = "Disconnect";
665 $('connectButton').onclick = RFB.disconnect;
b7ec5487 666 console.log("<< connect");
532a9fd9
JM
667
668},
65e27ddd 669
532a9fd9 670disconnect: function () {
b7ec5487 671 console.log(">> disconnect");
cc0410a3
JM
672 if (RFB.ws) {
673 RFB.ws.close();
532a9fd9
JM
674 }
675 if (Canvas.ctx) {
676 Canvas.clear();
677 }
678 $('connectButton').value = "Connect";
679 $('connectButton').onclick = RFB.connect;
680 $('status').innerHTML = "Disconnected";
b7ec5487 681 console.log("<< disconnect");
65e27ddd
JM
682}
683
64ab5c4d 684}; /* End of RFB */