summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolly <directxman12+github@gmail.com>2015-03-10 17:13:21 -0400
committerSolly <directxman12+github@gmail.com>2015-03-10 17:13:21 -0400
commitcefc9a917763c6548934c18a09cb4be98f6cddcf (patch)
tree383ae9111821013499f137e645f4ae0cc8e993d8
parent3b8ec46fd26d644e6edbea4f46e630929297e448 (diff)
parentfdedbafb1de63c8c704db5396fbb15a4187d9c62 (diff)
downloadnovnc-cefc9a917763c6548934c18a09cb4be98f6cddcf.tar.gz
Merge pull request #464 from kanaka/bug/firefoxresize
Fix resize in Firefox on Android
-rw-r--r--include/base.css22
-rw-r--r--include/display.js98
-rw-r--r--include/rfb.js99
-rw-r--r--include/ui.js147
-rw-r--r--tests/test.display.js34
-rw-r--r--tests/test.rfb.js2
-rw-r--r--vnc.html4
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,
diff --git a/vnc.html b/vnc.html
index faa4e33..b8bda05 100644
--- a/vnc.html
+++ b/vnc.html
@@ -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>