diff options
author | Samuel Mannehed <samuel@cendio.se> | 2016-12-13 12:42:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-13 12:42:22 +0100 |
commit | c01b2f0259b101cd9b7fdac610ddb68a1e6d828a (patch) | |
tree | fc13ddad08dce733ff8c827ab8542023378ad194 | |
parent | e663da8c326442bad15b2b4463a4f56becbdad6e (diff) | |
parent | adf345fdc4409537bf2599c0550d771344da38e6 (diff) | |
download | novnc-c01b2f0259b101cd9b7fdac610ddb68a1e6d828a.tar.gz |
Merge pull request #699 from CendioOssman/double
Display double buffering
-rw-r--r-- | app/ui.js | 34 | ||||
-rw-r--r-- | core/display.js | 436 | ||||
-rw-r--r-- | core/rfb.js | 35 | ||||
-rw-r--r-- | tests/assertions.js | 3 | ||||
-rw-r--r-- | tests/playback.js | 76 | ||||
-rw-r--r-- | tests/test.display.js | 260 | ||||
-rw-r--r-- | tests/test.rfb.js | 70 | ||||
-rw-r--r-- | tests/vnc_perf.html | 27 | ||||
-rw-r--r-- | tests/vnc_playback.html | 20 |
9 files changed, 463 insertions, 498 deletions
@@ -398,7 +398,7 @@ var UI; 'onClipboard': UI.clipboardReceive, 'onBell': UI.bell, 'onFBUComplete': UI.initialResize, - 'onFBResize': UI.updateViewDrag, + 'onFBResize': UI.updateSessionSize, 'onDesktopName': UI.updateDesktopName}); return true; } catch (exc) { @@ -1204,11 +1204,6 @@ var UI; // is finished we wait 0.5 seconds before sending the request. clearTimeout(UI.resizeTimeout); UI.resizeTimeout = setTimeout(function(){ - - // Limit the viewport to the size of the browser window - display.set_maxWidth(screen.w); - display.set_maxHeight(screen.h); - // Request a remote size covering the viewport if (UI.rfb.requestDesktopSize(screen.w, screen.h)) { Util.Debug('Requested new desktop size: ' + @@ -1289,27 +1284,7 @@ var UI; if (new_clip && size) { // When clipping is enabled, the screen is limited to // the size of the browser window. - display.set_maxWidth(size.w); - display.set_maxHeight(size.h); - - var screen = document.getElementById('noVNC_screen'); - var canvas = document.getElementById('noVNC_canvas'); - - // Hide potential scrollbars that can skew the position - screen.style.overflow = "hidden"; - - // The x position marks the left margin of the canvas, - // remove the margin from both sides to keep it centered. - var new_w = size.w - (2 * Util.getPosition(canvas).x); - - screen.style.overflow = "visible"; - - display.viewportChangeSize(new_w, size.h); - } else { - // Disable max dimensions - display.set_maxWidth(0); - display.set_maxHeight(0); - display.viewportChangeSize(); + display.viewportChangeSize(size.w, size.h); } }, @@ -1678,6 +1653,11 @@ var UI; } }, + updateSessionSize: function(rfb, width, height) { + UI.updateViewClip(); + UI.updateViewDrag(); + }, + updateDesktopName: function(rfb, name) { UI.desktopName = name; // Display the desktop name in the document title diff --git a/core/display.js b/core/display.js index d9f66d7..ac2e1e5 100644 --- a/core/display.js +++ b/core/display.js @@ -26,14 +26,6 @@ this._fb_width = 0; this._fb_height = 0; - // the size limit of the viewport (start disabled) - this._maxWidth = 0; - this._maxHeight = 0; - - // the visible "physical canvas" viewport - this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 }; - this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 }; - this._prevDrawStyle = ""; this._tile = null; this._tile16x16 = null; @@ -51,6 +43,7 @@ Util.Debug(">> Display.constructor"); + // The visible canvas if (!this._target) { throw new Error("Target must be set"); } @@ -63,9 +56,18 @@ throw new Error("no getContext method"); } - if (!this._drawCtx) { - this._drawCtx = this._target.getContext('2d'); - } + this._targetCtx = this._target.getContext('2d'); + + // the visible canvas viewport (i.e. what actually gets seen) + this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height }; + + // The hidden canvas, where we do the actual rendering + this._backbuffer = document.createElement('canvas'); + this._drawCtx = this._backbuffer.getContext('2d'); + + this._damageBounds = { left:0, top:0, + right: this._backbuffer.width, + bottom: this._backbuffer.height }; Util.Debug("User Agent: " + navigator.userAgent); if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } @@ -145,187 +147,49 @@ Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); vp.x += deltaX; - vx2 += deltaX; vp.y += deltaY; - vy2 += deltaY; - // Update the clean rectangle - var cr = this._cleanRect; - if (vp.x > cr.x1) { - cr.x1 = vp.x; - } - if (vx2 < cr.x2) { - cr.x2 = vx2; - } - if (vp.y > cr.y1) { - cr.y1 = vp.y; - } - if (vy2 < cr.y2) { - cr.y2 = vy2; - } + this._damage(vp.x, vp.y, vp.w, vp.h); - var x1, w; - if (deltaX < 0) { - // Shift viewport left, redraw left section - x1 = 0; - w = -deltaX; - } else { - // Shift viewport right, redraw right section - x1 = vp.w - deltaX; - w = deltaX; - } - - var y1, h; - if (deltaY < 0) { - // Shift viewport up, redraw top section - y1 = 0; - h = -deltaY; - } else { - // Shift viewport down, redraw bottom section - y1 = vp.h - deltaY; - h = deltaY; - } - - var saveStyle = this._drawCtx.fillStyle; - var canvas = this._target; - this._drawCtx.fillStyle = "rgb(255,255,255)"; - - // Due to this bug among others [1] we need to disable the image-smoothing to - // avoid getting a blur effect when panning. - // - // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 - // - // We need to set these every time since all properties are reset - // when the the size is changed - if (this._drawCtx.mozImageSmoothingEnabled) { - this._drawCtx.mozImageSmoothingEnabled = false; - } else if (this._drawCtx.webkitImageSmoothingEnabled) { - this._drawCtx.webkitImageSmoothingEnabled = false; - } else if (this._drawCtx.msImageSmoothingEnabled) { - this._drawCtx.msImageSmoothingEnabled = false; - } else if (this._drawCtx.imageSmoothingEnabled) { - this._drawCtx.imageSmoothingEnabled = false; - } - - // Copy the valid part of the viewport to the shifted location - this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, -deltaY, vp.w, vp.h); - - if (deltaX !== 0) { - this._drawCtx.fillRect(x1, 0, w, vp.h); - } - if (deltaY !== 0) { - this._drawCtx.fillRect(0, y1, vp.w, h); - } - this._drawCtx.fillStyle = saveStyle; + this.flip(); }, viewportChangeSize: function(width, height) { - if (typeof(width) === "undefined" || typeof(height) === "undefined") { + if (!this._viewport || + typeof(width) === "undefined" || + typeof(height) === "undefined") { Util.Debug("Setting viewport to full display region"); width = this._fb_width; height = this._fb_height; } + if (width > this._fb_width) { + width = this._fb_width; + } + if (height > this._fb_height) { + height = this._fb_height; + } + var vp = this._viewportLoc; if (vp.w !== width || vp.h !== height) { - - if (this._viewport) { - if (this._maxWidth !== 0 && width > this._maxWidth) { - width = this._maxWidth; - } - if (this._maxHeight !== 0 && height > this._maxHeight) { - height = this._maxHeight; - } - } - - var cr = this._cleanRect; - - if (width < vp.w && cr.x2 > vp.x + width - 1) { - cr.x2 = vp.x + width - 1; - } - if (height < vp.h && cr.y2 > vp.y + height - 1) { - cr.y2 = vp.y + height - 1; - } - vp.w = width; vp.h = height; var canvas = this._target; - if (canvas.width !== width || canvas.height !== height) { - - // We have to save the canvas data since changing the size will clear it - var saveImg = null; - if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) { - var img_width = canvas.width < vp.w ? canvas.width : vp.w; - var img_height = canvas.height < vp.h ? canvas.height : vp.h; - saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height); - } - - if (canvas.width !== width) { - canvas.width = width; - canvas.style.width = width + 'px'; - } - if (canvas.height !== height) { - canvas.height = height; - canvas.style.height = height + 'px'; - } - - if (saveImg) { - this._drawCtx.putImageData(saveImg, 0, 0); - } - } - } - }, + canvas.width = width; + canvas.height = height; - // Return a map of clean and dirty areas of the viewport and reset the - // tracking of clean and dirty areas - // - // Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h}, - // 'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] } - getCleanDirtyReset: function () { - var vp = this._viewportLoc; - var cr = this._cleanRect; + // The position might need to be updated if we've grown + this.viewportChangePos(0, 0); - var cleanBox = { 'x': cr.x1, 'y': cr.y1, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 }; + this._damage(vp.x, vp.y, vp.w, vp.h); + this.flip(); - var dirtyBoxes = []; - if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) { - // Whole viewport is dirty - dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h }); - } else { - // Redraw dirty regions - var vx2 = vp.x + vp.w - 1; - var vy2 = vp.y + vp.h - 1; - - if (vp.x < cr.x1) { - // left side dirty region - dirtyBoxes.push({'x': vp.x, 'y': vp.y, - 'w': cr.x1 - vp.x + 1, 'h': vp.h}); - } - if (vx2 > cr.x2) { - // right side dirty region - dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y, - 'w': vx2 - cr.x2, 'h': vp.h}); - } - if(vp.y < cr.y1) { - // top/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': vp.y, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y}); - } - if (vy2 > cr.y2) { - // bottom/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1, - 'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2}); - } + // Update the visible size of the target canvas + this._rescale(this._scale); } - - this._cleanRect = {'x1': vp.x, 'y1': vp.y, - 'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1}; - - return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; }, absX: function (x) { @@ -342,27 +206,109 @@ this._fb_width = width; this._fb_height = height; - this._rescale(this._scale); + var canvas = this._backbuffer; + if (canvas.width !== width || canvas.height !== height) { - this.viewportChangeSize(); + // We have to save the canvas data since changing the size will clear it + var saveImg = null; + if (canvas.width > 0 && canvas.height > 0) { + saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height); + } + + if (canvas.width !== width) { + canvas.width = width; + } + if (canvas.height !== height) { + canvas.height = height; + } + + if (saveImg) { + this._drawCtx.putImageData(saveImg, 0, 0); + } + } + + // Readjust the viewport as it may be incorrectly sized + // and positioned + var vp = this._viewportLoc; + this.viewportChangeSize(vp.w, vp.h); + this.viewportChangePos(0, 0); + }, + + // Track what parts of the visible canvas that need updating + _damage: function(x, y, w, h) { + if (x < this._damageBounds.left) { + this._damageBounds.left = x; + } + if (y < this._damageBounds.top) { + this._damageBounds.top = y; + } + if ((x + w) > this._damageBounds.right) { + this._damageBounds.right = x + w; + } + if ((y + h) > this._damageBounds.bottom) { + this._damageBounds.bottom = y + h; + } + }, + + // Update the visible canvas with the contents of the + // rendering canvas + flip: function(from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + this._renderQ_push({ + 'type': 'flip' + }); + } else { + var x, y, vx, vy, w, h; + + x = this._damageBounds.left; + y = this._damageBounds.top; + w = this._damageBounds.right - x; + h = this._damageBounds.bottom - y; + + vx = x - this._viewportLoc.x; + vy = y - this._viewportLoc.y; + + if (vx < 0) { + w += vx; + x -= vx; + vx = 0; + } + if (vy < 0) { + h += vy; + y -= vy; + vy = 0; + } + + if ((vx + w) > this._viewportLoc.w) { + w = this._viewportLoc.w - vx; + } + if ((vy + h) > this._viewportLoc.h) { + h = this._viewportLoc.h - vy; + } + + if ((w > 0) && (h > 0)) { + // FIXME: We may need to disable image smoothing here + // as well (see copyImage()), but we haven't + // noticed any problem yet. + this._targetCtx.drawImage(this._backbuffer, + x, y, w, h, + vx, vy, w, h); + } + + this._damageBounds.left = this._damageBounds.top = 65535; + this._damageBounds.right = this._damageBounds.bottom = 0; + } }, clear: function () { if (this._logo) { this.resize(this._logo.width, this._logo.height); - this.blitStringImage(this._logo.data, 0, 0); + this.imageRect(0, 0, this._logo.type, this._logo.data); } else { - if (Util.Engine.trident === 6) { - // NB(directxman12): there's a bug in IE10 where we can fail to actually - // clear the canvas here because of the resize. - // Clearing the current viewport first fixes the issue - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); - } this.resize(240, 20); - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); + this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height); } - - this._renderQ = []; + this.flip(); }, pending: function() { @@ -389,7 +335,8 @@ }); } else { this._setFillColor(color); - this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height); + this._drawCtx.fillRect(x, y, width, height); + this._damage(x, y, width, height); } }, @@ -405,12 +352,22 @@ 'height': h, }); } else { - var x1 = old_x - this._viewportLoc.x; - var y1 = old_y - this._viewportLoc.y; - var x2 = new_x - this._viewportLoc.x; - var y2 = new_y - this._viewportLoc.y; + // Due to this bug among others [1] we need to disable the image-smoothing to + // avoid getting a blur effect when copying data. + // + // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 + // + // We need to set these every time since all properties are reset + // when the the size is changed + this._drawCtx.mozImageSmoothingEnabled = false; + this._drawCtx.webkitImageSmoothingEnabled = false; + this._drawCtx.msImageSmoothingEnabled = false; + this._drawCtx.imageSmoothingEnabled = false; - this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h); + this._drawCtx.drawImage(this._backbuffer, + old_x, old_y, w, h, + new_x, new_y, w, h); + this._damage(new_x, new_y, w, h); } }, @@ -492,8 +449,9 @@ // draw the current tile to the screen finishTile: function () { if (this._prefer_js) { - this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x, - this._tile_y - this._viewportLoc.y); + this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y); + this._damage(this._tile_x, this._tile_y, + this._tile.width, this._tile.height); } // else: No-op -- already done by setSubTile }, @@ -514,9 +472,9 @@ 'height': height, }); } else if (this._true_color) { - this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._bgrxImageData(x, y, width, height, arr, offset); } else { - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._cmapImageData(x, y, width, height, arr, offset); } }, @@ -536,10 +494,10 @@ 'height': height, }); } else if (this._true_color) { - this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._rgbImageData(x, y, width, height, arr, offset); } else { // probably wrong? - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._cmapImageData(x, y, width, height, arr, offset); } }, @@ -559,22 +517,13 @@ 'height': height, }); } else { - this._rgbxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._rgbxImageData(x, y, width, height, arr, offset); } }, - blitStringImage: function (str, x, y) { - var img = new Image(); - img.onload = function () { - this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); - }.bind(this); - img.src = str; - return img; // for debugging purposes - }, - - // wrap ctx.drawImage but relative to viewport drawImage: function (img, x, y) { - this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); + this._drawCtx.drawImage(img, x, y); + this._damage(x, y, img.width, img.height); }, changeCursor: function (pixels, mask, hotx, hoty, w, h) { @@ -600,37 +549,25 @@ clippingDisplay: function () { var vp = this._viewportLoc; - - var fbClip = this._fb_width > vp.w || this._fb_height > vp.h; - var limitedVp = this._maxWidth !== 0 && this._maxHeight !== 0; - var clipping = false; - - if (limitedVp) { - clipping = vp.w > this._maxWidth || vp.h > this._maxHeight; - } - - return fbClip || (limitedVp && clipping); + return this._fb_width > vp.w || this._fb_height > vp.h; }, // Overridden getters/setters - get_context: function () { - return this._drawCtx; - }, - set_scale: function (scale) { this._rescale(scale); }, - set_width: function (w) { - this._fb_width = w; + set_viewport: function (viewport) { + this._viewport = viewport; + // May need to readjust the viewport dimensions + var vp = this._viewportLoc; + this.viewportChangeSize(vp.w, vp.h); + this.viewportChangePos(0, 0); }, + get_width: function () { return this._fb_width; }, - - set_height: function (h) { - this._fb_height = h; - }, get_height: function () { return this._fb_height; }, @@ -674,21 +611,9 @@ // Private Methods _rescale: function (factor) { this._scale = factor; - - var w; - var h; - - if (this._viewport && - this._maxWidth !== 0 && this._maxHeight !== 0) { - w = Math.min(this._fb_width, this._maxWidth); - h = Math.min(this._fb_height, this._maxHeight); - } else { - w = this._fb_width; - h = this._fb_height; - } - - this._target.style.width = Math.round(factor * w) + 'px'; - this._target.style.height = Math.round(factor * h) + 'px'; + var vp = this._viewportLoc; + this._target.style.width = Math.round(factor * vp.w) + 'px'; + this._target.style.height = Math.round(factor * vp.h) + 'px'; }, _setFillColor: function (color) { @@ -706,7 +631,7 @@ } }, - _rgbImageData: function (x, y, vx, vy, width, height, arr, offset) { + _rgbImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { @@ -715,10 +640,11 @@ data[i + 2] = arr[j + 2]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); }, - _bgrxImageData: function (x, y, vx, vy, width, height, arr, offset) { + _bgrxImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { @@ -727,10 +653,11 @@ data[i + 2] = arr[j]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); }, - _rgbxImageData: function (x, y, vx, vy, width, height, arr, offset) { + _rgbxImageData: function (x, y, width, height, arr, offset) { // NB(directxman12): arr must be an Type Array view var img; if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { @@ -739,10 +666,11 @@ img = this._drawCtx.createImageData(width, height); img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4)); } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); }, - _cmapImageData: function (x, y, vx, vy, width, height, arr, offset) { + _cmapImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; var cmap = this._colourMap; @@ -753,7 +681,8 @@ data[i + 2] = bgr[0]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); }, _renderQ_push: function (action) { @@ -777,6 +706,9 @@ while (ready && this._renderQ.length > 0) { var a = this._renderQ[0]; switch (a.type) { + case 'flip': + this.flip(true); + break; case 'copy': this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true); break; @@ -820,15 +752,13 @@ Util.make_properties(Display, [ ['target', 'wo', 'dom'], // Canvas element for rendering ['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only) - ['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "data": data} + ['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data} ['true_color', 'rw', 'bool'], // Use true-color pixel data ['colourMap', 'rw', 'arr'], // Colour map array (when not true-color) ['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0 ['viewport', 'rw', 'bool'], // Use viewport clipping - ['width', 'rw', 'int'], // Display area width - ['height', 'rw', 'int'], // Display area height - ['maxWidth', 'rw', 'int'], // Viewport max width (0 if disabled) - ['maxHeight', 'rw', 'int'], // Viewport max height (0 if disabled) + ['width', 'ro', 'int'], // Display area width + ['height', 'ro', 'int'], // Display area height ['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only) diff --git a/core/rfb.js b/core/rfb.js index bc1fc61..fdb9560 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -1118,7 +1118,7 @@ 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, false, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height); + RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height); this._timing.fbu_rt_start = (new Date()).getTime(); this._timing.pixels = 0; @@ -1276,11 +1276,9 @@ switch (msg_type) { case 0: // FramebufferUpdate var ret = this._framebufferUpdate(); - if (ret) { - RFB.messages.fbUpdateRequests(this._sock, - this._enabledContinuousUpdates, - this._display.getCleanDirtyReset(), - this._fb_width, this._fb_height); + if (ret && !this._enabledContinuousUpdates) { + RFB.messages.fbUpdateRequest(this._sock, true, 0, 0, + this._fb_width, this._fb_height); } return ret; @@ -1425,6 +1423,8 @@ if (!ret) { return ret; } // need more data } + this._display.flip(); + this._onFBUComplete(this, {'x': this._FBU.x, 'y': this._FBU.y, 'width': this._FBU.width, 'height': this._FBU.height, @@ -1743,27 +1743,6 @@ sock.flush(); }, - fbUpdateRequests: function (sock, onlyNonInc, cleanDirty, fb_width, fb_height) { - var offsetIncrement = 0; - - var cb = cleanDirty.cleanBox; - var w, h; - 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 - RFB.messages.fbUpdateRequest(sock, 1, cb.x, cb.y, w, h); - } - - for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) { - var db = cleanDirty.dirtyBoxes[i]; - // Force all (non-incremental) for dirty box - w = typeof db.w === "undefined" ? fb_width : db.w; - h = typeof db.h === "undefined" ? fb_height : db.h; - RFB.messages.fbUpdateRequest(sock, 0, db.x, db.y, w, h); - } - }, - fbUpdateRequest: function (sock, incremental, x, y, w, h) { var buff = sock._sQ; var offset = sock._sQlen; @@ -1772,7 +1751,7 @@ if (typeof(y) === "undefined") { y = 0; } buff[offset] = 3; // msg-type - buff[offset + 1] = incremental; + buff[offset + 1] = incremental ? 1 : 0; buff[offset + 2] = (x >> 8) & 0xFF; buff[offset + 3] = x & 0xFF; diff --git a/tests/assertions.js b/tests/assertions.js index fa122dc..da6f0c5 100644 --- a/tests/assertions.js +++ b/tests/assertions.js @@ -2,7 +2,8 @@ chai.use(function (_chai, utils) { _chai.Assertion.addMethod('displayed', function (target_data) { var obj = this._obj; - var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data; + var ctx = obj._target.getContext('2d'); + var data_cl = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data; // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that var data = new Uint8Array(data_cl); var same = true; diff --git a/tests/playback.js b/tests/playback.js index 5c94f1e..a25fac4 100644 --- a/tests/playback.js +++ b/tests/playback.js @@ -12,13 +12,50 @@ var rfb, mode, test_state, frame_idx, frame_length, iteration, iterations, istart_time, encoding, // Pre-declarations for jslint - send_array, next_iteration, queue_next_packet, do_packet, enable_test_mode; + send_array, next_iteration, end_iteration, queue_next_packet, + do_packet, enable_test_mode; // Override send_array send_array = function (arr) { // Stub out send_array }; +// Immediate polyfill +if (window.setImmediate === undefined) { + var _immediateIdCounter = 1; + var _immediateFuncs = {}; + + window.setImmediate = function (func) { + var index = Util._immediateIdCounter++; + _immediateFuncs[index] = func; + window.postMessage("noVNC immediate trigger:" + index, "*"); + return index; + }; + + window.clearImmediate = function (id) { + _immediateFuncs[id]; + }; + + var _onMessage = function (event) { + if ((typeof event.data !== "string") || + (event.data.indexOf("noVNC immediate trigger:") !== 0)) { + return; + } + + var index = event.data.slice("noVNC immediate trigger:".length); + + var callback = _immediateFuncs[index]; + if (callback === undefined) { + return; + } + + delete _immediateFuncs[index]; + + callback(); + }; + window.addEventListener("message", _onMessage); +} + enable_test_mode = function () { rfb._sock.send = send_array; rfb._sock.close = function () {}; @@ -30,13 +67,16 @@ enable_test_mode = function () { this._rfb_password = (password !== undefined) ? password : ""; this._rfb_path = (path !== undefined) ? path : ""; this._sock.init('binary', 'ws'); - this._updateState('ProtocolVersion', "Starting VNC handshake"); + this._rfb_connection_state = 'connecting'; + this._rfb_init_state = 'ProtocolVersion'; }; }; next_iteration = function () { rfb = new RFB({'target': document.getElementById('VNC_canvas'), - 'onUpdateState': updateState}); + 'view_only': true, + 'onDisconnected': disconnected, + 'onNotification': notification}); enable_test_mode(); // Missing in older recordings @@ -73,6 +113,20 @@ next_iteration = function () { }; +end_iteration = function () { + if (rfb._display.pending()) { + rfb._display.set_onFlush(function () { + if (rfb._flushing) { + rfb._onFlush(); + } + end_iteration(); + }); + rfb._display.flush(); + } else { + next_iteration(); + } +}; + queue_next_packet = function () { var frame, foffset, toffset, delay; if (test_state !== 'running') { return; } @@ -86,12 +140,12 @@ queue_next_packet = function () { if (frame === 'EOF') { Util.Debug("Finished, found EOF"); - next_iteration(); + end_iteration(); return; } if (frame_idx >= frame_length) { Util.Debug("Finished, no more frames"); - next_iteration(); + end_iteration(); return; } @@ -105,13 +159,23 @@ queue_next_packet = function () { setTimeout(do_packet, delay); } else { - setTimeout(do_packet, 0); + window.setImmediate(do_packet); } }; var bytes_processed = 0; do_packet = function () { + // Avoid having an excessive queue buildup + if (rfb._flushing && (mode !== 'realtime')) { + rfb._display.set_onFlush(function () { + rfb._display.set_onFlush(rfb._onFlush.bind(rfb)); + rfb._onFlush(); + do_packet(); + }); + return; + } + //Util.Debug("Processing frame: " + frame_idx); var frame = VNC_frame_data[frame_idx], start = frame.indexOf('{', 1) + 1; diff --git a/tests/test.display.js b/tests/test.display.js index 3c7a28f..5f4eed1 100644 --- a/tests/test.display.js +++ b/tests/test.display.js @@ -26,6 +26,13 @@ describe('Display/Canvas Helper', function () { return canvas; } + function make_image_png (input_data) { + var canvas = make_image_canvas(input_data); + var url = canvas.toDataURL(); + var data = url.split(",")[1]; + return Base64.decode(data); + } + describe('checking for cursor uri support', function () { beforeEach(function () { this._old_browser_supports_cursor_uris = Util.browserSupportsCursorURIs; @@ -61,14 +68,13 @@ describe('Display/Canvas Helper', function () { display.resize(5, 5); display.viewportChangeSize(3, 3); display.viewportChangePos(1, 1); - display.getCleanDirtyReset(); }); it('should take viewport location into consideration when drawing images', function () { - display.set_width(4); - display.set_height(4); + display.resize(4, 4); display.viewportChangeSize(2, 2); display.drawImage(make_image_canvas(basic_data), 1, 1); + display.flip(); var expected = new Uint8Array(16); var i; @@ -77,96 +83,82 @@ describe('Display/Canvas Helper', function () { expect(display).to.have.displayed(expected); }); - it('should redraw the left side when shifted left', function () { - display.viewportChangePos(-1, 0); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 }); - }); - - it('should redraw the right side when shifted right', function () { - display.viewportChangePos(1, 0); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 }); + it('should resize the target canvas when resizing the viewport', function() { + display.viewportChangeSize(2, 2); + expect(display._target.width).to.equal(2); + expect(display._target.height).to.equal(2); }); - it('should redraw the top part when shifted up', function () { - display.viewportChangePos(0, -1); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 }); + it('should move the viewport if necessary', function() { + display.viewportChangeSize(5, 5); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(5); + expect(display._target.height).to.equal(5); }); - it('should redraw the bottom part when shifted down', function () { - display.viewportChangePos(0, 1); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 }); + it('should limit the viewport to the framebuffer size', function() { + display.viewportChangeSize(6, 6); + expect(display._target.width).to.equal(5); + expect(display._target.height).to.equal(5); }); - it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () { - display.viewportChangePos(0, 1); - var cdr1 = display.getCleanDirtyReset(); - var cdr2 = display.getCleanDirtyReset(); - expect(cdr1).to.not.deep.equal(cdr2); - expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 }); - expect(cdr2.dirtyBoxes).to.be.empty; + it('should redraw when moving the viewport', function () { + display.flip = sinon.spy(); + display.viewportChangePos(-1, 1); + expect(display.flip).to.have.been.calledOnce; }); - it('should simply mark the whole display area as dirty if not using viewports', function () { - display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false }); - display.resize(5, 5); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 }); - }); - }); - - describe('clipping', function () { - var display; - beforeEach(function () { - display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); - display.resize(4, 3); + it('should redraw when resizing the viewport', function () { + display.flip = sinon.spy(); + display.viewportChangeSize(2, 2); + expect(display.flip).to.have.been.calledOnce; }); - it('should report true when no max-size and framebuffer > viewport', function () { - display.viewportChangeSize(2,2); + it('should report clipping when framebuffer > viewport', function () { var clipping = display.clippingDisplay(); expect(clipping).to.be.true; }); - it('should report false when no max-size and framebuffer = viewport', function () { + it('should report not clipping when framebuffer = viewport', function () { + display.viewportChangeSize(5, 5); var clipping = display.clippingDisplay(); expect(clipping).to.be.false; }); - it('should report true when viewport > max-size and framebuffer > viewport', function () { - display.viewportChangeSize(2,2); - display.set_maxWidth(1); - display.set_maxHeight(2); - var clipping = display.clippingDisplay(); - expect(clipping).to.be.true; + it('should show the entire framebuffer when disabling the viewport', function() { + display.set_viewport(false); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(5); + expect(display._target.height).to.equal(5); }); - it('should report true when viewport > max-size and framebuffer = viewport', function () { - display.set_maxWidth(1); - display.set_maxHeight(2); - var clipping = display.clippingDisplay(); - expect(clipping).to.be.true; + it('should ignore viewport changes when the viewport is disabled', function() { + display.set_viewport(false); + display.viewportChangeSize(2, 2); + display.viewportChangePos(1, 1); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(5); + expect(display._target.height).to.equal(5); + }); + + it('should show the entire framebuffer just after enabling the viewport', function() { + display.set_viewport(false); + display.set_viewport(true); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(5); + expect(display._target.height).to.equal(5); }); }); describe('resizing', function () { var display; beforeEach(function () { - display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); - display.resize(4, 3); + display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false }); + display.resize(4, 4); }); it('should change the size of the logical canvas', function () { @@ -175,10 +167,49 @@ describe('Display/Canvas Helper', function () { expect(display._fb_height).to.equal(7); }); - it('should update the viewport dimensions', function () { - sinon.spy(display, 'viewportChangeSize'); + it('should keep the framebuffer data', function () { + display.fillRect(0, 0, 4, 4, [0, 0, 0xff]); display.resize(2, 2); - expect(display.viewportChangeSize).to.have.been.calledOnce; + display.flip(); + var expected = []; + for (var i = 0; i < 4 * 2*2; i += 4) { + expected[i] = 0xff; + expected[i+1] = expected[i+2] = 0; + expected[i+3] = 0xff; + } + expect(display).to.have.displayed(new Uint8Array(expected)); + }); + + describe('viewport', function () { + beforeEach(function () { + display.set_viewport(true); + display.viewportChangeSize(3, 3); + display.viewportChangePos(1, 1); + }); + + it('should keep the viewport position and size if possible', function () { + display.resize(6, 6); + expect(display.absX(0)).to.equal(1); + expect(display.absY(0)).to.equal(1); + expect(display._target.width).to.equal(3); + expect(display._target.height).to.equal(3); + }); + + it('should move the viewport if necessary', function () { + display.resize(3, 3); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(3); + expect(display._target.height).to.equal(3); + }); + + it('should shrink the viewport if necessary', function () { + display.resize(2, 2); + expect(display.absX(0)).to.equal(0); + expect(display.absY(0)).to.equal(0); + expect(display._target.width).to.equal(2); + expect(display._target.height).to.equal(2); + }); }); }); @@ -188,7 +219,9 @@ describe('Display/Canvas Helper', function () { beforeEach(function () { display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); - display.resize(4, 3); + display.resize(4, 4); + display.viewportChangeSize(3, 3); + display.viewportChangePos(1, 1); canvas = display.get_target(); document.body.appendChild(canvas); }); @@ -198,15 +231,25 @@ describe('Display/Canvas Helper', function () { }); it('should not change the bitmap size of the canvas', function () { - display.set_scale(0.5); - expect(canvas.width).to.equal(4); + display.set_scale(2.0); + expect(canvas.width).to.equal(3); expect(canvas.height).to.equal(3); }); it('should change the effective rendered size of the canvas', function () { - display.set_scale(0.5); - expect(canvas.clientWidth).to.equal(2); - expect(canvas.clientHeight).to.equal(2); + display.set_scale(2.0); + expect(canvas.clientWidth).to.equal(6); + expect(canvas.clientHeight).to.equal(6); + }); + + it('should not change when resizing', function () { + display.set_scale(2.0); + display.resize(5, 5); + expect(display.get_scale()).to.equal(2.0); + expect(canvas.width).to.equal(3); + expect(canvas.height).to.equal(3); + expect(canvas.clientWidth).to.equal(6); + expect(canvas.clientHeight).to.equal(6); }); }); @@ -282,22 +325,35 @@ describe('Display/Canvas Helper', function () { }); it('should draw the logo on #clear with a logo set', function (done) { - display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() }; - display._drawCtx._act_drawImg = display._drawCtx.drawImage; - display._drawCtx.drawImage = function (img, x, y) { - this._act_drawImg(img, x, y); + display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) }; + display.clear(); + display.set_onFlush(function () { expect(display).to.have.displayed(checked_data); + expect(display._fb_width).to.equal(4); + expect(display._fb_height).to.equal(4); done(); - }; - display.clear(); - expect(display._fb_width).to.equal(4); - expect(display._fb_height).to.equal(4); + }); + display.flush(); + }); + + it('should not draw directly on the target canvas', function () { + display.fillRect(0, 0, 4, 4, [0, 0, 0xff]); + display.flip(); + display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); + var expected = []; + for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) { + expected[i] = 0xff; + expected[i+1] = expected[i+2] = 0; + expected[i+3] = 0xff; + } + expect(display).to.have.displayed(new Uint8Array(expected)); }); it('should support filling a rectangle with particular color via #fillRect', function () { display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); display.fillRect(0, 0, 2, 2, [0xff, 0, 0]); display.fillRect(2, 2, 2, 2, [0xff, 0, 0]); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -305,14 +361,26 @@ describe('Display/Canvas Helper', function () { display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]); display.copyImage(0, 0, 2, 2, 2, 2); + display.flip(); expect(display).to.have.displayed(checked_data); }); + it('should support drawing images via #imageRect', function (done) { + display.imageRect(0, 0, "image/png", make_image_png(checked_data)); + display.flip(); + display.set_onFlush(function () { + expect(display).to.have.displayed(checked_data); + done(); + }); + display.flush(); + }); + it('should support drawing tile data with a background color and sub tiles', function () { display.startTile(0, 0, 4, 4, [0, 0xff, 0]); display.subTile(0, 0, 2, 2, [0xff, 0, 0]); display.subTile(2, 2, 2, 2, [0xff, 0, 0]); display.finishTile(); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -325,6 +393,7 @@ describe('Display/Canvas Helper', function () { data[i * 4 + 3] = checked_data[i * 4 + 3]; } display.blitImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -336,26 +405,17 @@ describe('Display/Canvas Helper', function () { data[i * 3 + 2] = checked_data[i * 4 + 2]; } display.blitRgbImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); - it('should support drawing blit images from a data URL via #blitStringImage', function (done) { - var img_url = make_image_canvas(checked_data).toDataURL(); - display._drawCtx._act_drawImg = display._drawCtx.drawImage; - display._drawCtx.drawImage = function (img, x, y) { - this._act_drawImg(img, x, y); - expect(display).to.have.displayed(checked_data); - done(); - }; - display.blitStringImage(img_url, 0, 0); - }); - it('should support drawing solid colors with color maps', function () { display._true_color = false; display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] }); display.fillRect(0, 0, 4, 4, 1); display.fillRect(0, 0, 2, 2, 0); display.fillRect(2, 2, 2, 2, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -364,12 +424,14 @@ describe('Display/Canvas Helper', function () { display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] }); var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; }); display.blitImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); it('should support drawing an image object via #drawImage', function () { var img = make_image_canvas(checked_data); display.drawImage(img, 0, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); } @@ -420,6 +482,14 @@ describe('Display/Canvas Helper', function () { expect(img.addEventListener).to.have.been.calledOnce; }); + it('should call callback when queue is flushed', function () { + display.set_onFlush(sinon.spy()); + display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); + expect(display.get_onFlush()).to.not.have.been.called; + display.flush(); + expect(display.get_onFlush()).to.have.been.calledOnce; + }); + it('should draw a blit image on type "blit"', function () { display.blitImage = sinon.spy(); display._renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index ae51bff..229cfe5 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1274,7 +1274,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should send an update request if there is sufficient data', function () { var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}}; - RFB.messages.fbUpdateRequest(expected_msg, false, 0, 0, 240, 20); + RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20); client._framebufferUpdate = function () { return true; }; client._sock._websocket._receive_data(new Uint8Array([0])); @@ -1289,7 +1289,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should resume receiving an update if we previously did not have enough data', function () { var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}}; - RFB.messages.fbUpdateRequest(expected_msg, false, 0, 0, 240, 20); + RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20); // just enough to set FBU.rects client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3])); @@ -1301,43 +1301,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._sock).to.have.sent(expected_msg._sQ); }); - it('should send a request for both clean and dirty areas', function () { - var expected_msg = {_sQ: new Uint8Array(20), _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, true, 0, 0, 120, 20); - RFB.messages.fbUpdateRequest(expected_msg, false, 120, 0, 120, 20); - - 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 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: [] }; - + it('should not send a request in continuous updates mode', function () { 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); @@ -1389,14 +1355,11 @@ describe('Remote Frame Buffer Protocol Client', function() { client._fb_width = 4; client._fb_height = 4; client._display.resize(4, 4); - var initial_data = client._display._drawCtx.createImageData(4, 2); - var initial_data_arr = target_data_check_arr.slice(0, 32); - for (var i = 0; i < 32; i++) { initial_data.data[i] = initial_data_arr[i]; } - client._display._drawCtx.putImageData(initial_data, 0, 0); + client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0); var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01}, { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}]; - // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }] + // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }] var rects = [[0, 2, 0, 0], [0, 0, 0, 0]]; send_fbu_msg([info[0]], [rects[0]], client, 2); send_fbu_msg([info[1]], [rects[1]], client, -1); @@ -1415,10 +1378,7 @@ describe('Remote Frame Buffer Protocol Client', function() { // a really small frame client._fb_width = 4; client._fb_height = 4; - client._display._fb_width = 4; - client._display._fb_height = 4; - client._display._viewportLoc.w = 4; - client._display._viewportLoc.h = 4; + client._display.resize(4, 4); client._fb_Bpp = 4; }); @@ -1439,10 +1399,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should handle the COPYRECT encoding', function () { // seed some initial data to copy - var initial_data = client._display._drawCtx.createImageData(4, 2); - var initial_data_arr = target_data_check_arr.slice(0, 32); - for (var i = 0; i < 32; i++) { initial_data.data[i] = initial_data_arr[i]; } - client._display._drawCtx.putImageData(initial_data, 0, 0); + client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0); var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01}, { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}]; @@ -1492,10 +1449,7 @@ describe('Remote Frame Buffer Protocol Client', function() { // a really small frame client._fb_width = 4; client._fb_height = 4; - client._display._fb_width = 4; - client._display._fb_height = 4; - client._display._viewportLoc.w = 4; - client._display._viewportLoc.h = 4; + client._display.resize(4, 4); client._fb_Bpp = 4; }); @@ -1546,8 +1500,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should handle a tile with only bg specified and an empty frame afterwards', function () { // set the width so we can have two tiles client._fb_width = 8; - client._display._fb_width = 8; - client._display._viewportLoc.w = 8; + client._display.resize(8, 4); var info = [{ x: 0, y: 0, width: 32, height: 4, encoding: 0x05 }]; @@ -1670,10 +1623,7 @@ describe('Remote Frame Buffer Protocol Client', function() { // a really small frame client._fb_width = 4; client._fb_height = 4; - client._display._fb_width = 4; - client._display._fb_height = 4; - client._display._viewportLoc.w = 4; - client._display._viewportLoc.h = 4; + client._display.resize(4, 4); client._fb_Bpp = 4; sinon.spy(client._display, 'resize'); client.set_onFBResize(sinon.spy()); diff --git a/tests/vnc_perf.html b/tests/vnc_perf.html index 03f4f35..c3e6a11 100644 --- a/tests/vnc_perf.html +++ b/tests/vnc_perf.html @@ -74,25 +74,20 @@ } } - updateState = function (rfb, state, oldstate, mesg) { - switch (state) { - case 'failed': - case 'fatal': - msg("noVNC sent '" + state + - "' state during pass " + pass + - ", iteration " + iteration + - " frame " + frame_idx); - test_state = 'failed'; - break; - case 'loaded': - document.getElementById('startButton').disabled = false; - break; - } - if (typeof mesg !== 'undefined') { - document.getElementById('VNC_status').innerHTML = mesg; + disconnected = function (rfb, reason) { + if (reason) { + msg("noVNC sent '" + state + + "' state during pass " + pass + + ", iteration " + iteration + + " frame " + frame_idx); + test_state = 'failed'; } } + notification = function (rfb, mesg, level, options) { + document.getElementById('VNC_status').innerHTML = mesg; + } + function do_test() { document.getElementById('startButton').value = "Running"; document.getElementById('startButton').disabled = true; diff --git a/tests/vnc_playback.html b/tests/vnc_playback.html index edfcacc..510ad06 100644 --- a/tests/vnc_playback.html +++ b/tests/vnc_playback.html @@ -68,21 +68,17 @@ message("Must specify data=FOO in query string."); } - updateState = function (rfb, state, oldstate, msg) { - switch (state) { - case 'failed': - case 'fatal': - message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx); - test_state = 'failed'; - break; - case 'loaded': - break; - } - if (typeof msg !== 'undefined') { - document.getElementById('VNC_status').innerHTML = msg; + disconnected = function (rfb, reason) { + if (reason) { + message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx); + test_state = 'failed'; } } + notification = function (rfb, mesg, level, options) { + document.getElementById('VNC_status').innerHTML = mesg; + } + function start() { document.getElementById('startButton').value = "Running"; document.getElementById('startButton').disabled = true; |