This commit is contained in:
GitHub Merge Button 2011-09-23 10:36:41 -07:00
commit 83a86dfa28
5 changed files with 233 additions and 51 deletions

View File

@ -23,7 +23,7 @@ var that = {}, // Public API methods
// Predefine function variables (jslint)
imageDataCreate, imageDataGet, rgbxImageData, cmapImageData,
rgbxImageFill, cmapImageFill, setFillColor, rescale, flush,
rgbxImageFill, cmapImageFill, setFillColor, rescale, rescaleAuto, flush,
// The full frame buffer (logical canvas) size
fb_width = 0,
@ -232,13 +232,52 @@ rescale = function(factor) {
if (conf.scale === factor) {
//Util.Debug("Display already scaled to '" + factor + "'");
UI.message("Display already scaled to '" + factor + "'");
return;
}
conf.scale = factor;
x = c.width - c.width * factor;
y = c.height - c.height * factor;
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
UI.message("scale: "+conf.scale+", x: "+x+", y: "+y);
};
rescaleAuto = function() {
var c, tp, x, y, xFactor, yFactor, factor,
properties = ['transform', 'WebkitTransform', 'MozTransform', null];
c = conf.target;
tp = properties.shift();
while (tp) {
if (typeof c.style[tp] !== 'undefined') {
break;
}
tp = properties.shift();
}
if (tp === null) {
Util.Debug("No scaling support");
return;
}
xFactor=viewport.w/fb_width;
yFactor=viewport.h/fb_height;
UI.message("xFactor: "+xFactor+", yFactor: "+yFactor+", viewport.h: "+viewport.h+", fb_height: "+fb_height);
if (xFactor > 1.0) {xFactor = 1.0;}
else if (xFactor < 0.1) {xFactor = 0.1;}
if (yFactor > 1.0) {yFactor = 1.0;}
else if (yFactor < 0.1) {yFactor = 0.1;}
/* Rescale screen to whichever factor that is smaller (so we get to see the whole screen). */
factor=xFactor<yFactor?xFactor:yFactor;
factor-=0.01;
c.style[tp] = "scale(" + factor + ")";
c.style[tp+'Origin'] = "top left";
UI.rfb.get_mouse().set_scale(factor);
};
that.viewportChange = function(deltaX, deltaY, width, height) {
@ -439,7 +478,6 @@ setFillColor = function(color) {
//
// Public API interface functions
//
that.resize = function(width, height) {
c_prevStyle = "";
@ -450,8 +488,65 @@ that.resize = function(width, height) {
that.viewportChange();
};
that.clear = function() {
that.resizeAuto = function(canvas) {
var c=canvas;
var v = viewport,
cw=window.innerWidth,
ch=window.innerHeight;
UI.message("container: " + cw + "," + ch);
if (cw > fb_width) {
cw = fb_width;
}
if (ch > fb_height) {
ch = fb_height;
}
if ((cw !== v.w) || (ch !== v.h)) {
v.w = cw;
v.h = ch;
UI.message("new viewport: " + v.w + "," + v.h);
c = conf.target;
c.width = v.w;
c.height = v.h;
rescaleAuto();
that.refresh();
}
UI.message("framebuffer: "+fb_width+","+fb_height+"; viewport: "+v.w+","+v.h);
};
/* Test graphics (repeating pattern). */
that.drawArea = function(x, y, w, h) {
UI.message("draw "+x+","+y+" ("+w+","+h+")");
var imgData = ctx.createImageData(w, h),
data = imgData.data, pixel, realX, realY;
for (var i = 0; i < w; i++) {
realX = viewport.x + x + i;
for (var j = 0; j < h; j++) {
realY = viewport.y + y + j;
pixel = (j * w * 4 + i * 4);
data[pixel + 0] = ((realX * realY) / 13) % 256;
data[pixel + 1] = ((realX * realY) + 392) % 256;
data[pixel + 2] = ((realX + realY) + 256) % 256;
data[pixel + 3] = 255;
}
}
//UI.message("i: " + i + ", j: " + j + ", pixel: " + pixel);
ctx.putImageData(imgData, x, y);
};
/* Screen refresh. */
that.refresh = function() {
if (UI.rfb) {UI.rfb.refresh();}
UI.message('screen refreshed.');
Util.Debug('screen refreshed.');
};
that.clear = function() {
if (conf.logo) {
that.resize(conf.logo.width, conf.logo.height);
that.viewportChange(0, 0, conf.logo.width, conf.logo.height);

View File

@ -80,10 +80,11 @@
}
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect
* scaling will occur. Canvas resizes to remote VNC settings */
* scaling will occur. Canvas resizes to client browser size (viewport). */
#VNC_screen {
text-align: center;
display: table;
/* display: table; */
display: block;
}
#VNC_canvas {
background: #eee;

View File

@ -132,6 +132,7 @@ Util.conf_defaults(conf, that, defaults, [
['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'],
['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
['refreshTimeout', 'rw', 'int', 2, 'Time (s) to wait for a refresh'],
['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
@ -428,7 +429,6 @@ updateState = function(state, statusMsg) {
case 'connect':
connTimer = setTimeout(function () {
fail("Connect timeout");
}, conf.connectTimeout * 1000);
@ -441,7 +441,8 @@ updateState = function(state, statusMsg) {
case 'disconnect':
rfb_password='';
if (! test_mode) {
disconnTimer = setTimeout(function () {
fail("Disconnect timeout");
@ -449,7 +450,8 @@ updateState = function(state, statusMsg) {
}
print_stats();
display.clear();
// WebSocket.onclose transitions to 'disconnected'
break;
@ -1527,6 +1529,17 @@ that.sendPassword = function(passwd) {
setTimeout(init_msg, 1);
};
that.refresh = function() {
// Refresh only when already connected, i.e. don't refresh during disconnected or loaded states.
if (rfb_state!='loaded' && rfb_password.length>0) {
updateState('connect','refreshing...');
setTimeout(function () {
if (rfb_state!=='normal') {that.refresh();}
}, conf.refreshTimeout * 1000);
}
UI.message('state: '+rfb_state);
};
that.sendCtrlAltDel = function() {
if (rfb_state !== "normal") { return false; }
Util.Info("Sending Ctrl-Alt-Del");

View File

@ -10,13 +10,31 @@
/*jslint white: false, browser: true */
/*global window, $D, Util, WebUtil, RFB, Display */
var msg_cnt = 0, iterations,
//fb_width = 800,
//fb_height = 768,
fb_width=1920,
fb_height=1200,
viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 },
cleanRect = {},
penDown = false, doMove = false,
inMove = false, lastPos = {},
canvas, ctx, keyboard, mouse;
var newline = "\n";
var UI = {
settingsOpen : false,
// Render default UI and initialize settings menu
load: function(target) {
var html = '', i, sheet, sheets, llevels;
if (Util.Engine.trident) {
var newline = "<br>\n";
}
var html = '', i, val, sheet, sheets, llevels;
/* Populate the 'target' DOM element with default UI */
if (!target) {
@ -39,17 +57,18 @@ load: function(target) {
target.innerHTML = html;
return;
}
html += '<div id="VNC_controls">';
html += ' <ul>';
html += ' <li>Host: <input id="VNC_host"></li>';
html += ' <li>Port: <input id="VNC_port"></li>';
html += ' <li>Password: <input id="VNC_password"';
html += ' type="password"></li>';
html += ' <li><input id="VNC_connect_button" type="button"';
html += ' value="Loading" disabled></li>';
html += ' </ul>';
html += '</div>';
html+='<table border="0"><tr>';
html+=' <td width="150pt">Host: <input id="VNC_host"/></td>';
html+=' <td width="100pt">Port: <input id="VNC_port"/></td>';
html+=' <td><div id="VNC_passwdField">';
html+=' <form onsubmit="return UI.connect();">';
html+=' Password: <input id="VNC_password" type="password"/>';
html+=' </form>';
html+=' <input id="VNC_connect_button" type="button"/>';
html+=' </div></td>';
html+='</tr></table>';
html += '<div id="VNC_screen">';
html += ' <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">';
html += ' <table border=0 width=100%><tr>';
@ -86,7 +105,15 @@ load: function(target) {
html += ' <li><input id="VNC_connectTimeout"';
html += ' type="input"> Connect Timeout (s)</li>';
html += ' <hr>';
// Scale selection dropdown
html += ' <li><select id="VNC_scale">';
for (i = 10; i >= 1; i -= 1) {
val = (i/10).toFixed(1);
html += '<option value="' + val + '">' + val + '</option>';
}
html += ' </select> Scale</li>';
// Stylesheet selection dropdown
html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
html += ' <option value="default">default</option>';
@ -140,6 +167,7 @@ load: function(target) {
// Settings with immediate effects
UI.initSetting('logging', 'warn');
WebUtil.init_logging(UI.getSetting('logging'));
UI.initSetting('scale', 1.0);
UI.initSetting('stylesheet', 'default');
WebUtil.selectStylesheet(null); // call twice to get around webkit bug
@ -149,6 +177,7 @@ load: function(target) {
UI.initSetting('host', '');
UI.initSetting('port', '');
UI.initSetting('password', '');
//TODO: Dynamically detect URI to determine encryption mode.
UI.initSetting('encrypt', false);
UI.initSetting('true_color', true);
UI.initSetting('cursor', false);
@ -158,7 +187,7 @@ load: function(target) {
UI.rfb = RFB({'target': $D('VNC_canvas'),
'onUpdateState': UI.updateState,
'onClipboard': UI.clipReceive});
// Unfocus clipboard when over the VNC area
$D('VNC_screen').onmousemove = function () {
var keyboard = UI.rfb.get_keyboard();
@ -172,7 +201,9 @@ load: function(target) {
$D('VNC_mouse_buttons').style.display = "inline";
UI.setMouseButton();
}
ctx=UI.rfb.get_display().get_context();
UI.rfb.get_display().resizeAuto(target);
},
// Read form control compatible setting from cookie
@ -264,6 +295,7 @@ clickSettingsMenu: function() {
UI.updateSetting('shared');
UI.updateSetting('connectTimeout');
UI.updateSetting('stylesheet');
UI.updateSetting('scale');
UI.updateSetting('logging');
UI.openSettingsMenu();
@ -300,6 +332,7 @@ settingsDisabled: function(disabled, rfb) {
// Save/apply settings when 'Apply' button is pressed
settingsApply: function() {
var scale = UI.getSetting('scale');
//Util.Debug(">> settingsApply");
UI.saveSetting('encrypt');
UI.saveSetting('true_color');
@ -308,18 +341,24 @@ settingsApply: function() {
}
UI.saveSetting('shared');
UI.saveSetting('connectTimeout');
UI.saveSetting('scale');
UI.saveSetting('stylesheet');
UI.saveSetting('logging');
// Settings with immediate (non-connected related) effect
if (UI.rfb) {
//Util.Debug("scale: " + scale);
UI.rfb.get_display().set_scale(UI.getSetting('scale'));
UI.rfb.get_mouse().set_scale(UI.getSetting('scale'));
//UI.rfb.get_display().resize();
UI.rfb.get_display().resizeAuto($D('vnc'));
}
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
WebUtil.init_logging(UI.getSetting('logging'));
//Util.Debug("<< settingsApply");
},
setPassword: function() {
UI.rfb.sendPassword($D('VNC_password').value);
return false;
@ -359,22 +398,25 @@ setMouseButton: function(num) {
},
updateState: function(rfb, state, oldstate, msg) {
var s, sb, c, cad, klass;
//var link, host, port, password;
var html, s, sb, cad, klass;
s = $D('VNC_status');
sb = $D('VNC_status_bar');
c = $D('VNC_connect_button');
cad = $D('sendCtrlAltDelButton');
switch (state) {
case 'failed':
case 'fatal':
c.disabled = true;
cad.disabled = true;
UI.settingsDisabled(true, rfb);
klass = "VNC_status_error";
break;
case 'normal':
c.value = "Disconnect";
c.onclick = UI.disconnect;
$D('VNC_passwdField').innerHTML='<input id="VNC_connect_button" type="button"/>';
var c=$D('VNC_connect_button');
c.value="Disconnect";
c.onclick=UI.disconnect;
c.disabled = false;
cad.disabled = false;
UI.settingsDisabled(true, rfb);
@ -382,25 +424,21 @@ updateState: function(rfb, state, oldstate, msg) {
break;
case 'disconnected':
case 'loaded':
c.value = "Connect";
c.onclick = UI.connect;
c.disabled = false;
html='<form onsubmit="return UI.connect();">';
html+=' Password: <input id="VNC_password" type="password"/>';
html+='</form>';
$D('VNC_passwdField').innerHTML=html;
cad.disabled = true;
UI.settingsDisabled(false, rfb);
klass = "VNC_status_normal";
break;
case 'password':
c.value = "Send Password";
c.onclick = UI.setPassword;
c.disabled = false;
cad.disabled = true;
UI.settingsDisabled(true, rfb);
klass = "VNC_status_warn";
break;
default:
c.disabled = true;
cad.disabled = true;
UI.settingsDisabled(true, rfb);
klass = "VNC_status_warn";
@ -412,21 +450,42 @@ updateState: function(rfb, state, oldstate, msg) {
sb.setAttribute("class", klass);
s.innerHTML = msg;
}
},
/* updateState: function(rfb, state, oldstate, msg) {
var link, s, sb, cad, level;
s = $D('VNC_status');
sb = $D('VNC_status_bar');
cad = $D('sendCtrlAltDelButton');
switch (state) {
case 'failed': level = "error"; break;
case 'fatal': level = "error"; break;
case 'normal': level = "normal"; break;
case 'disconnected': level = "normal"; break;
case 'loaded': level = "normal"; break;
default: level = "warn"; break;
}
if (state === "normal") { cad.disabled = false; }
else { cad.disabled = true; }
if (typeof(msg) !== 'undefined') {
sb.setAttribute("class", "VNC_status_" + level);
s.innerHTML = msg;
}
}, */
clipReceive: function(rfb, text) {
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
$D('VNC_clipboard_text').value = text;
Util.Debug("<< UI.clipReceive");
},
connect: function() {
var host, port, password;
UI.closeSettingsMenu();
host = $D('VNC_host').value;
port = $D('VNC_port').value;
password = $D('VNC_password').value;
@ -439,13 +498,14 @@ connect: function() {
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
UI.rfb.set_shared(UI.getSetting('shared'));
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
UI.rfb.connect(host, port, password);
return false;
},
disconnect: function() {
UI.closeSettingsMenu();
UI.rfb.disconnect();
},
@ -469,6 +529,14 @@ clipSend: function() {
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
UI.rfb.clipboardPasteFrom(text);
Util.Debug("<< UI.clipSend");
}
},
message: function(str) {
console.log(str);
var cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + newline;
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
};

View File

@ -8,6 +8,7 @@
<head>
<title>noVNC</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<link rel="stylesheet" href="../include/mobile.css">
<link rel="stylesheet" href="include/plain.css">
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black">
<!--
@ -19,13 +20,17 @@
</head>
<body>
<div id='vnc'>Loading</div>
<div id="vnc" class="flex-box">Loading...</div>
<div>
<br>
Log:<br>
<textarea id="messages" font="9pt" cols=80 rows=8></textarea>
</div>
<script>
window.onload = function () {
UI.load('vnc');
};
//window.onresize=function() {UI.rfb.get_display().resize(); UI.rfb.get_display().refresh();};
window.onresize=function() {UI.rfb.get_display().resizeAuto($D('vnc'));};
window.onload=function() {UI.load('vnc');};
</script>
</body>
</html>