]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Indexed receive queue. Up to 2X speedup in Chrome.
authorJoel Martin <github@martintribe.org>
Thu, 26 Aug 2010 15:22:29 +0000 (10:22 -0500)
committerJoel Martin <github@martintribe.org>
Thu, 26 Aug 2010 15:22:29 +0000 (10:22 -0500)
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.

include/canvas.js
include/rfb.js
include/util.js
tests/cursor.html
tests/vnc_playback.html

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