Commit 67b4e987 authored by Joel Martin's avatar Joel Martin

Indexed receive queue. Up to 2X speedup in Chrome.

Generally, most servers send hextile updates as single updates
containing many rects. Some servers send hextile updates as many small
framebuffer updates with a few rects each (such as QEMU). This latter
cases revealed that shifting off the beginning of the receive queue
(which happens after each hextile FBU) performs poorly.

This change switches to using an indexed receive queue (instead of
actually shifting off the array). When the receive queue has grown to
a certain size, then it is compacted all at once.

The code is not as clean, but this change results in more than 2X
speedup under Chrome for the pessimal case and 10-20% in firefox.
parent fb007628
...@@ -654,6 +654,18 @@ that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { ...@@ -654,6 +654,18 @@ that.changeCursor = function(pixels, mask, hotx, hoty, w, h) {
return; return;
} }
// Push multi-byte little-endian values
cur.push16le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF );
};
cur.push32le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF );
};
cmap = conf.colourMap; cmap = conf.colourMap;
IHDRsz = 40; IHDRsz = 40;
ANDsz = w * h * 4; ANDsz = w * h * 4;
......
...@@ -69,6 +69,7 @@ var that = {}, // Public API interface ...@@ -69,6 +69,7 @@ var that = {}, // Public API interface
// Receive and send queues // Receive and send queues
RQ = [], // Receive Queue RQ = [], // Receive Queue
RQi = 0, // Receive Queue Index
SQ = "", // Send Queue SQ = "", // Send Queue
// Frame buffer update state // Frame buffer update state
...@@ -100,6 +101,7 @@ var that = {}, // Public API interface ...@@ -100,6 +101,7 @@ var that = {}, // Public API interface
scan_imgs_rate = 100, scan_imgs_rate = 100,
last_req_time = 0, last_req_time = 0,
rre_chunk_sz = 100, rre_chunk_sz = 100,
maxRQlen = 100000,
timing = { timing = {
last_fbu : 0, last_fbu : 0,
...@@ -155,7 +157,7 @@ Util.conf_default(conf, that, 'updateState', function () { ...@@ -155,7 +157,7 @@ Util.conf_default(conf, that, 'updateState', function () {
Util.Debug(">> externalUpdateState stub"); }); Util.Debug(">> externalUpdateState stub"); });
// clipboard contents received callback // clipboard contents received callback
Util.conf_default(conf, that, 'clipboardReceive', function () { Util.conf_default(conf, that, 'clipboardReceive', function () {
Util.Debug(">> clipboardReceive stub"); }); Util.Debug(">> clipboardReceive stub"); });
// Override/add some specific getters/setters // Override/add some specific getters/setters
...@@ -182,6 +184,35 @@ that.get_canvas = function() { ...@@ -182,6 +184,35 @@ that.get_canvas = function() {
// Private functions // Private functions
// //
//
// Receive Queue functions
//
RQlen = function() {
return RQ.length - RQi;
}
RQshift16 = function() {
return (RQ[RQi++] << 8) +
(RQ[RQi++] );
}
RQshift32 = function() {
return (RQ[RQi++] << 24) +
(RQ[RQi++] << 16) +
(RQ[RQi++] << 8) +
(RQ[RQi++] );
}
RQshiftStr = function(len) {
var arr = RQ.slice(RQi, RQi + len);
RQi += len;
return arr.map(function (num) {
return String.fromCharCode(num); } ).join('');
}
RQshiftBytes = function(len) {
RQi += len;
return RQ.slice(RQi-len, RQi);
}
// //
// Setup routines // Setup routines
// //
...@@ -189,7 +220,7 @@ that.get_canvas = function() { ...@@ -189,7 +220,7 @@ that.get_canvas = function() {
// Create the public API interface // Create the public API interface
function constructor() { function constructor() {
var i; var i;
//Util.Debug(">> init"); Util.Debug(">> RFB.constructor");
// Create lookup tables based encoding number // Create lookup tables based encoding number
for (i=0; i < encodings.length; i+=1) { for (i=0; i < encodings.length; i+=1) {
...@@ -205,12 +236,12 @@ function constructor() { ...@@ -205,12 +236,12 @@ function constructor() {
updateState('fatal', "No working Canvas"); updateState('fatal', "No working Canvas");
} }
//Util.Debug("<< init"); Util.Debug("<< RFB.constructor");
return that; // Return the public API interface return that; // Return the public API interface
} }
function init_ws() { function init_ws() {
//Util.Debug(">> init_ws"); Util.Debug(">> RFB.init_ws");
var uri = "", vars = []; var uri = "", vars = [];
if (conf.encrypt) { if (conf.encrypt) {
...@@ -261,7 +292,7 @@ function init_ws() { ...@@ -261,7 +292,7 @@ function init_ws() {
} }
}, conf.connectTimeout); }, conf.connectTimeout);
//Util.Debug("<< init_ws"); Util.Debug("<< RFB.init_ws");
} }
init_vars = function() { init_vars = function() {
...@@ -269,6 +300,7 @@ init_vars = function() { ...@@ -269,6 +300,7 @@ init_vars = function() {
cuttext = 'none'; cuttext = 'none';
cuttext_length = 0; cuttext_length = 0;
RQ = []; RQ = [];
RQi = 0;
SQ = ""; SQ = "";
FBU.rects = 0; FBU.rects = 0;
FBU.subrects = 0; // RRE and HEXTILE FBU.subrects = 0; // RRE and HEXTILE
...@@ -456,8 +488,8 @@ function decode_message(data) { ...@@ -456,8 +488,8 @@ function decode_message(data) {
} }
function handle_message() { function handle_message() {
//Util.Debug("RQ.slice(0,20): " + RQ.slice(0,20) + " (" + RQ.length + ")"); //Util.Debug("RQ.slice(RQi,RQi+20): " + RQ.slice(RQi,RQi+20) + " (" + RQlen() + ")");
if (RQ.length === 0) { if (RQlen() === 0) {
Util.Warn("handle_message called on empty receive queue"); Util.Warn("handle_message called on empty receive queue");
return; return;
} }
...@@ -470,7 +502,7 @@ function handle_message() { ...@@ -470,7 +502,7 @@ function handle_message() {
that.disconnect(); that.disconnect();
break; break;
case 'normal': case 'normal':
if (normal_msg() && RQ.length > 0) { if (normal_msg() && RQlen() > 0) {
// true means we can continue processing // true means we can continue processing
// Give other events a chance to run // Give other events a chance to run
if (msgTimer === null) { if (msgTimer === null) {
...@@ -483,6 +515,12 @@ function handle_message() { ...@@ -483,6 +515,12 @@ function handle_message() {
Util.Debug("More data to process, existing timer"); Util.Debug("More data to process, existing timer");
} }
} }
// Compact the queue
if (RQ.length > maxRQlen) {
//Util.Debug("Compacting receive queue");
RQ = RQ.slice(RQi);
RQi = 0;
}
break; break;
default: default:
init_msg(); init_msg();
...@@ -495,7 +533,7 @@ recv_message = function(e) { ...@@ -495,7 +533,7 @@ recv_message = function(e) {
try { try {
decode_message(e.data); decode_message(e.data);
if (RQ.length > 0) { if (RQlen() > 0) {
handle_message(); handle_message();
} else { } else {
Util.Debug("Ignoring empty message"); Util.Debug("Ignoring empty message");
...@@ -669,16 +707,16 @@ init_msg = function() { ...@@ -669,16 +707,16 @@ init_msg = function() {
i, types, num_types, challenge, response, bpp, depth, i, types, num_types, challenge, response, bpp, depth,
big_endian, true_color, name_length; big_endian, true_color, name_length;
//Util.Debug("RQ (" + RQ.length + ") " + RQ); //Util.Debug("RQ (" + RQlen() + ") " + RQ);
switch (rfb_state) { switch (rfb_state) {
case 'ProtocolVersion' : case 'ProtocolVersion' :
if (RQ.length < 12) { if (RQlen() < 12) {
updateState('failed', updateState('failed',
"Disconnected: incomplete protocol version"); "Disconnected: incomplete protocol version");
return; return;
} }
sversion = RQ.shiftStr(12).substr(4,7); sversion = RQshiftStr(12).substr(4,7);
Util.Info("Server ProtocolVersion: " + sversion); Util.Info("Server ProtocolVersion: " + sversion);
switch (sversion) { switch (sversion) {
case "003.003": rfb_version = 3.3; break; case "003.003": rfb_version = 3.3; break;
...@@ -718,16 +756,16 @@ init_msg = function() { ...@@ -718,16 +756,16 @@ init_msg = function() {
case 'Security' : case 'Security' :
if (rfb_version >= 3.7) { if (rfb_version >= 3.7) {
num_types = RQ.shift8(); num_types = RQ[RQi++];
if (num_types === 0) { if (num_types === 0) {
strlen = RQ.shift32(); strlen = RQshift32();
reason = RQ.shiftStr(strlen); reason = RQshiftStr(strlen);
updateState('failed', updateState('failed',
"Disconnected: security failure: " + reason); "Disconnected: security failure: " + reason);
return; return;
} }
rfb_auth_scheme = 0; rfb_auth_scheme = 0;
types = RQ.shiftBytes(num_types); types = RQshiftBytes(num_types);
Util.Debug("Server security types: " + types); Util.Debug("Server security types: " + types);
for (i=0; i < types.length; i+=1) { for (i=0; i < types.length; i+=1) {
if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) { if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {
...@@ -742,11 +780,11 @@ init_msg = function() { ...@@ -742,11 +780,11 @@ init_msg = function() {
send_array([rfb_auth_scheme]); send_array([rfb_auth_scheme]);
} else { } else {
if (RQ.length < 4) { if (RQlen() < 4) {
updateState('failed', "Invalid security frame"); updateState('failed', "Invalid security frame");
return; return;
} }
rfb_auth_scheme = RQ.shift32(); rfb_auth_scheme = RQshift32();
} }
updateState('Authentication', updateState('Authentication',
"Authenticating using scheme: " + rfb_auth_scheme); "Authenticating using scheme: " + rfb_auth_scheme);
...@@ -757,12 +795,12 @@ init_msg = function() { ...@@ -757,12 +795,12 @@ init_msg = function() {
//Util.Debug("Security auth scheme: " + rfb_auth_scheme); //Util.Debug("Security auth scheme: " + rfb_auth_scheme);
switch (rfb_auth_scheme) { switch (rfb_auth_scheme) {
case 0: // connection failed case 0: // connection failed
if (RQ.length < 4) { if (RQlen() < 4) {
//Util.Debug(" waiting for auth reason bytes"); //Util.Debug(" waiting for auth reason bytes");
return; return;
} }
strlen = RQ.shift32(); strlen = RQshift32();
reason = RQ.shiftStr(strlen); reason = RQshiftStr(strlen);
updateState('failed', updateState('failed',
"Disconnected: auth failure: " + reason); "Disconnected: auth failure: " + reason);
return; return;
...@@ -774,11 +812,11 @@ init_msg = function() { ...@@ -774,11 +812,11 @@ init_msg = function() {
updateState('password', "Password Required"); updateState('password', "Password Required");
return; return;
} }
if (RQ.length < 16) { if (RQlen() < 16) {
//Util.Debug(" waiting for auth challenge bytes"); //Util.Debug(" waiting for auth challenge bytes");
return; return;
} }
challenge = RQ.shiftBytes(16); challenge = RQshiftBytes(16);
//Util.Debug("Password: " + rfb_password); //Util.Debug("Password: " + rfb_password);
//Util.Debug("Challenge: " + challenge + //Util.Debug("Challenge: " + challenge +
// " (" + challenge.length + ")"); // " (" + challenge.length + ")");
...@@ -799,18 +837,18 @@ init_msg = function() { ...@@ -799,18 +837,18 @@ init_msg = function() {
break; break;
case 'SecurityResult' : case 'SecurityResult' :
if (RQ.length < 4) { if (RQlen() < 4) {
updateState('failed', "Invalid VNC auth response"); updateState('failed', "Invalid VNC auth response");
return; return;
} }
switch (RQ.shift32()) { switch (RQshift32()) {
case 0: // OK case 0: // OK
updateState('ServerInitialisation', "Authentication OK"); updateState('ServerInitialisation', "Authentication OK");
break; break;
case 1: // failed case 1: // failed
if (rfb_version >= 3.8) { if (rfb_version >= 3.8) {
reason_len = RQ.shift32(); reason_len = RQshift32();
reason = RQ.shiftStr(reason_len); reason = RQshiftStr(reason_len);
updateState('failed', reason); updateState('failed', reason);
} else { } else {
updateState('failed', "Authentication failed"); updateState('failed', "Authentication failed");
...@@ -825,20 +863,20 @@ init_msg = function() { ...@@ -825,20 +863,20 @@ init_msg = function() {
break; break;
case 'ServerInitialisation' : case 'ServerInitialisation' :
if (RQ.length < 24) { if (RQlen() < 24) {
updateState('failed', "Invalid server initialisation"); updateState('failed', "Invalid server initialisation");
return; return;
} }
/* Screen size */ /* Screen size */
fb_width = RQ.shift16(); fb_width = RQshift16();
fb_height = RQ.shift16(); fb_height = RQshift16();
/* PIXEL_FORMAT */ /* PIXEL_FORMAT */
bpp = RQ.shift8(); bpp = RQ[RQi++];
depth = RQ.shift8(); depth = RQ[RQi++];
big_endian = RQ.shift8(); big_endian = RQ[RQi++];
true_color = RQ.shift8(); true_color = RQ[RQi++];
Util.Info("Screen: " + fb_width + "x" + fb_height + Util.Info("Screen: " + fb_width + "x" + fb_height +
", bpp: " + bpp + ", depth: " + depth + ", bpp: " + bpp + ", depth: " + depth +
...@@ -846,9 +884,9 @@ init_msg = function() { ...@@ -846,9 +884,9 @@ init_msg = function() {
", true_color: " + true_color); ", true_color: " + true_color);
/* Connection name/title */ /* Connection name/title */
RQ.shiftStr(12); RQshiftStr(12);
name_length = RQ.shift32(); name_length = RQshift32();
fb_name = RQ.shiftStr(name_length); fb_name = RQshiftStr(name_length);
canvas.resize(fb_width, fb_height, conf.true_color); canvas.resize(fb_width, fb_height, conf.true_color);
canvas.start(keyPress, mouseButton, mouseMove); canvas.start(keyPress, mouseButton, mouseMove);
...@@ -891,14 +929,12 @@ normal_msg = function() { ...@@ -891,14 +929,12 @@ normal_msg = function() {
var ret = true, msg_type, var ret = true, msg_type,
c, first_colour, num_colours, red, green, blue; c, first_colour, num_colours, red, green, blue;
//Util.Debug(">> msg RQ.slice(0,10): " + RQ.slice(0,20));
//Util.Debug(">> msg RQ.slice(-10,-1): " + RQ.slice(RQ.length-10,RQ.length));
if (FBU.rects > 0) { if (FBU.rects > 0) {
msg_type = 0; msg_type = 0;
} else if (cuttext !== 'none') { } else if (cuttext !== 'none') {
msg_type = 3; msg_type = 3;
} else { } else {
msg_type = RQ.shift8(); msg_type = RQ[RQi++];
} }
switch (msg_type) { switch (msg_type) {
case 0: // FramebufferUpdate case 0: // FramebufferUpdate
...@@ -906,16 +942,16 @@ normal_msg = function() { ...@@ -906,16 +942,16 @@ normal_msg = function() {
break; break;
case 1: // SetColourMapEntries case 1: // SetColourMapEntries
Util.Debug("SetColourMapEntries"); Util.Debug("SetColourMapEntries");
RQ.shift8(); // Padding RQ[RQi++]; // Padding
first_colour = RQ.shift16(); // First colour first_colour = RQshift16(); // First colour
num_colours = RQ.shift16(); num_colours = RQshift16();
for (c=0; c < num_colours; c+=1) { for (c=0; c < num_colours; c+=1) {
red = RQ.shift16(); red = RQshift16();
//Util.Debug("red before: " + red); //Util.Debug("red before: " + red);
red = parseInt(red / 256, 10); red = parseInt(red / 256, 10);
//Util.Debug("red after: " + red); //Util.Debug("red after: " + red);
green = parseInt(RQ.shift16() / 256, 10); green = parseInt(RQshift16() / 256, 10);
blue = parseInt(RQ.shift16() / 256, 10); blue = parseInt(RQshift16() / 256, 10);
canvas.set_colourMap([red, green, blue], first_colour + c); canvas.set_colourMap([red, green, blue], first_colour + c);
} }
Util.Info("Registered " + num_colours + " colourMap entries"); Util.Info("Registered " + num_colours + " colourMap entries");
...@@ -931,19 +967,19 @@ normal_msg = function() { ...@@ -931,19 +967,19 @@ normal_msg = function() {
cuttext = 'header'; cuttext = 'header';
} }
if (cuttext === 'header') { if (cuttext === 'header') {
if (RQ.length < 7) { if (RQlen() < 7) {
//Util.Debug("waiting for ServerCutText header"); //Util.Debug("waiting for ServerCutText header");
return false; return false;
} }
RQ.shiftBytes(3); // Padding RQshiftBytes(3); // Padding
cuttext_length = RQ.shift32(); cuttext_length = RQshift32();
} }
cuttext = 'bytes'; cuttext = 'bytes';
if (RQ.length < cuttext_length) { if (RQlen() < cuttext_length) {
//Util.Debug("waiting for ServerCutText bytes"); //Util.Debug("waiting for ServerCutText bytes");
return false; return false;
} }
conf.clipboardReceive(that, RQ.shiftStr(cuttext_length)); conf.clipboardReceive(that, RQshiftStr(cuttext_length));
cuttext = 'none'; cuttext = 'none';
break; break;
default: default:
...@@ -961,13 +997,17 @@ framebufferUpdate = function() { ...@@ -961,13 +997,17 @@ framebufferUpdate = function() {
if (FBU.rects === 0) { if (FBU.rects === 0) {
//Util.Debug("New FBU: RQ.slice(0,20): " + RQ.slice(0,20)); //Util.Debug("New FBU: RQ.slice(0,20): " + RQ.slice(0,20));
if (RQ.length < 3) { if (RQlen() < 3) {
RQ.unshift(0); // FBU msg_type if (RQi === 0) {
Util.Debug(" waiting for FBU header bytes"); RQ.unshift(0); // FBU msg_type
} else {
RQi -= 1;
}
//Util.Debug(" waiting for FBU header bytes");
return false; return false;
} }
RQ.shift8(); RQ[RQi++];
FBU.rects = RQ.shift16(); FBU.rects = RQshift16();
//Util.Debug("FramebufferUpdate, rects:" + FBU.rects); //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);
FBU.bytes = 0; FBU.bytes = 0;
timing.cur_fbu = 0; timing.cur_fbu = 0;
...@@ -982,17 +1022,18 @@ framebufferUpdate = function() { ...@@ -982,17 +1022,18 @@ framebufferUpdate = function() {
if (rfb_state !== "normal") { if (rfb_state !== "normal") {
return false; return false;
} }
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
//Util.Debug(" waiting for " + (FBU.bytes - RQlen()) + " FBU bytes");
return false; return false;
} }
if (FBU.bytes === 0) { if (FBU.bytes === 0) {
if (RQ.length < 12) { if (RQlen() < 12) {
//Util.Debug(" waiting for rect header bytes"); //Util.Debug(" waiting for rect header bytes");
return false; return false;
} }
/* New FramebufferUpdate */ /* New FramebufferUpdate */
hdr = RQ.shiftBytes(12); hdr = RQshiftBytes(12);
FBU.x = (hdr[0] << 8) + hdr[1]; FBU.x = (hdr[0] << 8) + hdr[1];
FBU.y = (hdr[2] << 8) + hdr[3]; FBU.y = (hdr[2] << 8) + hdr[3];
FBU.width = (hdr[4] << 8) + hdr[5]; FBU.width = (hdr[4] << 8) + hdr[5];
...@@ -1009,7 +1050,7 @@ framebufferUpdate = function() { ...@@ -1009,7 +1050,7 @@ framebufferUpdate = function() {
msg += " width: " + FBU.width + " height: " + FBU.height; msg += " width: " + FBU.width + " height: " + FBU.height;
msg += " encoding:" + FBU.encoding; msg += " encoding:" + FBU.encoding;
msg += "(" + encNames[FBU.encoding] + ")"; msg += "(" + encNames[FBU.encoding] + ")";
msg += ", RQ.length: " + RQ.length; msg += ", RQlen(): " + RQlen();
Util.Debug(msg); Util.Debug(msg);
*/ */
} else { } else {
...@@ -1021,7 +1062,7 @@ framebufferUpdate = function() { ...@@ -1021,7 +1062,7 @@ framebufferUpdate = function() {
} }
timing.last_fbu = (new Date()).getTime(); timing.last_fbu = (new Date()).getTime();
last_bytes = RQ.length; last_bytes = RQlen();
last_rects = FBU.rects; last_rects = FBU.rects;
// false ret means need more data // false ret means need more data
...@@ -1029,7 +1070,7 @@ framebufferUpdate = function() { ...@@ -1029,7 +1070,7 @@ framebufferUpdate = function() {
now = (new Date()).getTime(); now = (new Date()).getTime();
timing.cur_fbu += (now - timing.last_fbu); timing.cur_fbu += (now - timing.last_fbu);
timing.h_bytes += last_bytes-RQ.length; timing.h_bytes += last_bytes-RQlen();
if (FBU.rects < last_rects) { if (FBU.rects < last_rects) {
// Some work was done // Some work was done
...@@ -1063,6 +1104,9 @@ framebufferUpdate = function() { ...@@ -1063,6 +1104,9 @@ framebufferUpdate = function() {
timing.fbu_rt_start = 0; timing.fbu_rt_start = 0;
} }
} }
if (! ret) {
break; // false ret means need more data
}
} }
return ret; return ret;
}; };
...@@ -1080,16 +1124,16 @@ encHandlers.RAW = function display_raw() { ...@@ -1080,16 +1124,16 @@ encHandlers.RAW = function display_raw() {
FBU.lines = FBU.height; FBU.lines = FBU.height;
} }
FBU.bytes = FBU.width * fb_Bpp; // At least a line FBU.bytes = FBU.width * fb_Bpp; // At least a line
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
//Util.Debug(" waiting for " + //Util.Debug(" waiting for " +
// (FBU.bytes - RQ.length) + " RAW bytes"); // (FBU.bytes - RQlen()) + " RAW bytes");
return false; return false;
} }
cur_y = FBU.y + (FBU.height - FBU.lines); cur_y = FBU.y + (FBU.height - FBU.lines);
cur_height = Math.min(FBU.lines, cur_height = Math.min(FBU.lines,
Math.floor(RQ.length/(FBU.width * fb_Bpp))); Math.floor(RQlen()/(FBU.width * fb_Bpp)));
canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, RQ, 0); canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, RQ, RQi);
RQ.shiftBytes(FBU.width * cur_height * fb_Bpp); RQshiftBytes(FBU.width * cur_height * fb_Bpp);
FBU.lines -= cur_height; FBU.lines -= cur_height;
if (FBU.lines > 0) { if (FBU.lines > 0) {
...@@ -1106,13 +1150,13 @@ encHandlers.COPYRECT = function display_copy_rect() { ...@@ -1106,13 +1150,13 @@ encHandlers.COPYRECT = function display_copy_rect() {
var old_x, old_y; var old_x, old_y;
if (RQ.length < 4) { if (RQlen() < 4) {
//Util.Debug(" waiting for " + //Util.Debug(" waiting for " +
// (FBU.bytes - RQ.length) + " COPYRECT bytes"); // (FBU.bytes - RQlen()) + " COPYRECT bytes");
return false; return false;
} }
old_x = RQ.shift16(); old_x = RQshift16();
old_y = RQ.shift16(); old_y = RQshift16();
canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height); canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
FBU.rects -= 1; FBU.rects -= 1;
FBU.bytes = 0; FBU.bytes = 0;
...@@ -1120,25 +1164,25 @@ encHandlers.COPYRECT = function display_copy_rect() { ...@@ -1120,25 +1164,25 @@ encHandlers.COPYRECT = function display_copy_rect() {
}; };
encHandlers.RRE = function display_rre() { encHandlers.RRE = function display_rre() {
//Util.Debug(">> display_rre (" + RQ.length + " bytes)"); //Util.Debug(">> display_rre (" + RQlen() + " bytes)");
var color, x, y, width, height, chunk; var color, x, y, width, height, chunk;
if (FBU.subrects === 0) { if (FBU.subrects === 0) {
if (RQ.length < 4 + fb_Bpp) { if (RQlen() < 4 + fb_Bpp) {
//Util.Debug(" waiting for " + //Util.Debug(" waiting for " +
// (4 + fb_Bpp - RQ.length) + " RRE bytes"); // (4 + fb_Bpp - RQlen()) + " RRE bytes");
return false; return false;
} }
FBU.subrects = RQ.shift32(); FBU.subrects = RQshift32();
color = RQ.shiftBytes(fb_Bpp); // Background color = RQshiftBytes(fb_Bpp); // Background
canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
} }
while ((FBU.subrects > 0) && (RQ.length >= (fb_Bpp + 8))) { while ((FBU.subrects > 0) && (RQlen() >= (fb_Bpp + 8))) {
color = RQ.shiftBytes(fb_Bpp); color = RQshiftBytes(fb_Bpp);
x = RQ.shift16(); x = RQshift16();
y = RQ.shift16(); y = RQshift16();
width = RQ.shift16(); width = RQshift16();
height = RQ.shift16(); height = RQshift16();
canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color); canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
FBU.subrects -= 1; FBU.subrects -= 1;
} }
...@@ -1158,7 +1202,7 @@ encHandlers.RRE = function display_rre() { ...@@ -1158,7 +1202,7 @@ encHandlers.RRE = function display_rre() {
encHandlers.HEXTILE = function display_hextile() { encHandlers.HEXTILE = function display_hextile() {
//Util.Debug(">> display_hextile"); //Util.Debug(">> display_hextile");
var subencoding, subrects, idx, tile, color, cur_tile, var subencoding, subrects, tile, color, cur_tile,
tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh; tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh;
if (FBU.tiles === 0) { if (FBU.tiles === 0) {
...@@ -1168,14 +1212,15 @@ encHandlers.HEXTILE = function display_hextile() { ...@@ -1168,14 +1212,15 @@ encHandlers.HEXTILE = function display_hextile() {
FBU.tiles = FBU.total_tiles; FBU.tiles = FBU.total_tiles;
} }
/* FBU.bytes comes in as 1, RQ.length at least 1 */ /* FBU.bytes comes in as 1, RQlen() at least 1 */
while (FBU.tiles > 0) { while (FBU.tiles > 0) {
FBU.bytes = 1; FBU.bytes = 1;
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
//Util.Debug(" waiting for HEXTILE subencoding byte"); //Util.Debug(" waiting for HEXTILE subencoding byte");
return false; return false;
} }
subencoding = RQ[0]; // Peek //Util.Debug(" 2 RQ length: " + RQlen() + " RQ[RQi]: " + RQ[RQi] + " RQ.slice(RQi,RQi+20): " + RQ.slice(RQi,RQi+20) + ", FBU.rects: " + FBU.rects + ", FBU.tiles: " + FBU.tiles);
subencoding = RQ[RQi]; // Peek
if (subencoding > 30) { // Raw if (subencoding > 30) { // Raw
updateState('failed', updateState('failed',
"Disconnected: illegal hextile subencoding " + subencoding); "Disconnected: illegal hextile subencoding " + subencoding);
...@@ -1204,12 +1249,12 @@ encHandlers.HEXTILE = function display_hextile() { ...@@ -1204,12 +1249,12 @@ encHandlers.HEXTILE = function display_hextile() {
} }
if (subencoding & 0x08) { // AnySubrects if (subencoding & 0x08) { // AnySubrects
FBU.bytes += 1; // Since we aren't shifting it off FBU.bytes += 1; // Since we aren't shifting it off
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
/* Wait for subrects byte */ /* Wait for subrects byte */
//Util.Debug(" waiting for hextile subrects header byte"); //Util.Debug(" waiting for hextile subrects header byte");
return false; return false;
} }
subrects = RQ[FBU.bytes-1]; // Peek subrects = RQ[RQi + FBU.bytes-1]; // Peek
if (subencoding & 0x10) { // SubrectsColoured if (subencoding & 0x10) { // SubrectsColoured
FBU.bytes += subrects * (fb_Bpp + 2); FBU.bytes += subrects * (fb_Bpp + 2);
} else { } else {
...@@ -1218,23 +1263,26 @@ encHandlers.HEXTILE = function display_hextile() { ...@@ -1218,23 +1263,26 @@ encHandlers.HEXTILE = function display_hextile() {
} }
} }
//Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + /*
// ", subencoding:" + subencoding + Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
// "(last: " + FBU.lastsubencoding + "), subrects:" + " (" + tile_x + "," + tile_y + ")" +
// subrects + ", tile:" + tile_x + "," + tile_y + " [" + x + "," + y + "]@" + w + "x" + h +
// " [" + x + "," + y + "]@" + w + "x" + h + ", subenc:" + subencoding +
// ", d.length:" + RQ.length + ", bytes:" + FBU.bytes + "(last: " + FBU.lastsubencoding + "), subrects:" +
// " last:" + RQ.slice(FBU.bytes-10, FBU.bytes) + subrects +
// " next:" + RQ.slice(FBU.bytes-1, FBU.bytes+10)); ", RQlen():" + RQlen() + ", FBU.bytes:" + FBU.bytes +
if (RQ.length < FBU.bytes) { " last:" + RQ.slice(FBU.bytes-10, FBU.bytes) +
" next:" + RQ.slice(FBU.bytes-1, FBU.bytes+10));
*/
if (RQlen() < FBU.bytes) {
//Util.Debug(" waiting for " + //Util.Debug(" waiting for " +
// (FBU.bytes - RQ.length) + " hextile bytes"); // (FBU.bytes - RQlen()) + " hextile bytes");
return false; return false;
} }
/* We know the encoding and have a whole tile */ /* We know the encoding and have a whole tile */
FBU.subencoding = RQ[0]; FBU.subencoding = RQ[RQi];
idx = 1; RQi += 1;
if (FBU.subencoding === 0) { if (FBU.subencoding === 0) {
if (FBU.lastsubencoding & 0x01) { if (FBU.lastsubencoding & 0x01) {
/* Weird: ignore blanks after RAW */ /* Weird: ignore blanks after RAW */
...@@ -1243,35 +1291,36 @@ encHandlers.HEXTILE = function display_hextile() { ...@@ -1243,35 +1291,36 @@ encHandlers.HEXTILE = function display_hextile() {
canvas.fillRect(x, y, w, h, FBU.background); canvas.fillRect(x, y, w, h, FBU.background);
} }
} else if (FBU.subencoding & 0x01) { // Raw } else if (FBU.subencoding & 0x01) { // Raw
canvas.blitImage(x, y, w, h, RQ, idx); canvas.blitImage(x, y, w, h, RQ, RQi);
RQi += FBU.bytes - 1;
} else { } else {
if (FBU.subencoding & 0x02) { // Background if (FBU.subencoding & 0x02) { // Background
FBU.background = RQ.slice(idx, idx + fb_Bpp); FBU.background = RQ.slice(RQi, RQi + fb_Bpp);
idx += fb_Bpp; RQi += fb_Bpp;
} }
if (FBU.subencoding & 0x04) { // Foreground if (FBU.subencoding & 0x04) { // Foreground
FBU.foreground = RQ.slice(idx, idx + fb_Bpp); FBU.foreground = RQ.slice(RQi, RQi + fb_Bpp);
idx += fb_Bpp; RQi += fb_Bpp;
} }
tile = canvas.getTile(x, y, w, h, FBU.background); tile = canvas.getTile(x, y, w, h, FBU.background);
if (FBU.subencoding & 0x08) { // AnySubrects if (FBU.subencoding & 0x08) { // AnySubrects
subrects = RQ[idx]; subrects = RQ[RQi];
idx += 1; RQi += 1;
for (s = 0; s < subrects; s += 1) { for (s = 0; s < subrects; s += 1) {
if (FBU.subencoding & 0x10) { // SubrectsColoured if (FBU.subencoding & 0x10) { // SubrectsColoured
color = RQ.slice(idx, idx + fb_Bpp); color = RQ.slice(RQi, RQi + fb_Bpp);
idx += fb_Bpp; RQi += fb_Bpp;
} else { } else {
color = FBU.foreground; color = FBU.foreground;
} }
xy = RQ[idx]; xy = RQ[RQi];
idx += 1; RQi += 1;
sx = (xy >> 4); sx = (xy >> 4);
sy = (xy & 0x0f); sy = (xy & 0x0f);
wh = RQ[idx]; wh = RQ[RQi];
idx += 1; RQi += 1;
sw = (wh >> 4) + 1; sw = (wh >> 4) + 1;
sh = (wh & 0x0f) + 1; sh = (wh & 0x0f) + 1;
...@@ -1280,7 +1329,7 @@ encHandlers.HEXTILE = function display_hextile() { ...@@ -1280,7 +1329,7 @@ encHandlers.HEXTILE = function display_hextile() {
} }
canvas.putTile(tile); canvas.putTile(tile);
} }
RQ.shiftBytes(FBU.bytes); //RQshiftBytes(FBU.bytes);
FBU.lastsubencoding = FBU.subencoding; FBU.lastsubencoding = FBU.subencoding;
FBU.bytes = 0; FBU.bytes = 0;
FBU.tiles -= 1; FBU.tiles -= 1;
...@@ -1299,12 +1348,12 @@ encHandlers.TIGHT_PNG = function display_tight_png() { ...@@ -1299,12 +1348,12 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
//Util.Debug(">> display_tight_png"); //Util.Debug(">> display_tight_png");
var ctl, cmode, clength, getCLength, color, img; var ctl, cmode, clength, getCLength, color, img;
//Util.Debug(" FBU.rects: " + FBU.rects); //Util.Debug(" FBU.rects: " + FBU.rects);
//Util.Debug(" RQ.length: " + RQ.length); //Util.Debug(" RQlen(): " + RQlen());
//Util.Debug(" RQ.slice(0,20): " + RQ.slice(0,20)); //Util.Debug(" RQ.slice(0,20): " + RQ.slice(0,20));
FBU.bytes = 1; // compression-control byte FBU.bytes = 1; // compression-control byte
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
Util.Debug(" waiting for TIGHT compression-control byte"); Util.Debug(" waiting for TIGHT compression-control byte");
return false; return false;
} }
...@@ -1324,7 +1373,7 @@ encHandlers.TIGHT_PNG = function display_tight_png() { ...@@ -1324,7 +1373,7 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
return [header, data]; return [header, data];
}; };
ctl = RQ[0]; ctl = RQ[RQi];
switch (ctl >> 4) { switch (ctl >> 4) {
case 0x08: cmode = "fill"; break; case 0x08: cmode = "fill"; break;
case 0x09: cmode = "jpeg"; break; case 0x09: cmode = "jpeg"; break;
...@@ -1338,44 +1387,44 @@ encHandlers.TIGHT_PNG = function display_tight_png() { ...@@ -1338,44 +1387,44 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
case "png": FBU.bytes += 3; break; // max clength case "png": FBU.bytes += 3; break; // max clength
} }
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
Util.Debug(" waiting for TIGHT " + cmode + " bytes"); Util.Debug(" waiting for TIGHT " + cmode + " bytes");
return false; return false;
} }
//Util.Debug(" RQ.slice(0,20): " + RQ.slice(0,20) + " (" + RQ.length + ")"); //Util.Debug(" RQ.slice(0,20): " + RQ.slice(0,20) + " (" + RQlen() + ")");
//Util.Debug(" cmode: " + cmode); //Util.Debug(" cmode: " + cmode);
// Determine FBU.bytes // Determine FBU.bytes
switch (cmode) { switch (cmode) {
case "fill": case "fill":
RQ.shift8(); // shift off ctl RQ[RQi++]; // shift off ctl
color = RQ.shiftBytes(fb_depth); color = RQshiftBytes(fb_depth);
canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
break; break;
case "jpeg": case "jpeg":
case "png": case "png":
clength = getCLength(RQ, 1); clength = getCLength(RQ, 1);
FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
if (RQ.length < FBU.bytes) { if (RQlen() < FBU.bytes) {
Util.Debug(" waiting for TIGHT " + cmode + " bytes"); Util.Debug(" waiting for TIGHT " + cmode + " bytes");
return false; return false;
} }
// We have everything, render it // We have everything, render it
//Util.Debug(" png, RQ.length: " + RQ.length + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]); //Util.Debug(" png, RQlen(): " + RQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]);
RQ.shiftBytes(1 + clength[0]); // shift off ctl + compact length RQshiftBytes(1 + clength[0]); // shift off ctl + compact length
img = new Image(); img = new Image();
img.onload = scan_tight_imgs; img.onload = scan_tight_imgs;
FBU.imgs.push([img, FBU.x, FBU.y]); FBU.imgs.push([img, FBU.x, FBU.y]);
img.src = "data:image/" + cmode + img.src = "data:image/" + cmode +
extract_data_uri(RQ.shiftBytes(clength[1])); extract_data_uri(RQshiftBytes(clength[1]));
img = null; img = null;
break; break;
} }
FBU.bytes = 0; FBU.bytes = 0;
FBU.rects -= 1; FBU.rects -= 1;
//Util.Debug(" ending RQ.length: " + RQ.length); //Util.Debug(" ending RQlen(): " + RQlen());
//Util.Debug(" ending RQ.slice(0,20): " + RQ.slice(0,20)); //Util.Debug(" ending RQ.slice(0,20): " + RQ.slice(0,20));
//Util.Debug("<< display_tight_png"); //Util.Debug("<< display_tight_png");
return true; return true;
...@@ -1431,7 +1480,7 @@ encHandlers.Cursor = function set_cursor() { ...@@ -1431,7 +1480,7 @@ encHandlers.Cursor = function set_cursor() {
pixelslength = w * h * fb_Bpp; pixelslength = w * h * fb_Bpp;
masklength = Math.floor((w + 7) / 8) * h; masklength = Math.floor((w + 7) / 8) * h;
if (RQ.length < (pixelslength + masklength)) { if (RQlen() < (pixelslength + masklength)) {
//Util.Debug("waiting for cursor encoding bytes"); //Util.Debug("waiting for cursor encoding bytes");
FBU.bytes = pixelslength + masklength; FBU.bytes = pixelslength + masklength;
return false; return false;
...@@ -1439,8 +1488,8 @@ encHandlers.Cursor = function set_cursor() { ...@@ -1439,8 +1488,8 @@ encHandlers.Cursor = function set_cursor() {
//Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h); //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
canvas.changeCursor(RQ.shiftBytes(pixelslength), canvas.changeCursor(RQshiftBytes(pixelslength),
RQ.shiftBytes(masklength), RQshiftBytes(masklength),
x, y, w, h); x, y, w, h);
FBU.bytes = 0; FBU.bytes = 0;
...@@ -1556,13 +1605,16 @@ pointerEvent = function(x, y) { ...@@ -1556,13 +1605,16 @@ pointerEvent = function(x, y) {
clientCutText = function(text) { clientCutText = function(text) {
//Util.Debug(">> clientCutText"); //Util.Debug(">> clientCutText");
var arr; var arr, i, n;
arr = [6]; // msg-type arr = [6]; // msg-type
arr.push8(0); // padding arr.push8(0); // padding
arr.push8(0); // padding arr.push8(0); // padding
arr.push8(0); // padding arr.push8(0); // padding
arr.push32(text.length); arr.push32(text.length);
arr.pushStr(text); n = text.length;
for (i=0; i < n; i+=1) {
arr.push(text.charCodeAt(i));
}
//Util.Debug("<< clientCutText:" + arr); //Util.Debug("<< clientCutText:" + arr);
return arr; return arr;
}; };
......
...@@ -34,68 +34,20 @@ if (!window.$) { ...@@ -34,68 +34,20 @@ if (!window.$) {
* Make arrays quack * Make arrays quack
*/ */
Array.prototype.shift8 = function () {
return this.shift();
};
Array.prototype.push8 = function (num) { Array.prototype.push8 = function (num) {
this.push(num & 0xFF); this.push(num & 0xFF);
}; };
Array.prototype.shift16 = function () {
return (this.shift() << 8) +
(this.shift() );
};
Array.prototype.push16 = function (num) { Array.prototype.push16 = function (num) {
this.push((num >> 8) & 0xFF, this.push((num >> 8) & 0xFF,
(num ) & 0xFF ); (num ) & 0xFF );
}; };
Array.prototype.push16le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF );
};
Array.prototype.shift32 = function () {
return (this.shift() << 24) +
(this.shift() << 16) +
(this.shift() << 8) +
(this.shift() );
};
Array.prototype.get32 = function (off) {
return (this[off ] << 24) +
(this[off + 1] << 16) +
(this[off + 2] << 8) +
(this[off + 3] );
};
Array.prototype.push32 = function (num) { Array.prototype.push32 = function (num) {
this.push((num >> 24) & 0xFF, this.push((num >> 24) & 0xFF,
(num >> 16) & 0xFF, (num >> 16) & 0xFF,
(num >> 8) & 0xFF, (num >> 8) & 0xFF,
(num ) & 0xFF ); (num ) & 0xFF );
}; };
Array.prototype.push32le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF );
};
Array.prototype.shiftStr = function (len) {
var arr = this.splice(0, len);
return arr.map(function (num) {
return String.fromCharCode(num); } ).join('');
};
Array.prototype.pushStr = function (str) {
var i, n = str.length;
for (i=0; i < n; i+=1) {
this.push(str.charCodeAt(i));
}
};
Array.prototype.shiftBytes = function (len) {
return this.splice(0, len);
};
/* /*
* ------------------------------------------------------ * ------------------------------------------------------
......
...@@ -45,6 +45,17 @@ ...@@ -45,6 +45,17 @@
var ANDsz = w * h * 4; var ANDsz = w * h * 4;
var XORsz = Math.ceil( (w * h) / 8.0 ); var XORsz = Math.ceil( (w * h) / 8.0 );
// Push multi-byte little-endian values
arr.push16le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF );
};
arr.push32le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF );
};
// Main header // Main header
arr.push16le(0); // Reserved arr.push16le(0); // Reserved
......
<html> <html>
<head> <head>
<title>VNC Test</title> <title>VNC Playback</title>
<link rel="stylesheet" href="include/plain.css"> <link rel="stylesheet" href="include/plain.css">
</head> </head>
<body> <body>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment