]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Add support for ContinuousUpdates
authorsamhed <samuel@cendio.se>
Thu, 2 Jun 2016 14:41:38 +0000 (16:41 +0200)
committersamhed <samuel@cendio.se>
Fri, 26 Aug 2016 09:04:19 +0000 (11:04 +0200)
Instead of requesting frame buffer updates we can, if the server
supports it, continuously recieve frame buffer updates at a rate
determined by the server.

The server can use fencing messages and measure response times to
determine how often it will continue to send updates.

include/rfb.js
tests/test.rfb.js

index 71672ffe1aed5cc67cfe38997d0fbaeb59d3d6a7..e6597af246b4824571d08ea4124aac82f085e8e5 100644 (file)
@@ -57,7 +57,8 @@ var RFB;
             ['Cursor',              -239 ],
             ['ExtendedDesktopSize', -308 ],
             ['xvp',                 -309 ],
-            ['Fence',               -312 ]
+            ['Fence',               -312 ],
+            ['ContinuousUpdates',   -313 ]
         ];
 
         this._encHandlers = {};
@@ -73,6 +74,9 @@ var RFB;
 
         this._supportsFence = false;
 
+        this._supportsContinuousUpdates = false;
+        this._enabledContinuousUpdates = false;
+
         // Frame buffer update state
         this._FBU = {
             rects: 0,
@@ -975,7 +979,7 @@ var RFB;
 
             RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color);
             RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color);
-            RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
+            RFB.messages.fbUpdateRequests(this._sock, false, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
 
             this._timing.fbu_rt_start = (new Date()).getTime();
             this._timing.pixels = 0;
@@ -1051,7 +1055,13 @@ var RFB;
             var length = this._sock.rQshift8();
 
             if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
-            var payload = this._sock.rQshiftStr(length); // FIXME: 64 bytes max
+
+            if (length > 64) {
+                Util.Warn("Bad payload length (" + length + ") in fence response");
+                length = 64;
+            }
+
+            var payload = this._sock.rQshiftStr(length);
 
             this._supportsFence = true;
 
@@ -1116,7 +1126,10 @@ var RFB;
                 case 0:  // FramebufferUpdate
                     var ret = this._framebufferUpdate();
                     if (ret) {
-                        RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
+                        RFB.messages.fbUpdateRequests(this._sock,
+                                                      this._enabledContinuousUpdates,
+                                                      this._display.getCleanDirtyReset(),
+                                                      this._fb_width, this._fb_height);
                     }
                     return ret;
 
@@ -1131,6 +1144,20 @@ var RFB;
                 case 3:  // ServerCutText
                     return this._handle_server_cut_text();
 
+                case 150: // EndOfContinuousUpdates
+                    var first = !(this._supportsContinuousUpdates);
+                    this._supportsContinuousUpdates = true;
+                    this._enabledContinuousUpdates = false;
+                    if (first) {
+                        this._enabledContinuousUpdates = true;
+                        this._updateContinuousUpdates();
+                        Util.Info("Enabling continuous updates.");
+                    } else {
+                        // FIXME: We need to send a framebufferupdaterequest here
+                        // if we add support for turning off continuous updates
+                    }
+                    return true;
+
                 case 248: // ServerFence
                     return this._handle_server_fence_msg();
 
@@ -1245,6 +1272,13 @@ var RFB;
 
             return true;  // We finished this FBU
         },
