diff options
author | samhed <samuel@cendio.se> | 2015-03-09 14:30:56 +0100 |
---|---|---|
committer | samhed <samuel@cendio.se> | 2015-03-09 14:30:56 +0100 |
commit | fdedbafb1de63c8c704db5396fbb15a4187d9c62 (patch) | |
tree | 383ae9111821013499f137e645f4ae0cc8e993d8 | |
parent | 798340b98d097afd7e7cd949d9256dcf2a74eef4 (diff) | |
download | novnc-fdedbafb1de63c8c704db5396fbb15a4187d9c62.tar.gz |
* Don't check specific html elements from the display code (Fixes #446)
* Renamed and reworked fbuClip to clippingDisplay
* Added tests for clippingDisplay
* Use the a noVNC_container which covers the entire page to get the full size
(Fixes #463)
* Added maxWidth and maxHeight to the canvas which can limit the viewport size
* Only show either the canvas or the logo, hide one when the other is shown
* Always center the canvas (previously it was only centered when not clipping)
* Removed iOS specific "position-fixed" fixes and start calling setBarPosition
on every resize
* Removed the noVNC_screen_pad
-rw-r--r-- | include/base.css | 22 | ||||
-rw-r--r-- | include/display.js | 98 | ||||
-rw-r--r-- | include/ui.js | 147 | ||||
-rw-r--r-- | tests/test.display.js | 34 | ||||
-rw-r--r-- | vnc.html | 4 |
5 files changed, 194 insertions, 111 deletions
diff --git a/include/base.css b/include/base.css index e2c9a96..478b4d0 100644 --- a/include/base.css +++ b/include/base.css @@ -112,13 +112,7 @@ html { /* Do not set width/height for VNC_screen or VNC_canvas or incorrect * scaling will occur. Canvas resizes to remote VNC settings */ -#noVNC_screen_pad { - margin: 0px; - padding: 0px; - height: 36px; -} #noVNC_screen { - text-align: center; display: table; width:100%; height:100%; @@ -127,13 +121,25 @@ html { /*border-top-left-radius: 800px 600px;*/ } -#noVNC_container, #noVNC_canvas { +#noVNC_container { + display: none; + position: absolute; margin: 0px; padding: 0px; + bottom: 0px; + top: 36px; /* the height of the control bar */ + left: 0px; + right: 0px; + width: auto; + height: auto; } #noVNC_canvas { - left: 0px; + position: absolute; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; } #VNC_clipboard_clear_button { diff --git a/include/display.js b/include/display.js index 2b1b827..d10d9b9 100644 --- a/include/display.js +++ b/include/display.js @@ -1,6 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin + * Copyright (C) 2015 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -24,6 +25,10 @@ var Display; 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 }; @@ -202,8 +207,7 @@ var Display; viewportChangeSize: function(width, height) { - if (!this._viewport || - typeof(width) === "undefined" || typeof(height) === "undefined") { + if (typeof(width) === "undefined" || typeof(height) === "undefined") { Util.Debug("Setting viewport to full display region"); width = this._fb_width; @@ -213,41 +217,49 @@ var Display; 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; } - if (this.fbuClip()) { - // clipping - vp.w = window.innerWidth; - var cb = document.getElementById('noVNC-control-bar'); - var controlbar_h = (cb !== null) ? cb.offsetHeight : 0; - vp.h = window.innerHeight - controlbar_h - 5; - } else { - // scrollbars - vp.w = width; - vp.h = height; - } + vp.w = width; + vp.h = height; - var saveImg = null; var canvas = this._target; - 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.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; } + if (canvas.height !== height) { canvas.height = height; } - canvas.width = vp.w; - canvas.height = vp.h; + if (this._viewport) { + canvas.style.height = height + 'px'; + canvas.style.width = width + 'px'; + } - if (saveImg) { - this._drawCtx.putImageData(saveImg, 0, 0); + if (saveImg) { + this._drawCtx.putImageData(saveImg, 0, 0); + } } } }, @@ -487,12 +499,18 @@ var Display; this._target.style.cursor = "none"; }, - fbuClip: function () { - var cb = document.getElementById('noVNC-control-bar'); - var controlbar_h = (cb !== null) ? cb.offsetHeight : 0; - return (this._viewport && - (this._fb_width > window.innerWidth - || this._fb_height > window.innerHeight - controlbar_h - 5)); + 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); }, // Overridden getters/setters @@ -558,8 +576,20 @@ var Display; _rescale: function (factor) { this._scale = factor; - this._target.style.width = Math.round(factor * this._fb_width) + 'px'; - this._target.style.height = Math.round(factor * this._fb_height) + 'px'; + 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'; }, _setFillColor: function (color) { @@ -661,9 +691,11 @@ var Display; ['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 a viewport set with viewportChange() + ['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) ['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only) diff --git a/include/ui.js b/include/ui.js index fb28e3e..bcc0a94 100644 --- a/include/ui.js +++ b/include/ui.js @@ -1,7 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin - * Copyright (C) 2013 Samuel Mannehed for Cendio AB + * Copyright (C) 2015 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -45,33 +45,6 @@ var UI; WebUtil.initSettings(UI.start, callback); }, - onresize: function (callback) { - var innerW = window.innerWidth; - var innerH = window.innerHeight; - var controlbarH = $D('noVNC-control-bar').offsetHeight; - // For some unknown reason the container is higher than the canvas, - // 5px higher in Firefox and 4px higher in Chrome - var padding = 5; - var effectiveH = innerH - controlbarH - padding; - - var display = UI.rfb.get_display(); - - if (innerW !== undefined && innerH !== undefined) { - var scaleType = UI.getSetting('resize'); - if (scaleType === 'remote') { - // use remote resizing - Util.Debug('Attempting setDesktopSize(' + innerW + ', ' + effectiveH + ')'); - UI.rfb.setDesktopSize(innerW, effectiveH); - } else if (scaleType === 'scale' || scaleType === 'downscale') { - // use local scaling - var downscaleOnly = scaleType === 'downscale'; - var scaleRatio = display.autoscale(innerW, effectiveH, downscaleOnly); - UI.rfb.get_mouse().set_scale(scaleRatio); - Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale()); - } - } - }, - // Render default UI and initialize settings menu start: function(callback) { UI.isTouchDevice = 'ontouchstart' in document.documentElement; @@ -136,6 +109,8 @@ var UI; UI.updateVisualState(); + $D('noVNC_host').focus(); + // Show mouse selector buttons on touch screen devices if (UI.isTouchDevice) { // Show mobile buttons @@ -148,29 +123,14 @@ var UI; UI.initSetting('clip', false); } - //iOS Safari does not support CSS position:fixed. - //This detects iOS devices and enables javascript workaround. - if ((navigator.userAgent.match(/iPhone/i)) || - (navigator.userAgent.match(/iPod/i)) || - (navigator.userAgent.match(/iPad/i))) { - //UI.setOnscroll(); - //UI.setResize(); - } - UI.setBarPosition(); - - $D('noVNC_host').focus(); - UI.setViewClip(); + UI.setBarPosition(); Util.addEvent(window, 'resize', function () { + UI.onresize(); UI.setViewClip(); - // When the window has been resized, wait until the size remains - // the same for 0.5 seconds before sending the request for changing - // the resolution of the session - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(function(){ - UI.onresize(); - }, 500); + UI.updateViewDragButton(); + UI.setBarPosition(); } ); Util.addEvent(window, 'load', UI.keyboardinputReset); @@ -258,6 +218,55 @@ var UI; }; }, + onresize: function (callback) { + var size = UI.getCanvasLimit(); + + if (size && UI.rfb_state === 'normal' && UI.rfb.get_display()) { + var display = UI.rfb.get_display(); + var scaleType = UI.getSetting('resize'); + if (scaleType === 'remote') { + // use remote resizing + + // When the local window has been resized, wait until the size remains + // the same for 0.5 seconds before sending the request for changing + // the resolution of the session + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(function(){ + display.set_maxWidth(size.w); + display.set_maxHeight(size.h); + Util.Debug('Attempting setDesktopSize(' + + size.w + ', ' + size.h + ')'); + UI.rfb.setDesktopSize(size.w, size.h); + }, 500); + } else if (scaleType === 'scale' || scaleType === 'downscale') { + // use local scaling + + var downscaleOnly = scaleType === 'downscale'; + var scaleRatio = display.autoscale(size.w, size.h, downscaleOnly); + UI.rfb.get_mouse().set_scale(scaleRatio); + Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale()); + } + } + }, + + getCanvasLimit: function () { + var container = $D('noVNC_container'); + + // Hide the scrollbars until the size is calculated + container.style.overflow = "hidden"; + + var w = Util.getPosition(container).width; + var h = Util.getPosition(container).height; + + container.style.overflow = "visible"; + + if (isNaN(w) || isNaN(h)) { + return false; + } else { + return {w: w, h: h}; + } + }, + // Read form control compatible setting from cookie getSetting: function(name) { var ctrl = $D('noVNC_' + name); @@ -613,6 +622,7 @@ var UI; break; case 'disconnected': $D('noVNC_logo').style.display = "block"; + $D('noVNC_container').style.display = "none"; /* falls through */ case 'loaded': klass = "noVNC_status_normal"; @@ -781,6 +791,7 @@ var UI; //Close dialog. setTimeout(UI.setBarPosition, 100); $D('noVNC_logo').style.display = "none"; + $D('noVNC_container').style.display = "inline"; }, disconnect: function() { @@ -791,6 +802,8 @@ var UI; UI.rfb.set_onFBUComplete(UI.FBUComplete); $D('noVNC_logo').style.display = "block"; + $D('noVNC_container').style.display = "none"; + // Don't display the connection settings until we're actually disconnected }, @@ -839,17 +852,30 @@ var UI; // Turn clipping off UI.updateSetting('clip', false); display.set_viewport(false); - $D('noVNC_canvas').style.position = 'static'; + display.set_maxWidth(0); + display.set_maxHeight(0); display.viewportChangeSize(); } if (UI.getSetting('clip')) { // If clipping, update clipping settings - $D('noVNC_canvas').style.position = 'absolute'; - var pos = Util.getPosition($D('noVNC_canvas')); - var new_w = window.innerWidth - pos.x; - var new_h = window.innerHeight - pos.y; display.set_viewport(true); - display.viewportChangeSize(new_w, new_h); + + var size = UI.getCanvasLimit(); + if (size) { + display.set_maxWidth(size.w); + display.set_maxHeight(size.h); + + // Hide potential scrollbars that can skew the position + $D('noVNC_container').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($D('noVNC_canvas')).x); + + $D('noVNC_container').style.overflow = "visible"; + + display.viewportChangeSize(new_w, size.h); + } } }, @@ -878,7 +904,7 @@ var UI; var vmb = $D('noVNC_view_drag_button'); if (UI.rfb_state === 'normal' && UI.rfb.get_display().get_viewport() && - UI.rfb.get_display().fbuClip()) { + UI.rfb.get_display().clippingDisplay()) { vmb.style.display = "inline"; } else { vmb.style.display = "none"; @@ -1058,19 +1084,6 @@ var UI; UI.keyboardVisible = false; }, - // iOS < Version 5 does not support position fixed. Javascript workaround: - setOnscroll: function() { - window.onscroll = function() { - UI.setBarPosition(); - }; - }, - - setResize: function () { - window.onResize = function() { - UI.setBarPosition(); - }; - }, - //Helper to add options to dropdown. addOption: function(selectbox, text, value) { var optn = document.createElement("OPTION"); diff --git a/tests/test.display.js b/tests/test.display.js index f122dca..d54cb82 100644 --- a/tests/test.display.js +++ b/tests/test.display.js @@ -134,6 +134,40 @@ describe('Display/Canvas Helper', function () { }); }); + 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 report true when no max-size and framebuffer > viewport', function () { + display.viewportChangeSize(2,2); + var clipping = display.clippingDisplay(); + expect(clipping).to.be.true; + }); + + it('should report false when no max-size and framebuffer = viewport', function () { + 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 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; + }); + }); + describe('resizing', function () { var display; beforeEach(function () { @@ -203,13 +203,11 @@ <div id="noVNC_screen"> - <div id="noVNC_screen_pad"></div> - <h1 id="noVNC_logo"><span>no</span><br />VNC</h1> <!-- HTML5 Canvas --> <div id="noVNC_container"> - <canvas id="noVNC_canvas" width="640px" height="20px"> + <canvas id="noVNC_canvas" width="0" height="0"> Canvas not supported. </canvas> </div> |