]> git.proxmox.com Git - mirror_novnc.git/blame - vnc.js
WebWorkers example with two way messages.
[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,
46 bytes : 0,
47 x : 0,
48 y : 0,
49 width : 0,
50 height : 0,
51 encoding : 0,
52 arr : null};
53
d9cbdc7d 54
65e27ddd 55/*
cc0410a3 56 * RFB namespace
65e27ddd
JM
57 */
58
64ab5c4d
JM
59RFB = {
60
cc0410a3
JM
61ws : null, // Web Socket object
62
63version : "RFB 003.003\n",
64state : 'ProtocolVersion',
65shared : 1,
64ab5c4d
JM
66poll_rate : 3000,
67
cc0410a3
JM
68host : '',
69port : 5900,
70password : '',
71
72fb_width : 0,
73fb_height : 0,
74fb_name : "",
75fb_Bpp : 4,
76
77
78/*
79 * Server message handlers
80 */
81
65e27ddd 82/* RFB/VNC initialisation */
64ab5c4d 83init_msg: function (data) {
cc0410a3 84 debug(">> init_msg: " + RFB.state);
489d1676 85
64ab5c4d 86 switch (RFB.state) {
65e27ddd
JM
87
88 case 'ProtocolVersion' :
65e27ddd
JM
89 if (data.length != 12) {
90 debug("Invalid protocol version from server");
64ab5c4d 91 RFB.state = 'reset';
65e27ddd
JM
92 return;
93 }
cc0410a3
JM
94 debug("Server ProtocolVersion: " + data.shiftStr(11));
95 debug("Sending ProtocolVersion: " + RFB.version.substr(0,11));
96 RFB.send_string(RFB.version);
64ab5c4d 97 RFB.state = 'Authentication';
65e27ddd
JM
98 break;
99
100 case 'Authentication' :
64ab5c4d
JM
101 if (data.length < 4) {
102 debug("Invalid auth frame");
103 RFB.state = 'reset';
65e27ddd
JM
104 return;
105 }
64ab5c4d 106 var scheme = data.shift32();
65e27ddd
JM
107 debug("Auth scheme: " + scheme);
108 switch (scheme) {
109 case 0: // connection failed
64ab5c4d
JM
110 var strlen = data.shift32();
111 var reason = data.shiftStr(strlen);
65e27ddd 112 debug("auth failed: " + reason);
64ab5c4d 113 RFB.state = "failed";
65e27ddd
JM
114 return;
115 case 1: // no authentication
64ab5c4d
JM
116 RFB.send_array([RFB.shared]); // ClientInitialisation
117 RFB.state = "ServerInitialisation";
65e27ddd
JM
118 break;
119 case 2: // VNC authentication
8580b989 120 var challenge = data.shiftBytes(16);
cc0410a3
JM
121 debug("Password: " + RFB.password);
122 debug("Challenge: " + challenge + "(" + challenge.length + ")");
123 passwd = RFB.passwdTwiddle(RFB.password);
124 //debug("passwd: " + passwd + "(" + passwd.length + ")");
5aeb9880 125 response = des(passwd, challenge, 1);
cc0410a3 126 //debug("reponse: " + response + "(" + response.length + ")");
8580b989
JM
127
128 RFB.send_array(response);
129 RFB.state = "SecurityResult";
65e27ddd
JM
130 break;
131 }
132 break;
133
64ab5c4d 134 case 'SecurityResult' :
65e27ddd
JM
135 if (data.length != 4) {
136 debug("Invalid server auth response");
64ab5c4d 137 RFB.state = 'reset';
65e27ddd
JM
138 return;
139 }
64ab5c4d 140 var resp = data.shift32();
65e27ddd
JM
141 switch (resp) {
142 case 0: // OK
143 debug("Authentication OK");
144 break;
145 case 1: // failed
146 debug("Authentication failed");
64ab5c4d 147 RFB.state = "reset";
65e27ddd
JM
148 return;
149 case 2: // too-many
150 debug("Too many authentication attempts");
64ab5c4d 151 RFB.state = "failed";
65e27ddd
JM
152 return;
153 }
64ab5c4d
JM
154 RFB.send_array([RFB.shared]); // ClientInitialisation
155 RFB.state = "ServerInitialisation";
65e27ddd
JM
156 break;
157
158 case 'ServerInitialisation' :
65e27ddd
JM
159 if (data.length < 24) {
160 debug("Invalid server initialisation");
64ab5c4d 161 RFB.state = 'reset';
65e27ddd
JM
162 return;
163 }
489d1676
JM
164
165 /* Screen size */
64ab5c4d 166 //debug("data: " + data);
cc0410a3
JM
167 RFB.fb_width = data.shift16();
168 RFB.fb_height = data.shift16();
489d1676 169
cc0410a3 170 debug("Screen size: " + RFB.fb_width + "x" + RFB.fb_height);
489d1676
JM
171
172 /* PIXEL_FORMAT */
64ab5c4d
JM
173 var bpp = data.shift8();
174 var depth = data.shift8();
175 var big_endian = data.shift8();
176 var true_color = data.shift8();
489d1676 177
64ab5c4d 178 debug("bpp: " + bpp);
489d1676
JM
179 debug("depth: " + depth);
180 debug("big_endian: " + big_endian);
181 debug("true_color: " + true_color);
182
183 /* Connection name/title */
64ab5c4d
JM
184 data.shiftStr(12);
185 var name_length = data.shift32();
cc0410a3 186 RFB.fb_name = data.shiftStr(name_length);
489d1676 187
cc0410a3
JM
188 debug("Name: " + RFB.fb_name);
189 $('status').innerHTML = "Connected to: " + RFB.fb_name;
489d1676 190
cc0410a3 191 Canvas.init('vnc', RFB.fb_width, RFB.fb_height, RFB.keyDown, RFB.keyUp);
64ab5c4d
JM
192
193 RFB.setEncodings();
194 RFB.setPixelFormat();
489d1676 195
cc0410a3 196 RFB.fbUpdateRequest(0, 0, 0, RFB.fb_width, RFB.fb_height);
489d1676 197
64ab5c4d 198 RFB.state = 'normal';
65e27ddd
JM
199 break;
200 }
cc0410a3 201 debug("<< init_msg (" + RFB.state + ")");
64ab5c4d 202},
65e27ddd
JM
203
204/* Normal RFB/VNC messages */
64ab5c4d
JM
205normal_msg: function (data) {
206 //debug(">> normal_msg");
cc0410a3 207 if ((FBU.rects > 0) || (FBU.bytes > 0)) {
64ab5c4d 208 var msg_type = 0;
c8460b03 209 } else {
64ab5c4d 210 var msg_type = data.shift8();
489d1676 211 }
65e27ddd
JM
212 switch (msg_type) {
213 case 0: // FramebufferUpdate
cc0410a3 214 if (FBU.rects == 0) {
64ab5c4d 215 data.shift8();
cc0410a3
JM
216 FBU.rects = data.shift16();
217 debug("FramebufferUpdate, " + FBU.rects + " rects");
218 FBU.bytes = 0;
219 FBU.arr = [];
64ab5c4d
JM
220 } else {
221 //debug("FramebufferUpdate continuation");
222 }
223
224 while (data.length > 0) {
225 //debug("data.length: " + data.length);
cc0410a3
JM
226 if (FBU.bytes == 0) {
227 FBU.x = data.shift16();
228 FBU.y = data.shift16();
229 FBU.width = data.shift16();
230 FBU.height = data.shift16();
48617e27
JM
231 FBU.encoding = parseInt(data.shift32(), 10);
232 //debug("encoding: " + FBU.encoding);
cc0410a3
JM
233 //debug('New rect: ' + FBU.x + "," + FBU.y + " -> " + (FBU.x + FBU.width) + "," + (FBU.y + FBU.height));
234 switch (FBU.encoding) {
64ab5c4d 235 case 0: // Raw
cc0410a3 236 FBU.bytes = FBU.width * FBU.height * RFB.fb_Bpp;
64ab5c4d
JM
237 break;
238 case 1: // Copy-Rect
48617e27 239 FBU.bytes = 4;
64ab5c4d
JM
240 break;
241 }
242 } else {
cc0410a3 243 if (data.length >= FBU.bytes) {
48617e27 244 //debug('Done rect:');
cc0410a3
JM
245 FBU.arr = FBU.arr.concat(data.shiftBytes(FBU.bytes))
246 FBU.bytes = 0;
64ab5c4d 247
cc0410a3 248 switch (FBU.encoding) {
64ab5c4d 249 case 0: // Raw
48617e27 250 //debug('Raw-Rect: (' + FBU.x + "," + FBU.y + ")X(" + (FBU.x + FBU.width) + "," + (FBU.y + FBU.height) + ")");
cc0410a3 251 Canvas.rfbImage(FBU.x, FBU.y, FBU.width, FBU.height, FBU.arr);
64ab5c4d
JM
252 break;
253 case 1: // Copy-Rect
48617e27
JM
254 var old_x = FBU.arr.shift16();
255 var old_y = FBU.arr.shift16();
256 //debug('Copy-Rect: (' + old_x + "," + old_y + ")X(" + (FBU.x + FBU.width) + "," + (FBU.y + FBU.height) + ") -> (" + FBU.x + "," + FBU.y + ")");
257
258 Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
64ab5c4d
JM
259 break;
260 }
cc0410a3
JM
261 FBU.arr = [];
262 FBU.rects --;
64ab5c4d 263 } else {
cc0410a3
JM
264 //debug('Part rect: ' + FBU.x + "," + FBU.y + " -> " + (FBU.x + FBU.width) + "," + (FBU.y + FBU.height));
265 FBU.bytes = FBU.bytes - data.length;
266 FBU.arr = FBU.arr.concat(data.shiftBytes(data.length))
64ab5c4d
JM
267 }
268 }
269
cc0410a3 270 //debug("Bytes remaining: " + FBU.bytes);
64ab5c4d
JM
271 }
272 //debug("Finished frame buffer update");
65e27ddd
JM
273 break;
274 case 1: // SetColourMapEntries
275 debug("SetColourMapEntries");
276 break;
277 case 2: // Bell
278 debug("Bell");
279 break;
280 case 3: // ServerCutText
281 debug("ServerCutText");
282 break;
283 default:
284 debug("Unknown server message type: " + msg_type);
285 break;
286 }
64ab5c4d
JM
287 //debug("<< normal_msg");
288},
65e27ddd
JM
289
290/*
291 * Client message routines
292 */
293
64ab5c4d
JM
294setPixelFormat: function () {
295 debug(">> setPixelFormat");
296 var arr = [0]; // msg-type
297 arr.push8(0); // padding
298 arr.push8(0); // padding
299 arr.push8(0); // padding
300
cc0410a3 301 arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
64ab5c4d
JM
302 arr.push8(24); // depth
303 arr.push8(0); // little-endian
304 arr.push8(1); // true-color
305
306 arr.push16(255); // red-max
307 arr.push16(255); // green-max
308 arr.push16(255); // blue-max
309 arr.push8(16); // red-shift
310 arr.push8(8); // green-shift
311 arr.push8(0); // blue-shift
312
313 arr.push8(0); // padding
314 arr.push8(0); // padding
315 arr.push8(0); // padding
316 RFB.send_array(arr);
317 debug("<< setPixelFormat");
318},
319
320fixColourMapEntries: function () {
321},
322
323setEncodings: function () {
489d1676
JM
324 debug(">> setEncodings");
325 var arr = [2]; // msg-type
64ab5c4d
JM
326 arr.push8(0); // padding
327 arr.push16(2); // encoding count
328 arr.push32(1); // copy-rect encoding
329 arr.push32(0); // raw encoding
330 RFB.send_array(arr);
489d1676 331 debug("<< setEncodings");
64ab5c4d 332},
65e27ddd 333
64ab5c4d 334fbUpdateRequest: function (incremental, x, y, xw, yw) {
d9cbdc7d 335 //debug(">> fbUpdateRequest");
489d1676 336 var arr = [3]; // msg-type
64ab5c4d
JM
337 arr.push8(incremental);
338 arr.push16(x);
339 arr.push16(y);
340 arr.push16(xw);
341 arr.push16(yw);
342 RFB.send_array(arr);
d9cbdc7d 343 //debug("<< fbUpdateRequest");
64ab5c4d 344},
65e27ddd 345
d9cbdc7d
JM
346keyEvent: function (keysym, down) {
347 debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
64ab5c4d
JM
348 var arr = [4]; // msg-type
349 arr.push8(down);
350 arr.push16(0);
d9cbdc7d
JM
351 arr.push32(keysym);
352 //debug("keyEvent array: " + arr);
64ab5c4d 353 RFB.send_array(arr);
cc0410a3 354 RFB.fbUpdateRequest(1, 0, 0, RFB.fb_width, RFB.fb_height);
d9cbdc7d 355 //debug("<< keyEvent");
64ab5c4d 356},
65e27ddd 357
64ab5c4d
JM
358pointerEvent: function () {
359},
65e27ddd 360
64ab5c4d
JM
361clientCutText: function () {
362},
363
364
365/*
366 * Utility routines
367 */
368
369send_string: function (str) {
d9cbdc7d 370 //debug(">> send_string: " + str);
cc0410a3
JM
371 var arr = str.split('').map(function (chr) {
372 return chr.charCodeAt(0) } );
373 RFB.send_array(arr);
64ab5c4d
JM
374},
375
376send_array: function (arr) {
d9cbdc7d 377 //debug(">> send_array: " + Base64.encode_array(arr));
cc0410a3 378 RFB.ws.send(Base64.encode_array(arr));
64ab5c4d
JM
379},
380
532a9fd9
JM
381/* Mirror bits of each character and return as array */
382passwdTwiddle: function (passwd) {
383 var arr = [];
384 for (var i=0; i< passwd.length; i++) {
385 var c = passwd.charCodeAt(i);
386 arr.push( ((c & 0x80) >> 7) +
387 ((c & 0x40) >> 5) +
388 ((c & 0x20) >> 3) +
389 ((c & 0x10) >> 1) +
390 ((c & 0x08) << 1) +
391 ((c & 0x04) << 3) +
392 ((c & 0x02) << 5) +
393 ((c & 0x01) << 7) );
394 }
395 return arr;
396},
397
64ab5c4d
JM
398poller: function () {
399 if (RFB.state == 'normal') {
cc0410a3 400 RFB.fbUpdateRequest(1, 0, 0, RFB.fb_width, RFB.fb_height);
64ab5c4d
JM
401 RFB.poller.delay(RFB.poll_rate);
402 }
403},
404
405keyDown: function (e) {
d9cbdc7d 406 //debug(">> keyDown: " + e.key + "(" + e.code + ")");
64ab5c4d 407 e.stop();
d9cbdc7d 408 RFB.keyEvent(Canvas.getKeysym(e), 1);
64ab5c4d
JM
409},
410
411keyUp: function (e) {
d9cbdc7d 412 //debug(">> keyUp: " + e.key + "(" + e.code + ")");
64ab5c4d 413 e.stop();
d9cbdc7d 414 RFB.keyEvent(Canvas.getKeysym(e), 0);
64ab5c4d 415},
65e27ddd
JM
416
417
418/*
419 * Setup routines
420 */
421
532a9fd9
JM
422init_ws: function () {
423 debug(">> init_ws");
cc0410a3 424 var uri = "ws://" + RFB.host + ":" + RFB.port;
65e27ddd 425 debug("connecting to " + uri);
cc0410a3
JM
426 RFB.ws = new WebSocket(uri);
427 RFB.ws.onmessage = function(e) {
64ab5c4d 428 //debug(">> onmessage");
489d1676
JM
429 var data = Base64.decode_array(e.data);
430 //debug("decoded array: " + data);
64ab5c4d
JM
431 if (RFB.state != 'normal') {
432 RFB.init_msg(data);
65e27ddd 433 } else {
64ab5c4d 434 RFB.normal_msg(data);
65e27ddd 435 }
64ab5c4d 436 if (RFB.state == 'reset') {
65e27ddd 437 /* close and reset connection */
532a9fd9
JM
438 RFB.disconnect();
439 RFB.init_ws();
64ab5c4d
JM
440 } else if (RFB.state == 'failed') {
441 debug("Giving up!");
532a9fd9 442 RFB.disconnect();
65e27ddd 443 }
64ab5c4d 444 //debug("<< onmessage");
65e27ddd 445 };
cc0410a3 446 RFB.ws.onopen = function(e) {
65e27ddd 447 debug(">> onopen");
64ab5c4d 448 RFB.state = "ProtocolVersion";
65e27ddd
JM
449 debug("<< onopen");
450 };
cc0410a3 451 RFB.ws.onclose = function(e) {
65e27ddd 452 debug(">> onclose");
64ab5c4d 453 RFB.state = "closed";
65e27ddd
JM
454 debug("<< onclose");
455 }
64ab5c4d
JM
456 RFB.poller.delay(RFB.poll_rate);
457
532a9fd9 458 debug("<< init_ws");
64ab5c4d 459},
65e27ddd 460
532a9fd9
JM
461connect: function () {
462 debug(">> connect");
cc0410a3
JM
463 RFB.host = $('host').value;
464 RFB.port = $('port').value;
465 RFB.password = $('password').value;
532a9fd9
JM
466 if ((!host) || (!port)) {
467 debug("must set host and port");
468 return;
469 }
cc0410a3
JM
470 if (RFB.ws) {
471 RFB.ws.close();
65e27ddd 472 }
532a9fd9
JM
473 RFB.init_ws();
474 $('connectButton').value = "Disconnect";
475 $('connectButton').onclick = RFB.disconnect;
476 debug("<< connect");
477
478},
65e27ddd 479
532a9fd9
JM
480disconnect: function () {
481 debug(">> disconnect");
cc0410a3
JM
482 if (RFB.ws) {
483 RFB.ws.close();
532a9fd9
JM
484 }
485 if (Canvas.ctx) {
486 Canvas.clear();
487 }
488 $('connectButton').value = "Connect";
489 $('connectButton').onclick = RFB.connect;
490 $('status').innerHTML = "Disconnected";
491 debug("<< disconnect");
65e27ddd
JM
492}
493
64ab5c4d 494}; /* End of RFB */