diff options
author | Solly <directxman12+github@gmail.com> | 2015-03-10 17:13:21 -0400 |
---|---|---|
committer | Solly <directxman12+github@gmail.com> | 2015-03-10 17:13:21 -0400 |
commit | cefc9a917763c6548934c18a09cb4be98f6cddcf (patch) | |
tree | 383ae9111821013499f137e645f4ae0cc8e993d8 | |
parent | 3b8ec46fd26d644e6edbea4f46e630929297e448 (diff) | |
parent | fdedbafb1de63c8c704db5396fbb15a4187d9c62 (diff) | |
download | novnc-cefc9a917763c6548934c18a09cb4be98f6cddcf.tar.gz |
Merge pull request #464 from kanaka/bug/firefoxresize
Fix resize in Firefox on Android
-rw-r--r-- | include/base.css | 22 | ||||
-rw-r--r-- | include/display.js | 98 | ||||
-rw-r--r-- | include/rfb.js | 99 | ||||
-rw-r--r-- | include/ui.js | 147 | ||||
-rw-r--r-- | tests/test.display.js | 34 | ||||
-rw-r--r-- | tests/test.rfb.js | 2 | ||||
-rw-r--r-- | vnc.html | 4 |
7 files changed, 256 insertions, 150 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/rfb.js b/include/rfb.js index 909112d..6fcdab6 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -37,24 +37,24 @@ var RFB; // In preference order this._encodings = [ - ['COPYRECT', 0x01 ], - ['TIGHT', 0x07 ], - ['TIGHT_PNG', -260 ], - ['HEXTILE', 0x05 ], - ['RRE', 0x02 ], - ['RAW', 0x00 ], - ['DesktopSize', -223 ], - ['Cursor', -239 ], + ['COPYRECT', 0x01 ], + ['TIGHT', 0x07 ], + ['TIGHT_PNG', -260 ], + ['HEXTILE', 0x05 ], + ['RRE', 0x02 ], + ['RAW', 0x00 ], + ['DesktopSize', -223 ], + ['Cursor', -239 ], // Psuedo-encoding settings - //['JPEG_quality_lo', -32 ], - ['JPEG_quality_med', -26 ], - //['JPEG_quality_hi', -23 ], - //['compress_lo', -255 ], - ['compress_hi', -247 ], - ['last_rect', -224 ], - ['xvp', -309 ], - ['ext_desktop_size', -308 ] + //['JPEG_quality_lo', -32 ], + ['JPEG_quality_med', -26 ], + //['JPEG_quality_hi', -23 ], + //['compress_lo', -255 ], + ['compress_hi', -247 ], + ['last_rect', -224 ], + ['xvp', -309 ], + ['ExtendedDesktopSize', -308 ] ]; this._encHandlers = {}; @@ -1871,15 +1871,27 @@ var RFB; return true; }, - ext_desktop_size: function () { + handle_FB_resize: function () { + this._fb_width = this._FBU.width; + this._fb_height = this._FBU.height; + 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._FBU.bytes = 0; + this._FBU.rects -= 1; + return true; + }, + + ExtendedDesktopSize: function () { this._FBU.bytes = 1; - if (this._sock.rQwait("ext_desktop_size", this._FBU.bytes)) { return false; } + if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; } this._supportsSetDesktopSize = true; var number_of_screens = this._sock.rQpeek8(); this._FBU.bytes = 4 + (number_of_screens * 16); - if (this._sock.rQwait("ext_desktop_size", this._FBU.bytes)) { return false; } + if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; } this._sock.rQskipBytes(1); // number-of-screens this._sock.rQskipBytes(3); // padding @@ -1898,31 +1910,42 @@ var RFB; } } - if (this._FBU.x == 0 && this._FBU.y != 0) { return true; } - - this._fb_width = this._FBU.width; - this._fb_height = this._FBU.height; - this._display.resize(this._fb_width, this._fb_height); - this._onFBResize(this, this._fb_width, this._fb_height); + /* + * The x-position indicates the reason for the change: + * + * 0 - server resized on its own + * 1 - this client requested the resize + * 2 - another client requested the resize + */ - this._FBU.bytes = 0; - this._FBU.rects -= 1; + // We need to handle errors when we requested the resize. + if (this._FBU.x == 1 && this._FBU.y != 0) { + var msg = ""; + // The y-position indicates the status code from the server + switch (this._FBU.y) { + case 1: + msg = "Resize is administratively prohibited"; + break; + case 2: + msg = "Out of resources"; + break; + case 3: + msg = "Invalid screen layout"; + break; + default: + msg = "Unknown reason"; + break; + } + Util.Info("Server did not accept the resize request: " + msg); + return true; + } + this._encHandlers.handle_FB_resize(); return true; }, DesktopSize: function () { - Util.Debug(">> set_desktopsize"); - this._fb_width = this._FBU.width; - this._fb_height = this._FBU.height; - 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._FBU.bytes = 0; - this._FBU.rects--; - - Util.Debug("<< set_desktopsize"); + this._encHandlers.handle_FB_resize(); return true; }, 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 () { diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 444e42c..006b5fa 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1584,7 +1584,7 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should not handle a failed request', function () { - var reason_for_change = 0; // non-incremental + var reason_for_change = 1; // requested by this client var status_code = 1; // Resize is administratively prohibited send_fbu_msg([{ x: reason_for_change, y: status_code, @@ -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> |