- Many times better performance. Before this, browser was spending all it's time garbage collecting or doing something. Now the bottleneck is in set fillStyle and fillRect which is probably where it should be.
778 lines
24 KiB
JavaScript
778 lines
24 KiB
JavaScript
Array.prototype.shift8 = function () {
|
|
return this.shift();
|
|
}
|
|
Array.prototype.push8 = function (num) {
|
|
this.push(num & 0xFF);
|
|
}
|
|
|
|
Array.prototype.shift16 = function () {
|
|
return (this.shift() << 8) +
|
|
(this.shift() );
|
|
}
|
|
Array.prototype.push16 = function (num) {
|
|
this.push((num >> 8) & 0xFF,
|
|
(num ) & 0xFF );
|
|
}
|
|
|
|
|
|
Array.prototype.shift32 = function () {
|
|
return (this.shift() << 24) +
|
|
(this.shift() << 16) +
|
|
(this.shift() << 8) +
|
|
(this.shift() );
|
|
}
|
|
Array.prototype.push32 = function (num) {
|
|
this.push((num >> 24) & 0xFF,
|
|
(num >> 16) & 0xFF,
|
|
(num >> 8) & 0xFF,
|
|
(num ) & 0xFF );
|
|
}
|
|
|
|
Array.prototype.shiftStr = function (len) {
|
|
var arr = this.splice(0, len);
|
|
return arr.map(function (num) {
|
|
return String.fromCharCode(num); } ).join('');
|
|
}
|
|
Array.prototype.shiftBytes = function (len) {
|
|
return this.splice(0, len);
|
|
}
|
|
|
|
/*
|
|
* Frame buffer update state
|
|
*/
|
|
FBU = {
|
|
rects : 0,
|
|
subrects : 0,
|
|
tiles : 0,
|
|
bytes : 0,
|
|
x : 0,
|
|
y : 0,
|
|
width : 0,
|
|
height : 0,
|
|
encoding : 0,
|
|
subencoding : -1,
|
|
background: null};
|
|
|
|
/*
|
|
* Mouse state
|
|
*/
|
|
Mouse = {
|
|
buttonmask : 0,
|
|
arr : []
|
|
};
|
|
|
|
|
|
/*
|
|
* RFB namespace
|
|
*/
|
|
|
|
RFB = {
|
|
|
|
ws : null, // Web Socket object
|
|
d : [], // Received data accumulator
|
|
|
|
version : "RFB 003.003\n",
|
|
state : 'ProtocolVersion',
|
|
shared : 1,
|
|
push_rate : 1413,
|
|
|
|
host : '',
|
|
port : 5900,
|
|
password : '',
|
|
|
|
fb_width : 0,
|
|
fb_height : 0,
|
|
fb_name : "",
|
|
fb_Bpp : 4,
|
|
rre_chunk : 100,
|
|
|
|
|
|
/*
|
|
* Server message handlers
|
|
*/
|
|
|
|
/* RFB/VNC initialisation */
|
|
init_msg: function () {
|
|
console.log(">> init_msg: " + RFB.state);
|
|
|
|
switch (RFB.state) {
|
|
|
|
case 'ProtocolVersion' :
|
|
if (RFB.d.length != 12) {
|
|
console.log("Invalid protocol version from server");
|
|
RFB.state = 'reset';
|
|
return;
|
|
}
|
|
var server_version = RFB.d.shiftStr(12);
|
|
console.log("Server ProtocolVersion: " + server_version.substr(0,11));
|
|
console.log("Sending ProtocolVersion: " + RFB.version.substr(0,11));
|
|
RFB.send_string(RFB.version);
|
|
RFB.state = 'Authentication';
|
|
break;
|
|
|
|
case 'Authentication' :
|
|
if (RFB.d.length < 4) {
|
|
console.log("Invalid auth frame");
|
|
RFB.state = 'reset';
|
|
return;
|
|
}
|
|
var scheme = RFB.d.shift32();
|
|
console.log("Auth scheme: " + scheme);
|
|
switch (scheme) {
|
|
case 0: // connection failed
|
|
var strlen = RFB.d.shift32();
|
|
var reason = RFB.d.shiftStr(strlen);
|
|
console.log("auth failed: " + reason);
|
|
RFB.state = "failed";
|
|
return;
|
|
case 1: // no authentication
|
|
RFB.send_array([RFB.shared]); // ClientInitialisation
|
|
RFB.state = "ServerInitialisation";
|
|
break;
|
|
case 2: // VNC authentication
|
|
var challenge = RFB.d.shiftBytes(16);
|
|
console.log("Password: " + RFB.password);
|
|
console.log("Challenge: " + challenge + "(" + challenge.length + ")");
|
|
passwd = RFB.passwdTwiddle(RFB.password);
|
|
//console.log("passwd: " + passwd + "(" + passwd.length + ")");
|
|
response = des(passwd, challenge, 1);
|
|
//console.log("reponse: " + response + "(" + response.length + ")");
|
|
|
|
RFB.send_array(response);
|
|
RFB.state = "SecurityResult";
|
|
break;
|
|
default:
|
|
console.log("Unsupported auth scheme");
|
|
RFB.state = "failed";
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case 'SecurityResult' :
|
|
if (RFB.d.length != 4) {
|
|
console.log("Invalid server auth response");
|
|
RFB.state = 'reset';
|
|
return;
|
|
}
|
|
var resp = RFB.d.shift32();
|
|
switch (resp) {
|
|
case 0: // OK
|
|
console.log("Authentication OK");
|
|
break;
|
|
case 1: // failed
|
|
console.log("Authentication failed");
|
|
RFB.state = "reset";
|
|
return;
|
|
case 2: // too-many
|
|
console.log("Too many authentication attempts");
|
|
RFB.state = "failed";
|
|
return;
|
|
}
|
|
RFB.send_array([RFB.shared]); // ClientInitialisation
|
|
RFB.state = "ServerInitialisation";
|
|
break;
|
|
|
|
case 'ServerInitialisation' :
|
|
if (RFB.d.length < 24) {
|
|
console.log("Invalid server initialisation");
|
|
RFB.state = 'reset';
|
|
return;
|
|
}
|
|
|
|
/* Screen size */
|
|
//console.log("RFB.d: " + RFB.d);
|
|
RFB.fb_width = RFB.d.shift16();
|
|
RFB.fb_height = RFB.d.shift16();
|
|
|
|
console.log("Screen size: " + RFB.fb_width + "x" + RFB.fb_height);
|
|
|
|
/* PIXEL_FORMAT */
|
|
var bpp = RFB.d.shift8();
|
|
var depth = RFB.d.shift8();
|
|
var big_endian = RFB.d.shift8();
|
|
var true_color = RFB.d.shift8();
|
|
|
|
console.log("bpp: " + bpp);
|
|
console.log("depth: " + depth);
|
|
console.log("big_endian: " + big_endian);
|
|
console.log("true_color: " + true_color);
|
|
|
|
/* Connection name/title */
|
|
RFB.d.shiftStr(12);
|
|
var name_length = RFB.d.shift32();
|
|
RFB.fb_name = RFB.d.shiftStr(name_length);
|
|
|
|
console.log("Name: " + RFB.fb_name);
|
|
$('status').innerHTML = "Connected to: " + RFB.fb_name;
|
|
|
|
Canvas.init('vnc', RFB.fb_width, RFB.fb_height,
|
|
RFB.keyDown, RFB.keyUp, RFB.mouseDown, RFB.mouseUp);
|
|
|
|
var init = [];
|
|
init = init.concat(RFB.pixelFormat());
|
|
init = init.concat(RFB.encodings());
|
|
init = init.concat(RFB.fbUpdateRequest(0));
|
|
RFB.send_array(init);
|
|
|
|
/* Start pushing/polling */
|
|
RFB.pusher.delay(RFB.push_rate);
|
|
|
|
RFB.state = 'normal';
|
|
break;
|
|
}
|
|
console.log("<< init_msg (" + RFB.state + ")");
|
|
},
|
|
|
|
/* Framebuffer update display functions */
|
|
display_raw: function () {
|
|
console.log(">> display_raw");
|
|
Canvas.rfbImage(FBU.x, FBU.y, FBU.width, FBU.height, RFB.d);
|
|
RFB.d.shiftBytes(FBU.width * FBU.height * RFB.fb_Bpp);
|
|
FBU.rects --;
|
|
},
|
|
|
|
display_copy_rect: function () {
|
|
console.log(">> display_copy_rect");
|
|
var old_x = RFB.d.shift16();
|
|
var old_y = RFB.d.shift16();
|
|
Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
|
|
FBU.rects --;
|
|
},
|
|
|
|
display_rre: function () {
|
|
//console.log(">> display_rre (" + RFB.d.length + " bytes)");
|
|
if (FBU.subrects == 0) {
|
|
FBU.subrects = RFB.d.shift32();
|
|
console.log(">> display_rre " + "(" + FBU.subrects + " subrects)");
|
|
var color = RFB.d.shiftBytes(RFB.fb_Bpp); // Background
|
|
Canvas.rfbRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
|
|
}
|
|
while ((FBU.subrects > 0) && (RFB.d.length >= (RFB.fb_Bpp + 8))) {
|
|
FBU.subrects --;
|
|
var color = RFB.d.shiftBytes(RFB.fb_Bpp);
|
|
var x = RFB.d.shift16();
|
|
var y = RFB.d.shift16();
|
|
var width = RFB.d.shift16();
|
|
var height = RFB.d.shift16();
|
|
Canvas.rfbRect(FBU.x + x, FBU.y + y, width, height, color);
|
|
}
|
|
//console.log(" display_rre: rects: " + FBU.rects + ", FBU.subrects: " + FBU.subrects);
|
|
|
|
if (FBU.subrects > 0) {
|
|
var chunk = Math.min(RFB.rre_chunk, FBU.subrects);
|
|
FBU.bytes = (RFB.fb_Bpp + 8) * chunk;
|
|
} else {
|
|
FBU.rects --;
|
|
}
|
|
//console.log("<< display_rre, FBU.bytes: " + FBU.bytes);
|
|
},
|
|
|
|
display_hextile: function() {
|
|
//console.log(">> display_hextile, tiles: " + FBU.tiles + ", arr.length: " + RFB.d.length + ", bytes: " + FBU.bytes);
|
|
var subencoding, subrects, cur_tile, tile_x, x, w, tile_y, y, h;
|
|
|
|
/* FBU.bytes comes in as 0, RFB.d.length at least 1 */
|
|
while ((FBU.tiles > 0) && (RFB.d.length >= Math.max(1, FBU.bytes))) {
|
|
cur_tile = FBU.total_tiles - FBU.tiles;
|
|
tile_x = cur_tile % FBU.tiles_x;
|
|
tile_y = Math.floor(cur_tile / FBU.tiles_x);
|
|
x = FBU.x + tile_x * 16;
|
|
y = FBU.y + tile_y * 16;
|
|
w = Math.min(16, (FBU.x + FBU.width) - x)
|
|
h = Math.min(16, (FBU.y + FBU.height) - y)
|
|
subrects = 0;
|
|
if (FBU.subencoding == -1) {
|
|
/* We enter with at least 2 bytes */
|
|
subencoding = RFB.d[0]; // Peek
|
|
//console.log(" display_hextile, subencoding: " + subencoding);
|
|
FBU.bytes++; // Since we aren't shifting it off
|
|
//console.log(" subencoding: " + subencoding);
|
|
if (subencoding > 30) { // Raw
|
|
console.log("Illegal subencoding " + subencoding);
|
|
RFB.state = "failed";
|
|
return;
|
|
}
|
|
|
|
/* Figure out how much we are expecting */
|
|
if (subencoding & 0x01) { // Raw
|
|
//console.log(" Raw subencoding");
|
|
FBU.bytes += w * h * RFB.fb_Bpp;
|
|
} else {
|
|
if (subencoding & 0x02) { // Background
|
|
FBU.bytes += RFB.fb_Bpp;
|
|
}
|
|
if (subencoding & 0x04) { // Foreground
|
|
FBU.bytes += RFB.fb_Bpp;
|
|
}
|
|
if (subencoding & 0x08) { // AnySubrects
|
|
FBU.bytes++; // Since we aren't shifting it off
|
|
if (RFB.d.length < FBU.bytes) {
|
|
/* Wait for subrects byte */
|
|
console.log(" waiting for hextile subrects header bytes");
|
|
return;
|
|
}
|
|
subrects = RFB.d[FBU.bytes-1]; // Peek
|
|
if (subencoding & 0x10) { // SubrectsColoured
|
|
FBU.bytes += subrects * (RFB.fb_Bpp + 2);
|
|
} else {
|
|
FBU.bytes += subrects * 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//console.log(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + ", subencoding:" + subencoding + "(last: " + FBU.lastsubencoding + "), subrects:" + subrects + ", tile:" + tile_x + "," + tile_y + " [" + x + "," + y + "]@" + w + "x" + h + ", arr.length:" + RFB.d.length + ", bytes:" + FBU.bytes);
|
|
//console.log(" arr[0..30]: " + RFB.d.slice(0,30));
|
|
if (RFB.d.length < FBU.bytes) {
|
|
//console.log(" waiting for " + (FBU.bytes - RFB.d.length) + " hextile bytes");
|
|
return;
|
|
}
|
|
|
|
if (subencoding > -1) {
|
|
/* We know the encoding and have a whole tile */
|
|
FBU.subencoding = RFB.d.shift8();
|
|
FBU.bytes--;
|
|
if (FBU.subencoding == 0) {
|
|
if (FBU.lastsubencoding & 0x01) {
|
|
/* Weird: ignore blanks after RAW */
|
|
console.log(" Ignoring blank after RAW");
|
|
FBU.subencoding = -1;
|
|
//FBU.lastsubencoding = 0;
|
|
continue;
|
|
} else {
|
|
Canvas.rfbRect(x, y, w, h, FBU.background);
|
|
}
|
|
} else if (FBU.subencoding & 0x01) { // Raw
|
|
Canvas.rfbImage(x, y, w, h, RFB.d);
|
|
} else {
|
|
var idx = 0;
|
|
if (FBU.subencoding & 0x02) { // Background
|
|
FBU.background = RFB.d.slice(idx, idx + RFB.fb_Bpp);
|
|
idx += RFB.fb_Bpp;
|
|
//console.log(" background: " + FBU.background);
|
|
}
|
|
if (FBU.subencoding & 0x04) { // Foreground
|
|
FBU.foreground = RFB.d.slice(idx, idx + RFB.fb_Bpp);
|
|
idx += RFB.fb_Bpp;
|
|
//console.log(" foreground: " + FBU.foreground);
|
|
}
|
|
Canvas.rfbRect(x, y, w, h, FBU.background);
|
|
if (FBU.subencoding & 0x08) { // AnySubrects
|
|
subrects = RFB.d[idx];
|
|
idx++;
|
|
var color, xy, sx, sy, wh, sw, sh;
|
|
for (var i = 0; i < subrects; i ++) {
|
|
if (FBU.subencoding & 0x10) { // SubrectsColoured
|
|
color = RFB.d.slice(idx, idx + RFB.fb_Bpp);
|
|
idx += RFB.fb_Bpp;
|
|
} else {
|
|
color = FBU.foreground;
|
|
}
|
|
xy = RFB.d[idx];
|
|
idx++;
|
|
sx = x + (xy >> 4);
|
|
sy = y + (xy & 0x0f);
|
|
|
|
wh = RFB.d[idx];
|
|
idx++;
|
|
sw = (wh >> 4) + 1;
|
|
sh = (wh & 0x0f) + 1;
|
|
|
|
Canvas.rfbRect(sx, sy, sw, sh, color);
|
|
}
|
|
}
|
|
}
|
|
if (FBU.bytes > 0) RFB.d.shiftBytes(FBU.bytes);
|
|
FBU.lastsubencoding = FBU.subencoding;
|
|
FBU.subencoding = -1;
|
|
FBU.tiles --;
|
|
FBU.bytes = 0;
|
|
}
|
|
}
|
|
|
|
if (FBU.tiles > 0) {
|
|
FBU.bytes = 2;
|
|
} else {
|
|
FBU.background = [255, 255, 0, 0]; // Yellow: invalid
|
|
FBU.rects --;
|
|
}
|
|
|
|
//console.log("<< display_hextile, rects:" + FBU.rects, " d:" + RFB.d.slice(0,40));
|
|
},
|
|
|
|
|
|
/* Normal RFB/VNC messages */
|
|
normal_msg: function () {
|
|
//console.log(">> normal_msg");
|
|
if ((FBU.rects > 0) || (FBU.bytes > 0)) {
|
|
var msg_type = 0;
|
|
} else {
|
|
var msg_type = RFB.d.shift8();
|
|
}
|
|
switch (msg_type) {
|
|
case 0: // FramebufferUpdate
|
|
if (FBU.rects == 0) {
|
|
if (RFB.d.length < 3) {
|
|
console.log(" waiting for FBU header bytes");
|
|
return;
|
|
}
|
|
RFB.d.shift8();
|
|
FBU.rects = RFB.d.shift16();
|
|
//console.log("FramebufferUpdate, rects:" + FBU.rects);
|
|
FBU.bytes = 0;
|
|
}
|
|
|
|
while ((FBU.rects > 0) && (RFB.d.length > 0)) {
|
|
if (FBU.bytes == 0) {
|
|
if (RFB.d.length < 12) {
|
|
console.log(" waiting for rect header bytes");
|
|
return;
|
|
}
|
|
/* New FramebufferUpdate */
|
|
FBU.x = RFB.d.shift16();
|
|
FBU.y = RFB.d.shift16();
|
|
FBU.width = RFB.d.shift16();
|
|
FBU.height = RFB.d.shift16();
|
|
FBU.encoding = parseInt(RFB.d.shift32(), 10);
|
|
//var msg = "FramebufferUpdate rects:" + FBU.rects + " encoding:" + FBU.encoding
|
|
switch (FBU.encoding) {
|
|
case 0: // Raw
|
|
FBU.bytes = FBU.width * FBU.height * RFB.fb_Bpp;
|
|
//msg += "(RAW)"
|
|
break;
|
|
case 1: // Copy-Rect
|
|
FBU.bytes = 4;
|
|
//msg += "(COPY-RECT)"
|
|
break;
|
|
case 2: // RRE
|
|
FBU.bytes = 4 + RFB.fb_Bpp;
|
|
//msg += "(RRE)"
|
|
break;
|
|
case 5: // hextile
|
|
FBU.bytes = 2; // No header; get it started
|
|
FBU.tiles_x = Math.ceil(FBU.width/16);
|
|
FBU.tiles_y = Math.ceil(FBU.height/16);
|
|
FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
|
|
FBU.tiles = FBU.total_tiles;
|
|
//msg += "(HEXTILE " + FBU.tiles + " tiles)"
|
|
break;
|
|
default:
|
|
console.log("Unsupported encoding " + FBU.encoding);
|
|
RFB.state = "failed";
|
|
return;
|
|
}
|
|
//msg += ", RFB.d.length: " + RFB.d.length + ", FBU.bytes: " + FBU.bytes
|
|
//console.log(msg);
|
|
}
|
|
|
|
if (RFB.d.length >= FBU.bytes) {
|
|
FBU.bytes = 0;
|
|
|
|
switch (FBU.encoding) {
|
|
case 0: RFB.display_raw(); break; // Raw
|
|
case 1: RFB.display_copy_rect(); break; // Copy-Rect
|
|
case 2: RFB.display_rre(); break; // RRE
|
|
case 5: RFB.display_hextile(); break; // hextile
|
|
}
|
|
} else {
|
|
/* We don't have enough yet */
|
|
FBU.bytes = FBU.bytes - RFB.d.length;
|
|
break;
|
|
}
|
|
if (RFB.state != "normal") return;
|
|
}
|
|
|
|
//console.log("Finished frame buffer update");
|
|
break;
|
|
case 1: // SetColourMapEntries
|
|
console.log("SetColourMapEntries (unsupported)");
|
|
RFB.d.shift8(); // Padding
|
|
RFB.d.shift16(); // First colour
|
|
var num_colours = RFB.d.shift16();
|
|
RFB.d.shiftBytes(num_colours * 6);
|
|
break;
|
|
case 2: // Bell
|
|
console.log("Bell (unsupported)");
|
|
break;
|
|
case 3: // ServerCutText
|
|
console.log("ServerCutText");
|
|
RFB.d.shiftBytes(3); // Padding
|
|
var length = RFB.d.shift32();
|
|
RFB.d.shiftBytes(length);
|
|
break;
|
|
default:
|
|
console.log("Unknown server message type: " + msg_type);
|
|
RFB.state = "failed";
|
|
break;
|
|
}
|
|
//console.log("<< normal_msg");
|
|
},
|
|
|
|
/*
|
|
* Client message routines
|
|
*/
|
|
|
|
pixelFormat: function () {
|
|
console.log(">> setPixelFormat");
|
|
var arr;
|
|
arr = [0]; // msg-type
|
|
arr.push8(0); // padding
|
|
arr.push8(0); // padding
|
|
arr.push8(0); // padding
|
|
|
|
arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
|
|
arr.push8(24); // depth
|
|
arr.push8(0); // little-endian
|
|
arr.push8(1); // true-color
|
|
|
|
arr.push16(255); // red-max
|
|
arr.push16(255); // green-max
|
|
arr.push16(255); // blue-max
|
|
arr.push8(0); // red-shift
|
|
arr.push8(8); // green-shift
|
|
arr.push8(16); // blue-shift
|
|
|
|
arr.push8(0); // padding
|
|
arr.push8(0); // padding
|
|
arr.push8(0); // padding
|
|
console.log("<< setPixelFormat");
|
|
return arr;
|
|
},
|
|
|
|
fixColourMapEntries: function () {
|
|
},
|
|
|
|
encodings: function () {
|
|
console.log(">> setEncodings");
|
|
var arr;
|
|
arr = [2]; // msg-type
|
|
arr.push8(0); // padding
|
|
|
|
//arr.push16(3); // encoding count
|
|
arr.push16(4); // encoding count
|
|
arr.push32(5); // hextile encoding
|
|
|
|
arr.push32(2); // RRE encoding
|
|
arr.push32(1); // copy-rect encoding
|
|
arr.push32(0); // raw encoding
|
|
console.log("<< setEncodings");
|
|
return arr;
|
|
},
|
|
|
|
fbUpdateRequest: function (incremental, x, y, xw, yw) {
|
|
//console.log(">> fbUpdateRequest");
|
|
if (!x) x = 0;
|
|
if (!y) y = 0;
|
|
if (!xw) xw = RFB.fb_width;
|
|
if (!yw) yw = RFB.fb_height;
|
|
var arr;
|
|
arr = [3]; // msg-type
|
|
arr.push8(incremental);
|
|
arr.push16(x);
|
|
arr.push16(y);
|
|
arr.push16(xw);
|
|
arr.push16(yw);
|
|
//console.log("<< fbUpdateRequest");
|
|
return arr;
|
|
},
|
|
|
|
keyEvent: function (keysym, down) {
|
|
//console.log(">> keyEvent, keysym: " + keysym + ", down: " + down);
|
|
var arr;
|
|
arr = [4]; // msg-type
|
|
arr.push8(down);
|
|
arr.push16(0);
|
|
arr.push32(keysym);
|
|
//console.log("keyEvent array: " + arr);
|
|
//console.log("<< keyEvent");
|
|
return arr;
|
|
},
|
|
|
|
pointerEvent: function (x, y) {
|
|
//console.log(">> pointerEvent, x,y: " + x + "," + y + " , mask: " + Mouse.buttonMask);
|
|
var arr;
|
|
arr = [5]; // msg-type
|
|
arr.push8(Mouse.buttonMask);
|
|
arr.push16(x);
|
|
arr.push16(y);
|
|
//console.log("<< pointerEvent");
|
|
return arr;
|
|
},
|
|
|
|
clientCutText: function () {
|
|
},
|
|
|
|
|
|
/*
|
|
* Utility routines
|
|
*/
|
|
|
|
send_string: function (str) {
|
|
//console.log(">> send_string: " + str);
|
|
RFB.send_array(str.split('').map(
|
|
function (chr) { return chr.charCodeAt(0) } ) );
|
|
},
|
|
|
|
send_array: function (arr) {
|
|
//console.log(">> send_array: " + arr);
|
|
//console.log(">> send_array: " + Base64.encode_array(arr));
|
|
RFB.ws.send(Base64.encode_array(arr));
|
|
},
|
|
|
|
/* Mirror bits of each character and return as array */
|
|
passwdTwiddle: function (passwd) {
|
|
var arr;
|
|
arr = [];
|
|
for (var i=0; i< passwd.length; i++) {
|
|
var c = passwd.charCodeAt(i);
|
|
arr.push( ((c & 0x80) >> 7) +
|
|
((c & 0x40) >> 5) +
|
|
((c & 0x20) >> 3) +
|
|
((c & 0x10) >> 1) +
|
|
((c & 0x08) << 1) +
|
|
((c & 0x04) << 3) +
|
|
((c & 0x02) << 5) +
|
|
((c & 0x01) << 7) );
|
|
}
|
|
return arr;
|
|
},
|
|
|
|
flushClient: function () {
|
|
var arr = [];
|
|
if (Mouse.arr.length > 0) {
|
|
RFB.send_array(Mouse.arr.concat(RFB.fbUpdateRequest(1)));
|
|
Mouse.arr = [];
|
|
} else {
|
|
RFB.send_array(RFB.fbUpdateRequest(1));
|
|
}
|
|
},
|
|
|
|
pusher: function () {
|
|
if (RFB.state == 'normal') {
|
|
RFB.flushClient();
|
|
RFB.pusher.delay(RFB.push_rate);
|
|
}
|
|
},
|
|
|
|
keyDown: function (e) {
|
|
console.log(">> keyDown: " + Canvas.getKeysym(e));
|
|
e.stop();
|
|
var arr = RFB.keyEvent(Canvas.getKeysym(e), 1);
|
|
arr = arr.concat(RFB.fbUpdateRequest(1));
|
|
RFB.send_array(arr);
|
|
},
|
|
|
|
keyUp: function (e) {
|
|
console.log(">> keyUp: " + Canvas.getKeysym(e));
|
|
e.stop();
|
|
var arr = RFB.keyEvent(Canvas.getKeysym(e), 0);
|
|
arr = arr.concat(RFB.fbUpdateRequest(1));
|
|
RFB.send_array(arr);
|
|
},
|
|
|
|
mouseDown: function(e) {
|
|
var evt = e.event || window.event;
|
|
var x, y;
|
|
x = (evt.clientX - Canvas.c_x);
|
|
y = (evt.clientY - Canvas.c_y);
|
|
console.log('>> mouseDown ' + evt.which + '/' + evt.button + " " + x + "," + y);
|
|
Mouse.buttonMask |= 1 << evt.button;
|
|
Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
|
|
|
|
RFB.flushClient();
|
|
},
|
|
|
|
mouseUp: function(e) {
|
|
var evt = e.event || window.event;
|
|
var x, y;
|
|
x = (evt.clientX - Canvas.c_x);
|
|
y = (evt.clientY - Canvas.c_y);
|
|
console.log('>> mouseUp ' + evt.which + '/' + evt.button + " " + x + "," + y);
|
|
Mouse.buttonMask ^= 1 << evt.button;
|
|
Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
|
|
|
|
RFB.flushClient();
|
|
},
|
|
|
|
mouseMove: function(e) {
|
|
// TODO: accumulate in global array
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
* Setup routines
|
|
*/
|
|
|
|
init_ws: function () {
|
|
console.log(">> init_ws");
|
|
var uri = "ws://" + RFB.host + ":" + RFB.port;
|
|
console.log("connecting to " + uri);
|
|
RFB.ws = new WebSocket(uri);
|
|
RFB.ws.onmessage = function(e) {
|
|
//console.log(">> onmessage");
|
|
RFB.d = RFB.d.concat(Base64.decode_array(e.data));
|
|
if (RFB.state != 'normal') {
|
|
RFB.init_msg();
|
|
} else {
|
|
RFB.normal_msg();
|
|
}
|
|
if (RFB.state == 'reset') {
|
|
/* close and reset connection */
|
|
RFB.disconnect();
|
|
RFB.init_ws();
|
|
} else if (RFB.state == 'failed') {
|
|
console.log("Giving up!");
|
|
RFB.disconnect();
|
|
}
|
|
//console.log("<< onmessage");
|
|
};
|
|
RFB.ws.onopen = function(e) {
|
|
console.log(">> onopen");
|
|
RFB.state = "ProtocolVersion";
|
|
console.log("<< onopen");
|
|
};
|
|
RFB.ws.onclose = function(e) {
|
|
console.log(">> onclose");
|
|
RFB.state = "closed";
|
|
console.log("<< onclose");
|
|
}
|
|
|
|
console.log("<< init_ws");
|
|
},
|
|
|
|
connect: function () {
|
|
console.log(">> connect");
|
|
RFB.host = $('host').value;
|
|
RFB.port = $('port').value;
|
|
RFB.password = $('password').value;
|
|
if ((!host) || (!port)) {
|
|
console.log("must set host and port");
|
|
return;
|
|
}
|
|
if (RFB.ws) {
|
|
RFB.ws.close();
|
|
}
|
|
RFB.init_ws();
|
|
$('connectButton').value = "Disconnect";
|
|
$('connectButton').onclick = RFB.disconnect;
|
|
console.log("<< connect");
|
|
|
|
},
|
|
|
|
disconnect: function () {
|
|
console.log(">> disconnect");
|
|
if (RFB.ws) {
|
|
RFB.ws.close();
|
|
}
|
|
if (Canvas.ctx) {
|
|
Canvas.clear();
|
|
}
|
|
$('connectButton').value = "Connect";
|
|
$('connectButton').onclick = RFB.connect;
|
|
$('status').innerHTML = "Disconnected";
|
|
console.log("<< disconnect");
|
|
}
|
|
|
|
}; /* End of RFB */
|