+
+        _updateContinuousUpdates: function() {
+            if (!this._enabledContinuousUpdates) { return; }
+
+            RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
+                                                 this._fb_width, this._fb_height);
+        }
     };
 
     Util.make_properties(RFB, [
@@ -1419,6 +1453,26 @@ var RFB;
             sock.flush();
         },
 
+        enableContinuousUpdates: function (sock, enable, x, y, width, height) {
+            var buff = sock._sQ;
+            var offset = sock._sQlen;
+
+            buff[offset] = 150;             // msg-type
+            buff[offset + 1] = enable;      // enable-flag
+
+            buff[offset + 2] = x >> 8;      // x
+            buff[offset + 3] = x;
+            buff[offset + 4] = y >> 8;      // y
+            buff[offset + 5] = y;
+            buff[offset + 6] = width >> 8;  // width
+            buff[offset + 7] = width;
+            buff[offset + 8] = height >> 8; // height
+            buff[offset + 9] = height;
+
+            sock._sQlen += 10;
+            sock.flush();
+        },
+
         pixelFormat: function (sock, bpp, depth, true_color) {
             var buff = sock._sQ;
             var offset = sock._sQlen;
@@ -1490,12 +1544,12 @@ var RFB;
             sock.flush();
         },
 
-        fbUpdateRequests: function (sock, cleanDirty, fb_width, fb_height) {
+        fbUpdateRequests: function (sock, onlyNonInc, cleanDirty, fb_width, fb_height) {
             var offsetIncrement = 0;
 
             var cb = cleanDirty.cleanBox;
             var w, h;
-            if (cb.w > 0 && cb.h > 0) {
+            if (!onlyNonInc && (cb.w > 0 && cb.h > 0)) {
                 w = typeof cb.w === "undefined" ? fb_width : cb.w;
                 h = typeof cb.h === "undefined" ? fb_height : cb.h;
                 // Request incremental for clean box
@@ -2102,6 +2156,7 @@ var RFB;
             this._display.resize(this._fb_width, this._fb_height);
             this._onFBResize(this, this._fb_width, this._fb_height);
             this._timing.fbu_rt_start = (new Date()).getTime();
+            this._updateContinuousUpdates();
 
             this._FBU.bytes = 0;
             this._FBU.rects -= 1;
index be6aa1b2d52acb270c241d27b81f13e834307d56..65ce5f882720972c74f85db29a7e8ea38ad9bd44 100644 (file)
@@ -1197,6 +1197,33 @@ describe('Remote Frame Buffer Protocol Client', function() {
                 expect(client._sock).to.have.sent(expected_msg._sQ);
             });
 
+            it('should only request non-incremental rects in continuous updates mode', function () {
+                var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
+                var expected_cdr = { cleanBox: { x: 0, y: 0, w: 120, h: 20 },
+                                     dirtyBoxes: [ { x: 120, y: 0, w: 120, h: 20 } ] };
+
+                RFB.messages.fbUpdateRequest(expected_msg, false, 120, 0, 120, 20);
+
+                client._enabledContinuousUpdates = true;
+                client._framebufferUpdate = function () { return true; };
+                client._display.getCleanDirtyReset = function () { return expected_cdr; };
+                client._sock._websocket._receive_data(new Uint8Array([0]));
+
+                expect(client._sock).to.have.sent(expected_msg._sQ);
+            });
+
+            it('should not send a request in continuous updates mode when clean', function () {
+                var expected_cdr = { cleanBox: { x: 0, y: 0, w: 240, h: 20 },
+                                     dirtyBoxes: [] };
+
+                client._enabledContinuousUpdates = true;
+                client._framebufferUpdate = function () { return true; };
+                client._display.getCleanDirtyReset = function () { return expected_cdr; };
+                client._sock._websocket._receive_data(new Uint8Array([0]));
+
+                expect(client._sock._websocket._get_sent_data()).to.have.length(0);
+            });
+
             it('should parse out information from a header before any actual data comes in', function () {
                 client.set_onFBUReceive(sinon.spy());
                 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 0x02, encodingName: 'RRE' };
@@ -1730,6 +1757,49 @@ describe('Remote Frame Buffer Protocol Client', function() {
             expect(client._sock).to.have.sent(expected_msg._sQ);
         });
 
+        it('should enable continuous updates on first EndOfContinousUpdates', function () {
+            var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
+
+            RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 640, 20);
+
+            expect(client._enabledContinuousUpdates).to.be.false;
+
+            client._sock._websocket._receive_data(new Uint8Array([150]));
+
+            expect(client._enabledContinuousUpdates).to.be.true;
+            expect(client._sock).to.have.sent(expected_msg._sQ);
+        });
+
+        it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
+            client._enabledContinuousUpdates = true;
+            client._supportsContinuousUpdates = true;
+
+            client._sock._websocket._receive_data(new Uint8Array([150]));
+
+            expect(client._enabledContinuousUpdates).to.be.false;
+        });
+
+        it('should update continuous updates on resize', function () {
+            var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
+            RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 90, 700);
+
+            client._FBU.width = 450;
+            client._FBU.height = 160;
+
+            client._encHandlers.handle_FB_resize();
+
+            expect(client._sock._websocket._get_sent_data()).to.have.length(0);
+
+            client._enabledContinuousUpdates = true;
+
+            client._FBU.width = 90;
+            client._FBU.height = 700;
+
+            client._encHandlers.handle_FB_resize();
+
+            expect(client._sock).to.have.sent(expected_msg._sQ);
+        });
+
         it('should fail on an unknown message type', function () {
             client._sock._websocket._receive_data(new Uint8Array([87]));
             expect(client._rfb_state).to.equal('failed');