* 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
This commit is contained in:
parent
798340b98d
commit
fdedbafb1d
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
||||
canvas.width = vp.w;
|
||||
canvas.height = vp.h;
|
||||
// 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 (saveImg) {
|
||||
this._drawCtx.putImageData(saveImg, 0, 0);
|
||||
if (canvas.width !== width) { canvas.width = width; }
|
||||
if (canvas.height !== height) { canvas.height = height; }
|
||||
|
||||
if (this._viewport) {
|
||||
canvas.style.height = height + 'px';
|
||||
canvas.style.width = width + 'px';
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
147
include/ui.js
147
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.setViewClip();
|
||||
UI.setBarPosition();
|
||||
|
||||
$D('noVNC_host').focus();
|
||||
|
||||
UI.setViewClip();
|
||||
|
||||
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");
|
||||
|
|
|
@ -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 () {
|
||||
|
|
4
vnc.html
4
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>
|
||||
|
|
Loading…
Reference in New Issue