API changes/cleanup.
API changes: - include/canvas.js renamed to include/display.js - Display.rescale() method removed from API. Use Display.set_scale() instead. - Make logo configuration attribute of Display and display it when clear() is called if it is set. API deprecations: - use RFB onUpdateState instead of updateState. - use RFB onClipboard instead of clipboardReceive. See https://github.com/kanaka/noVNC/wiki/ModuleAPI for detailed noVNC modules and API description. Expand and normalize the event/callback interfaces. Standize on "onEventName" form for callbacks. Callback Renames: - RFB updateState -> onUpdateState - RFB clipboardReceive -> onClipboard - Keyboard keyPress -> onKeyPress - Mouse mouseButton -> onMouseButton - Mouse mouseMove -> onMouseMove Callback Additions: - RFB onPasswordRequired - RFB onBell - RFB onFBUReceive - RFB onFBUComplete Other: - Add array type support to Util.conf_default() - Removed a bunch of routines from the Display API that were just used internally and not actually by noVNC: flush, setFillColor, imageDataGet, imageDataCreate, rgbxImageData, rgbxImageFill, cmapImageData, cmapImageFill. - More keyboard/mouse logging when debug turned on. - Some JSLinting
This commit is contained in:
parent
2fb665ec94
commit
d890e8640f
|
@ -41,7 +41,6 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
/*jslint white: false, bitwise: false, plusplus: false */
|
||||
/*global console */
|
||||
|
||||
|
@ -52,6 +51,7 @@ toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+
|
|||
base64Pad : '=',
|
||||
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '',
|
||||
chrTable = Base64.toBase64Table.split(''),
|
||||
pad = Base64.base64Pad,
|
||||
|
@ -95,6 +95,7 @@ toBinaryTable : [
|
|||
],
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var binTable = Base64.toBinaryTable,
|
||||
pad = Base64.base64Pad,
|
||||
|
|
|
@ -9,15 +9,22 @@
|
|||
/*jslint browser: true, white: false, bitwise: false */
|
||||
/*global Util, Base64, changeCursor */
|
||||
|
||||
function Canvas(conf) {
|
||||
"use strict";
|
||||
function Display(conf) {
|
||||
"use strict";
|
||||
|
||||
conf = conf || {}; // Configuration
|
||||
var that = {}, // Public API interface
|
||||
|
||||
// Private Canvas namespace variables
|
||||
// Private Display namespace variables
|
||||
c_ctx = null,
|
||||
c_forceCanvas = false,
|
||||
|
||||
c_imageData, c_rgbxImage, c_cmapImage,
|
||||
|
||||
// Predefine function variables (jslint)
|
||||
imageDataCreate, imageDataGet, rgbxImageData, cmapImageData,
|
||||
rgbxImageFill, cmapImageFill, setFillColor, rescale, flush,
|
||||
|
||||
c_width = 0,
|
||||
c_height = 0,
|
||||
|
||||
|
@ -29,19 +36,24 @@ var that = {}, // Public API interface
|
|||
// Configuration settings
|
||||
function cdef(v, type, defval, desc) {
|
||||
Util.conf_default(conf, that, v, type, defval, desc); }
|
||||
function cdef_ro(v, type, defval, desc) {
|
||||
Util.conf_default({}, that, v, type, defval, desc); }
|
||||
|
||||
// Capability settings, default can be overridden
|
||||
cdef('prefer_js', 'raw', null, 'Prefer Javascript over canvas methods');
|
||||
cdef('target', 'dom', null, 'Canvas element for rendering');
|
||||
cdef_ro('context', 'raw', null, 'Canvas 2D context for rendering (read-only)');
|
||||
cdef('logo', 'raw', null, 'Logo to display when cleared: {"width": width, "height": height, "data": data}');
|
||||
cdef('true_color', 'bool', true, 'Use true-color pixel data');
|
||||
cdef('colourMap', 'arr', [], 'Colour map array (when not true-color)');
|
||||
cdef('scale', 'float', 1.0, 'Display area scale factor 0.0 - 1.0');
|
||||
cdef_ro('width', 'int', null, 'Display area width (read-only)');
|
||||
cdef_ro('height', 'int', null, 'Display area height (read-only)');
|
||||
|
||||
cdef_ro('render_mode', 'str', '', 'Canvas rendering mode (read-only)');
|
||||
|
||||
cdef('prefer_js', 'str', null, 'Prefer Javascript over canvas methods');
|
||||
cdef('cursor_uri', 'raw', null, 'Can we render cursor using data URI');
|
||||
|
||||
cdef('target', 'dom', null, 'Canvas element for VNC viewport');
|
||||
cdef('focusContainer', 'dom', document, 'DOM element that traps keyboard input');
|
||||
cdef('true_color', 'bool', true, 'Request true color pixel data');
|
||||
cdef('colourMap', 'raw', [], 'Colour map array (not true color)');
|
||||
cdef('scale', 'float', 1.0, 'Viewport scale factor 0.1 - 1.0');
|
||||
|
||||
cdef('render_mode', 'str', '', 'Canvas rendering mode (read-only)');
|
||||
|
||||
// Override some specific getters/setters
|
||||
that.set_prefer_js = function(val) {
|
||||
if (val && c_forceCanvas) {
|
||||
|
@ -52,34 +64,19 @@ that.set_prefer_js = function(val) {
|
|||
return true;
|
||||
};
|
||||
|
||||
that.get_colourMap = function(idx) {
|
||||
if (typeof idx === 'undefined') {
|
||||
return conf.colourMap;
|
||||
} else {
|
||||
return conf.colourMap[idx];
|
||||
}
|
||||
};
|
||||
|
||||
that.set_colourMap = function(val, idx) {
|
||||
if (typeof idx === 'undefined') {
|
||||
conf.colourMap = val;
|
||||
} else {
|
||||
conf.colourMap[idx] = val;
|
||||
}
|
||||
};
|
||||
|
||||
that.set_render_mode = function () { throw("render_mode is read-only"); };
|
||||
|
||||
that.set_scale = function(scale) { that.rescale(scale); };
|
||||
that.set_scale = function(scale) { rescale(scale); };
|
||||
|
||||
that.set_width = function (val) { that.resize(val, c_height); };
|
||||
that.get_width = function() { return c_width; };
|
||||
|
||||
that.set_height = function (val) { that.resize(c_width, val); };
|
||||
that.get_height = function() { return c_height; };
|
||||
|
||||
that.set_context = function () { throw("context is read-only"); };
|
||||
that.get_context = function () { return c_ctx; };
|
||||
|
||||
// Add some other getters/setters
|
||||
that.get_width = function() {
|
||||
return c_width;
|
||||
};
|
||||
that.get_height = function() {
|
||||
return c_height;
|
||||
};
|
||||
|
||||
//
|
||||
// Private functions
|
||||
|
@ -87,9 +84,9 @@ that.get_height = function() {
|
|||
|
||||
// Create the public API interface
|
||||
function constructor() {
|
||||
Util.Debug(">> Canvas.init");
|
||||
Util.Debug(">> Display.constructor");
|
||||
|
||||
var c, ctx, func, imgTest, tval, i, curDat, curSave,
|
||||
var c, func, imgTest, tval, i, curDat, curSave,
|
||||
has_imageData = false, UE = Util.Engine;
|
||||
|
||||
if (! conf.target) { throw("target must be set"); }
|
||||
|
@ -102,8 +99,7 @@ function constructor() {
|
|||
|
||||
if (! c.getContext) { throw("no getContext method"); }
|
||||
|
||||
if (! conf.ctx) { conf.ctx = c.getContext('2d'); }
|
||||
ctx = conf.ctx;
|
||||
if (! c_ctx) { c_ctx = c.getContext('2d'); }
|
||||
|
||||
Util.Debug("User Agent: " + navigator.userAgent);
|
||||
if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); }
|
||||
|
@ -119,11 +115,11 @@ function constructor() {
|
|||
*/
|
||||
tval = 0;
|
||||
try {
|
||||
imgTest = ctx.getImageData(0, 0, 1,1);
|
||||
imgTest = c_ctx.getImageData(0, 0, 1,1);
|
||||
imgTest.data[0] = 123;
|
||||
imgTest.data[3] = 255;
|
||||
ctx.putImageData(imgTest, 0, 0);
|
||||
tval = ctx.getImageData(0, 0, 1, 1).data[0];
|
||||
c_ctx.putImageData(imgTest, 0, 0);
|
||||
tval = c_ctx.getImageData(0, 0, 1, 1).data[0];
|
||||
if (tval === 123) {
|
||||
has_imageData = true;
|
||||
}
|
||||
|
@ -132,30 +128,30 @@ function constructor() {
|
|||
if (has_imageData) {
|
||||
Util.Info("Canvas supports imageData");
|
||||
c_forceCanvas = false;
|
||||
if (ctx.createImageData) {
|
||||
if (c_ctx.createImageData) {
|
||||
// If it's there, it's faster
|
||||
Util.Info("Using Canvas createImageData");
|
||||
conf.render_mode = "createImageData rendering";
|
||||
that.imageData = that.imageDataCreate;
|
||||
} else if (ctx.getImageData) {
|
||||
c_imageData = imageDataCreate;
|
||||
} else if (c_ctx.getImageData) {
|
||||
// I think this is mostly just Opera
|
||||
Util.Info("Using Canvas getImageData");
|
||||
conf.render_mode = "getImageData rendering";
|
||||
that.imageData = that.imageDataGet;
|
||||
c_imageData = imageDataGet;
|
||||
}
|
||||
Util.Info("Prefering javascript operations");
|
||||
if (conf.prefer_js === null) {
|
||||
conf.prefer_js = true;
|
||||
}
|
||||
that.rgbxImage = that.rgbxImageData;
|
||||
that.cmapImage = that.cmapImageData;
|
||||
c_rgbxImage = rgbxImageData;
|
||||
c_cmapImage = cmapImageData;
|
||||
} else {
|
||||
Util.Warn("Canvas lacks imageData, using fillRect (slow)");
|
||||
conf.render_mode = "fillRect rendering (slow)";
|
||||
c_forceCanvas = true;
|
||||
conf.prefer_js = false;
|
||||
that.rgbxImage = that.rgbxImageFill;
|
||||
that.cmapImage = that.cmapImageFill;
|
||||
c_rgbxImage = rgbxImageFill;
|
||||
c_cmapImage = cmapImageFill;
|
||||
}
|
||||
|
||||
if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) {
|
||||
|
@ -171,7 +167,7 @@ function constructor() {
|
|||
return function() {
|
||||
myfunc.apply(this, arguments);
|
||||
if (!c_flush_timer) {
|
||||
c_flush_timer = setTimeout(that.flush, 100);
|
||||
c_flush_timer = setTimeout(flush, 100);
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
@ -206,19 +202,11 @@ function constructor() {
|
|||
conf.cursor_uri = false;
|
||||
}
|
||||
|
||||
Util.Debug("<< Canvas.init");
|
||||
Util.Debug("<< Display.constructor");
|
||||
return that ;
|
||||
}
|
||||
|
||||
//
|
||||
// Public API interface functions
|
||||
//
|
||||
|
||||
that.getContext = function () {
|
||||
return conf.ctx;
|
||||
};
|
||||
|
||||
that.rescale = function(factor) {
|
||||
rescale = function(factor) {
|
||||
var c, tp, x, y,
|
||||
properties = ['transform', 'WebkitTransform', 'MozTransform', null];
|
||||
c = conf.target;
|
||||
|
@ -242,7 +230,7 @@ that.rescale = function(factor) {
|
|||
}
|
||||
|
||||
if (conf.scale === factor) {
|
||||
//Util.Debug("Canvas already scaled to '" + factor + "'");
|
||||
//Util.Debug("Display already scaled to '" + factor + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -252,35 +240,10 @@ that.rescale = function(factor) {
|
|||
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
|
||||
};
|
||||
|
||||
that.resize = function(width, height, true_color) {
|
||||
var c = conf.target;
|
||||
|
||||
if (typeof true_color !== "undefined") {
|
||||
conf.true_color = true_color;
|
||||
}
|
||||
c_prevStyle = "";
|
||||
|
||||
c.width = width;
|
||||
c.height = height;
|
||||
|
||||
c_width = c.offsetWidth;
|
||||
c_height = c.offsetHeight;
|
||||
|
||||
that.rescale(conf.scale);
|
||||
};
|
||||
|
||||
that.clear = function() {
|
||||
that.resize(640, 20);
|
||||
conf.ctx.clearRect(0, 0, c_width, c_height);
|
||||
|
||||
// No benefit over default ("source-over") in Chrome and firefox
|
||||
//conf.ctx.globalCompositeOperation = "copy";
|
||||
};
|
||||
|
||||
that.flush = function() {
|
||||
// Force canvas redraw (for webkit bug #46319 workaround)
|
||||
flush = function() {
|
||||
var old_val;
|
||||
//Util.Debug(">> flush");
|
||||
// Force canvas redraw (for webkit bug #46319 workaround)
|
||||
old_val = conf.target.style.marginRight;
|
||||
conf.target.style.marginRight = "1px";
|
||||
c_flush_timer = null;
|
||||
|
@ -289,7 +252,7 @@ that.flush = function() {
|
|||
}, 1);
|
||||
};
|
||||
|
||||
that.setFillColor = function(color) {
|
||||
setFillColor = function(color) {
|
||||
var rgb, newStyle;
|
||||
if (conf.true_color) {
|
||||
rgb = color;
|
||||
|
@ -298,18 +261,51 @@ that.setFillColor = function(color) {
|
|||
}
|
||||
newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
|
||||
if (newStyle !== c_prevStyle) {
|
||||
conf.ctx.fillStyle = newStyle;
|
||||
c_ctx.fillStyle = newStyle;
|
||||
c_prevStyle = newStyle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Public API interface functions
|
||||
//
|
||||
|
||||
that.resize = function(width, height) {
|
||||
var c = conf.target;
|
||||
|
||||
c_prevStyle = "";
|
||||
|
||||
c.width = width;
|
||||
c.height = height;
|
||||
|
||||
c_width = c.offsetWidth;
|
||||
c_height = c.offsetHeight;
|
||||
|
||||
rescale(conf.scale);
|
||||
};
|
||||
|
||||
that.clear = function() {
|
||||
|
||||
if (conf.logo) {
|
||||
that.resize(conf.logo.width, conf.logo.height);
|
||||
that.blitStringImage(conf.logo.data, 0, 0);
|
||||
} else {
|
||||
that.resize(640, 20);
|
||||
c_ctx.clearRect(0, 0, c_width, c_height);
|
||||
}
|
||||
|
||||
// No benefit over default ("source-over") in Chrome and firefox
|
||||
//c_ctx.globalCompositeOperation = "copy";
|
||||
};
|
||||
|
||||
that.fillRect = function(x, y, width, height, color) {
|
||||
that.setFillColor(color);
|
||||
conf.ctx.fillRect(x, y, width, height);
|
||||
setFillColor(color);
|
||||
c_ctx.fillRect(x, y, width, height);
|
||||
};
|
||||
|
||||
that.copyImage = function(old_x, old_y, new_x, new_y, width, height) {
|
||||
conf.ctx.drawImage(conf.target, old_x, old_y, width, height,
|
||||
c_ctx.drawImage(conf.target, old_x, old_y, width, height,
|
||||
new_x, new_y, width, height);
|
||||
};
|
||||
|
||||
|
@ -374,36 +370,36 @@ that.setSubTile = function(img, x, y, w, h, color) {
|
|||
|
||||
that.putTile = function(img) {
|
||||
if (conf.prefer_js) {
|
||||
that.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
|
||||
c_rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
|
||||
}
|
||||
// else: No-op, under gecko already done by setSubTile
|
||||
};
|
||||
|
||||
that.imageDataGet = function(width, height) {
|
||||
return conf.ctx.getImageData(0, 0, width, height);
|
||||
imageDataGet = function(width, height) {
|
||||
return c_ctx.getImageData(0, 0, width, height);
|
||||
};
|
||||
that.imageDataCreate = function(width, height) {
|
||||
return conf.ctx.createImageData(width, height);
|
||||
imageDataCreate = function(width, height) {
|
||||
return c_ctx.createImageData(width, height);
|
||||
};
|
||||
|
||||
that.rgbxImageData = function(x, y, width, height, arr, offset) {
|
||||
rgbxImageData = function(x, y, width, height, arr, offset) {
|
||||
var img, i, j, data;
|
||||
img = that.imageData(width, height);
|
||||
img = c_imageData(width, height);
|
||||
data = img.data;
|
||||
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
|
||||
data[i + 0] = arr[j + 0];
|
||||
data[i ] = arr[j ];
|
||||
data[i + 1] = arr[j + 1];
|
||||
data[i + 2] = arr[j + 2];
|
||||
data[i + 3] = 255; // Set Alpha
|
||||
}
|
||||
conf.ctx.putImageData(img, x, y);
|
||||
c_ctx.putImageData(img, x, y);
|
||||
};
|
||||
|
||||
// really slow fallback if we don't have imageData
|
||||
that.rgbxImageFill = function(x, y, width, height, arr, offset) {
|
||||
rgbxImageFill = function(x, y, width, height, arr, offset) {
|
||||
var i, j, sx = 0, sy = 0;
|
||||
for (i=0, j=offset; i < (width * height); i+=1, j+=4) {
|
||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j+0], arr[j+1], arr[j+2]]);
|
||||
that.fillRect(x+sx, y+sy, 1, 1, [arr[j], arr[j+1], arr[j+2]]);
|
||||
sx += 1;
|
||||
if ((sx % width) === 0) {
|
||||
sx = 0;
|
||||
|
@ -412,22 +408,22 @@ that.rgbxImageFill = function(x, y, width, height, arr, offset) {
|
|||
}
|
||||
};
|
||||
|
||||
that.cmapImageData = function(x, y, width, height, arr, offset) {
|
||||
cmapImageData = function(x, y, width, height, arr, offset) {
|
||||
var img, i, j, data, rgb, cmap;
|
||||
img = that.imageData(width, height);
|
||||
img = c_imageData(width, height);
|
||||
data = img.data;
|
||||
cmap = conf.colourMap;
|
||||
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
|
||||
rgb = cmap[arr[j]];
|
||||
data[i + 0] = rgb[0];
|
||||
data[i ] = rgb[0];
|
||||
data[i + 1] = rgb[1];
|
||||
data[i + 2] = rgb[2];
|
||||
data[i + 3] = 255; // Set Alpha
|
||||
}
|
||||
conf.ctx.putImageData(img, x, y);
|
||||
c_ctx.putImageData(img, x, y);
|
||||
};
|
||||
|
||||
that.cmapImageFill = function(x, y, width, height, arr, offset) {
|
||||
cmapImageFill = function(x, y, width, height, arr, offset) {
|
||||
var i, j, sx = 0, sy = 0, cmap;
|
||||
cmap = conf.colourMap;
|
||||
for (i=0, j=offset; i < (width * height); i+=1, j+=1) {
|
||||
|
@ -443,15 +439,15 @@ that.cmapImageFill = function(x, y, width, height, arr, offset) {
|
|||
|
||||
that.blitImage = function(x, y, width, height, arr, offset) {
|
||||
if (conf.true_color) {
|
||||
that.rgbxImage(x, y, width, height, arr, offset);
|
||||
c_rgbxImage(x, y, width, height, arr, offset);
|
||||
} else {
|
||||
that.cmapImage(x, y, width, height, arr, offset);
|
||||
c_cmapImage(x, y, width, height, arr, offset);
|
||||
}
|
||||
};
|
||||
|
||||
that.blitStringImage = function(str, x, y) {
|
||||
var img = new Image();
|
||||
img.onload = function () { conf.ctx.drawImage(img, x, y); };
|
||||
img.onload = function () { c_ctx.drawImage(img, x, y); };
|
||||
img.src = str;
|
||||
};
|
||||
|
||||
|
@ -469,16 +465,17 @@ that.changeCursor = function(pixels, mask, hotx, hoty, w, h) {
|
|||
};
|
||||
|
||||
that.defaultCursor = function() {
|
||||
conf.target.style.cursor = "default";
|
||||
conf.target.style.cursor = "default";
|
||||
};
|
||||
|
||||
return constructor(); // Return the public API interface
|
||||
|
||||
} // End of Canvas()
|
||||
} // End of Display()
|
||||
|
||||
|
||||
/* Set CSS cursor property using data URI encoded cursor file */
|
||||
function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) {
|
||||
"use strict";
|
||||
var cur = [], rgb, IHDRsz, RGBsz, ANDsz, XORsz, url, idx, alpha, x, y;
|
||||
//Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h);
|
||||
|
||||
|
@ -549,7 +546,7 @@ function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) {
|
|||
idx = ((w * y) + x) * 4;
|
||||
cur.push(pixels[idx + 2]); // blue
|
||||
cur.push(pixels[idx + 1]); // green
|
||||
cur.push(pixels[idx + 0]); // red
|
||||
cur.push(pixels[idx ]); // red
|
||||
cur.push(alpha); // alpha
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
|
||||
function Keyboard(conf) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
conf = conf || {}; // Configuration
|
||||
var that = {}, // Public API interface
|
||||
|
@ -27,12 +27,12 @@ function cdef(v, type, defval, desc) {
|
|||
Util.conf_default(conf, that, v, type, defval, desc); }
|
||||
|
||||
// Capability settings, default can be overridden
|
||||
cdef('target', 'dom', document, 'DOM element that grabs keyboard input');
|
||||
cdef('focused', 'bool', true, 'Capture and send key strokes');
|
||||
cdef('target', 'dom', document, 'DOM element that captures keyboard input');
|
||||
cdef('focused', 'bool', true, 'Capture and send key events');
|
||||
|
||||
cdef('keyPress', 'func', null, 'Handler for key press/release');
|
||||
cdef('onKeyPress', 'func', null, 'Handler for key press/release');
|
||||
|
||||
that.set_target = function () { throw("target cannot be changed"); }
|
||||
that.set_target = function() { throw("target cannot be changed"); };
|
||||
|
||||
//
|
||||
// Private functions
|
||||
|
@ -187,7 +187,7 @@ function getKeysym(evt) {
|
|||
}
|
||||
|
||||
if ((keysym > 255) && (keysym < 0xFF00)) {
|
||||
msg = "Mapping keysym " + keysym;
|
||||
msg = "Mapping character code " + keysym;
|
||||
// Map Unicode outside Latin 1 to X11 keysyms
|
||||
keysym = unicodeTable[keysym];
|
||||
if (typeof(keysym) === 'undefined') {
|
||||
|
@ -200,12 +200,13 @@ function getKeysym(evt) {
|
|||
}
|
||||
|
||||
function show_keyDownList(kind) {
|
||||
var c;
|
||||
var msg = "keyDownList (" + kind + "):\n";
|
||||
for (var c = 0; c < keyDownList.length; c++) {
|
||||
for (c = 0; c < keyDownList.length; c++) {
|
||||
msg = msg + " " + c + " - keyCode: " + keyDownList[c].keyCode +
|
||||
" - which: " + keyDownList[c].which + "\n";
|
||||
}
|
||||
//Util.Debug(msg);
|
||||
Util.Debug(msg);
|
||||
}
|
||||
|
||||
function copyKeyEvent(evt) {
|
||||
|
@ -302,7 +303,7 @@ function onKeyDown(e) {
|
|||
}
|
||||
var fevt = null, evt = (e ? e : window.event),
|
||||
keysym = null, suppress = false;
|
||||
Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
//Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
|
||||
fevt = copyKeyEvent(evt);
|
||||
|
||||
|
@ -313,10 +314,11 @@ function onKeyDown(e) {
|
|||
// If it is a key or key combination that might trigger
|
||||
// browser behaviors or it has no corresponding keyPress
|
||||
// event, then send it immediately
|
||||
if (conf.keyPress && !ignoreKeyEvent(evt)) {
|
||||
Util.Debug("keyPress down 1, keysym: " + keysym +
|
||||
" (key: " + evt.keyCode + ", which: " + evt.which + ")");
|
||||
conf.keyPress(keysym, 1, evt);
|
||||
if (conf.onKeyPress && !ignoreKeyEvent(evt)) {
|
||||
Util.Debug("onKeyPress down, keysym: " + keysym +
|
||||
" (onKeyDown key: " + evt.keyCode +
|
||||
", which: " + evt.which + ")");
|
||||
conf.onKeyPress(keysym, 1, evt);
|
||||
}
|
||||
suppress = true;
|
||||
}
|
||||
|
@ -324,7 +326,7 @@ function onKeyDown(e) {
|
|||
if (! ignoreKeyEvent(evt)) {
|
||||
// Add it to the list of depressed keys
|
||||
pushKeyEvent(fevt);
|
||||
show_keyDownList('down');
|
||||
//show_keyDownList('down');
|
||||
}
|
||||
|
||||
if (suppress) {
|
||||
|
@ -344,7 +346,7 @@ function onKeyPress(e) {
|
|||
}
|
||||
var evt = (e ? e : window.event),
|
||||
kdlen = keyDownList.length, keysym = null;
|
||||
Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
//Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
|
||||
if (((evt.which !== "undefined") && (evt.which === 0)) ||
|
||||
(getKeysymSpecial(evt))) {
|
||||
|
@ -369,13 +371,14 @@ function onKeyPress(e) {
|
|||
Util.Warn("keyDownList empty when keyPress triggered");
|
||||
}
|
||||
|
||||
show_keyDownList('press');
|
||||
//show_keyDownList('press');
|
||||
|
||||
// Send the translated keysym
|
||||
if (conf.keyPress && (keysym > 0)) {
|
||||
Util.Debug("keyPress down 2, keysym: " + keysym +
|
||||
" (key: " + evt.keyCode + ", which: " + evt.which + ")");
|
||||
conf.keyPress(keysym, 1, evt);
|
||||
if (conf.onKeyPress && (keysym > 0)) {
|
||||
Util.Debug("onKeyPress down, keysym: " + keysym +
|
||||
" (onKeyPress key: " + evt.keyCode +
|
||||
", which: " + evt.which + ")");
|
||||
conf.onKeyPress(keysym, 1, evt);
|
||||
}
|
||||
|
||||
// Stop keypress events just in case
|
||||
|
@ -387,8 +390,8 @@ function onKeyUp(e) {
|
|||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
var fevt = null, evt = (e ? e : window.event), i, keysym;
|
||||
Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
var fevt = null, evt = (e ? e : window.event), keysym;
|
||||
//Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
|
||||
|
||||
fevt = getKeyEvent(evt.keyCode, true);
|
||||
|
||||
|
@ -400,12 +403,15 @@ function onKeyUp(e) {
|
|||
keysym = 0;
|
||||
}
|
||||
|
||||
show_keyDownList('up');
|
||||
//show_keyDownList('up');
|
||||
|
||||
if (conf.keyPress && (keysym > 0)) {
|
||||
Util.Debug("keyPress up, keysym: " + keysym +
|
||||
" (key: " + evt.keyCode + ", which: " + evt.which + ")");
|
||||
conf.keyPress(keysym, 0, evt);
|
||||
if (conf.onKeyPress && (keysym > 0)) {
|
||||
//Util.Debug("keyPress up, keysym: " + keysym +
|
||||
// " (key: " + evt.keyCode + ", which: " + evt.which + ")");
|
||||
Util.Debug("onKeyPress up, keysym: " + keysym +
|
||||
" (onKeyPress key: " + evt.keyCode +
|
||||
", which: " + evt.which + ")");
|
||||
conf.onKeyPress(keysym, 0, evt);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
|
@ -447,7 +453,7 @@ return that; // Return the public API interface
|
|||
//
|
||||
|
||||
function Mouse(conf) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
conf = conf || {}; // Configuration
|
||||
var that = {}; // Public API interface
|
||||
|
@ -458,14 +464,14 @@ function cdef(v, type, defval, desc) {
|
|||
Util.conf_default(conf, that, v, type, defval, desc); }
|
||||
|
||||
// Capability settings, default can be overridden
|
||||
cdef('target', 'dom', document, 'DOM element that grabs mouse input');
|
||||
cdef('target', 'dom', document, 'DOM element that captures mouse input');
|
||||
cdef('focused', 'bool', true, 'Capture and send mouse clicks/movement');
|
||||
cdef('scale', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0');
|
||||
|
||||
cdef('mouseButton', 'func', null, 'Handler for mouse button click/release');
|
||||
cdef('mouseMove', 'func', null, 'Handler for mouse movement');
|
||||
cdef('onMouseButton', 'func', null, 'Handler for mouse button click/release');
|
||||
cdef('onMouseMove', 'func', null, 'Handler for mouse movement');
|
||||
|
||||
that.set_target = function () { throw("target cannot be changed"); }
|
||||
that.set_target = function() { throw("target cannot be changed"); };
|
||||
|
||||
//
|
||||
// Private functions
|
||||
|
@ -489,8 +495,10 @@ function onMouseButton(e, down) {
|
|||
}
|
||||
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
|
||||
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
|
||||
if (conf.mouseButton) {
|
||||
conf.mouseButton(pos.x, pos.y, down, bmask);
|
||||
if (conf.onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
conf.onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
|
@ -518,9 +526,9 @@ function onMouseWheel(e) {
|
|||
bmask = 1 << 4;
|
||||
}
|
||||
//Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
|
||||
if (conf.mouseButton) {
|
||||
conf.mouseButton(pos.x, pos.y, 1, bmask);
|
||||
conf.mouseButton(pos.x, pos.y, 0, bmask);
|
||||
if (conf.onMouseButton) {
|
||||
conf.onMouseButton(pos.x, pos.y, 1, bmask);
|
||||
conf.onMouseButton(pos.x, pos.y, 0, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
|
@ -534,8 +542,8 @@ function onMouseMove(e) {
|
|||
evt = (e ? e : window.event);
|
||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
||||
//Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
|
||||
if (conf.mouseMove) {
|
||||
conf.mouseMove(pos.x, pos.y);
|
||||
if (conf.onMouseMove) {
|
||||
conf.onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1862,5 +1870,5 @@ unicodeTable = {
|
|||
0x28fc : 0x10028fc,
|
||||
0x28fd : 0x10028fd,
|
||||
0x28fe : 0x10028fe,
|
||||
0x28ff : 0x10028ff,
|
||||
0x28ff : 0x10028ff
|
||||
};
|
||||
|
|
216
include/rfb.js
216
include/rfb.js
|
@ -7,11 +7,11 @@
|
|||
*/
|
||||
|
||||
/*jslint white: false, browser: true, bitwise: false, plusplus: false */
|
||||
/*global window, Util, Canvas, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
|
||||
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
|
||||
|
||||
|
||||
function RFB(conf) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
conf = conf || {}; // Configuration
|
||||
var that = {}, // Public API interface
|
||||
|
@ -64,7 +64,7 @@ var that = {}, // Public API interface
|
|||
encStats = {}, // [rectCnt, rectCntTot]
|
||||
|
||||
ws = null, // Websock object
|
||||
canvas = null, // Canvas object
|
||||
display = null, // Display object
|
||||
keyboard = null, // Keyboard input handler object
|
||||
mouse = null, // Mouse input handler object
|
||||
sendTimer = null, // Send Queue check timer
|
||||
|
@ -124,8 +124,8 @@ var that = {}, // Public API interface
|
|||
function cdef(v, type, defval, desc) {
|
||||
Util.conf_default(conf, that, v, type, defval, desc); }
|
||||
|
||||
cdef('target', 'str', null, 'VNC viewport rendering Canvas');
|
||||
cdef('focusContainer', 'dom', document, 'Area that traps keyboard input');
|
||||
cdef('target', 'dom', null, 'VNC display rendering Canvas object');
|
||||
cdef('focusContainer', 'dom', document, 'DOM element that captures keyboard input');
|
||||
|
||||
cdef('encrypt', 'bool', false, 'Use TLS/SSL/wss encryption');
|
||||
cdef('true_color', 'bool', true, 'Request true color pixel data');
|
||||
|
@ -141,12 +141,25 @@ cdef('disconnectTimeout', 'int', 3, 'Time (s) to wait for disconnection');
|
|||
cdef('check_rate', 'int', 217, 'Timing (ms) of send/receive check');
|
||||
cdef('fbu_req_rate', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests');
|
||||
|
||||
cdef('updateState',
|
||||
'func', function() { Util.Debug("updateState stub"); },
|
||||
'callback: state update');
|
||||
cdef('clipboardReceive',
|
||||
'func', function() { Util.Debug("clipboardReceive stub"); },
|
||||
'callback: clipboard contents received');
|
||||
// Callback functions
|
||||
cdef('onUpdateState', 'func', function() { },
|
||||
'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change ');
|
||||
cdef('onPasswordRequired', 'func', function() { },
|
||||
'onPasswordRequired(rfb): VNC password is required ');
|
||||
cdef('onClipboard', 'func', function() { },
|
||||
'onClipboard(rfb, text): RFB clipboard contents received');
|
||||
cdef('onBell', 'func', function() { },
|
||||
'onBell(rfb): RFB Bell message received ');
|
||||
cdef('onFBUReceive', 'func', function() { },
|
||||
'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed ');
|
||||
cdef('onFBUComplete', 'func', function() { },
|
||||
'onFBUComplete(rfb, fbu): RFB FBU received and processed ');
|
||||
|
||||
// These callback names are deprecated
|
||||
cdef('updateState', 'func', function() { },
|
||||
'obsolete, use onUpdateState');
|
||||
cdef('clipboardReceive', 'func', function() { },
|
||||
'obsolete, use onClipboard');
|
||||
|
||||
|
||||
// Override/add some specific getters/setters
|
||||
|
@ -154,7 +167,7 @@ that.set_local_cursor = function(cursor) {
|
|||
if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
|
||||
conf.local_cursor = false;
|
||||
} else {
|
||||
if (canvas.get_cursor_uri()) {
|
||||
if (display.get_cursor_uri()) {
|
||||
conf.local_cursor = true;
|
||||
} else {
|
||||
Util.Warn("Browser does not support local cursor");
|
||||
|
@ -162,8 +175,8 @@ that.set_local_cursor = function(cursor) {
|
|||
}
|
||||
};
|
||||
|
||||
that.get_canvas = function() {
|
||||
return canvas;
|
||||
that.get_display = function() {
|
||||
return display;
|
||||
};
|
||||
that.get_keyboard = function() {
|
||||
return keyboard;
|
||||
|
@ -181,7 +194,8 @@ that.get_mouse = function() {
|
|||
// Setup routines
|
||||
//
|
||||
|
||||
// Create the public API interface and initialize
|
||||
// Create the public API interface and initialize values that stay
|
||||
// constant across connect/disconnect
|
||||
function constructor() {
|
||||
var i, rmode;
|
||||
Util.Debug(">> RFB.constructor");
|
||||
|
@ -192,19 +206,49 @@ function constructor() {
|
|||
encNames[encodings[i][1]] = encodings[i][0];
|
||||
encStats[encodings[i][1]] = [0, 0];
|
||||
}
|
||||
// Initialize canvas, mouse and keyboard
|
||||
// Initialize display, mouse, keyboard, and websock
|
||||
try {
|
||||
canvas = new Canvas({'target': conf.target});
|
||||
keyboard = new Keyboard({'target': conf.focusContainer,
|
||||
'keyPress': keyPress});
|
||||
mouse = new Mouse({'target': conf.target,
|
||||
'mouseButton': mouseButton,
|
||||
'mouseMove': mouseMove});
|
||||
display = new Display({'target': conf.target});
|
||||
} catch (exc) {
|
||||
Util.Error("Canvas exception: " + exc);
|
||||
updateState('fatal', "No working Canvas");
|
||||
Util.Error("Display exception: " + exc);
|
||||
updateState('fatal', "No working Display");
|
||||
}
|
||||
rmode = canvas.get_render_mode();
|
||||
keyboard = new Keyboard({'target': conf.focusContainer,
|
||||
'onKeyPress': keyPress});
|
||||
mouse = new Mouse({'target': conf.target,
|
||||
'onMouseButton': mouseButton,
|
||||
'onMouseMove': mouseMove});
|
||||
|
||||
rmode = display.get_render_mode();
|
||||
|
||||
if (typeof noVNC_logo !== 'undefined') {
|
||||
display.set_logo(noVNC_logo);
|
||||
}
|
||||
|
||||
ws = new Websock();
|
||||
ws.on('message', handle_message);
|
||||
ws.on('open', function() {
|
||||
if (rfb_state === "connect") {
|
||||
updateState('ProtocolVersion', "Starting VNC handshake");
|
||||
} else {
|
||||
fail("Got unexpected WebSockets connection");
|
||||
}
|
||||
});
|
||||
ws.on('close', function() {
|
||||
if (rfb_state === 'disconnect') {
|
||||
updateState('disconnected', 'VNC disconnected');
|
||||
} else if (rfb_state === 'ProtocolVersion') {
|
||||
fail('Failed to connect to server');
|
||||
} else if (rfb_state in {'failed':1, 'disconnected':1}) {
|
||||
Util.Error("Received onclose while disconnected");
|
||||
} else {
|
||||
fail('Server disconnected');
|
||||
}
|
||||
});
|
||||
ws.on('error', function(e) {
|
||||
fail("WebSock error: " + e);
|
||||
});
|
||||
|
||||
|
||||
init_vars();
|
||||
|
||||
|
@ -246,36 +290,13 @@ function connect() {
|
|||
Util.Debug("<< RFB.connect");
|
||||
}
|
||||
|
||||
// Initialize variables that are reset before each connection
|
||||
init_vars = function() {
|
||||
var i;
|
||||
|
||||
/* Reset state */
|
||||
ws = new Websock();
|
||||
ws.init();
|
||||
|
||||
ws.on('message', handle_message);
|
||||
ws.on('open', function() {
|
||||
if (rfb_state === "connect") {
|
||||
updateState('ProtocolVersion', "Starting VNC handshake");
|
||||
} else {
|
||||
fail("Got unexpected WebSockets connection");
|
||||
}
|
||||
});
|
||||
ws.on('close', function() {
|
||||
if (rfb_state === 'disconnect') {
|
||||
updateState('disconnected', 'VNC disconnected');
|
||||
} else if (rfb_state === 'ProtocolVersion') {
|
||||
fail('Failed to connect to server');
|
||||
} else if (rfb_state in {'failed':1, 'disconnected':1}) {
|
||||
Util.Error("Received onclose while disconnected");
|
||||
} else {
|
||||
fail('Server disconnected');
|
||||
}
|
||||
});
|
||||
ws.on('error', function(e) {
|
||||
fail("WebSock error: " + e);
|
||||
});
|
||||
|
||||
FBU.rects = 0;
|
||||
FBU.subrects = 0; // RRE and HEXTILE
|
||||
FBU.lines = 0; // RAW
|
||||
|
@ -317,25 +338,23 @@ print_stats = function() {
|
|||
|
||||
|
||||
/*
|
||||
* Running states:
|
||||
* disconnected - idle state
|
||||
* normal - connected
|
||||
*
|
||||
* Page states:
|
||||
* loaded - page load, equivalent to disconnected
|
||||
* connect - starting initialization
|
||||
* disconnect - starting disconnect
|
||||
* failed - abnormal transition to disconnected
|
||||
* disconnected - idle state
|
||||
* connect - starting to connect (to ProtocolVersion)
|
||||
* normal - connected
|
||||
* disconnect - starting to disconnect
|
||||
* failed - abnormal disconnect
|
||||
* fatal - failed to load page, or fatal error
|
||||
*
|
||||
* VNC initialization states:
|
||||
* ProtocolVersion
|
||||
* RFB protocol initialization states:
|
||||
* ProtocolVersion
|
||||
* Security
|
||||
* Authentication
|
||||
* password - waiting for password, not part of RFB
|
||||
* SecurityResult
|
||||
* ClientInitialization - not triggered by server message
|
||||
* ServerInitialization
|
||||
* ServerInitialization (to normal)
|
||||
*/
|
||||
updateState = function(state, statusMsg) {
|
||||
var func, cmsg, oldstate = rfb_state;
|
||||
|
@ -362,22 +381,15 @@ updateState = function(state, statusMsg) {
|
|||
msgTimer = null;
|
||||
}
|
||||
|
||||
if (canvas && canvas.getContext()) {
|
||||
if (display && display.get_context()) {
|
||||
keyboard.ungrab();
|
||||
mouse.ungrab();
|
||||
canvas.defaultCursor();
|
||||
if (Util.get_logging() !== 'debug') {
|
||||
canvas.clear();
|
||||
}
|
||||
|
||||
display.defaultCursor();
|
||||
if ((Util.get_logging() !== 'debug') ||
|
||||
(state === 'loaded')) {
|
||||
// Show noVNC logo on load and when disconnected if
|
||||
// debug is off
|
||||
if (typeof noVNC_logo !== 'undefined' && noVNC_logo) {
|
||||
canvas.resize(noVNC_logo.width, noVNC_logo.height);
|
||||
canvas.blitStringImage(noVNC_logo.data, 0, 0);
|
||||
}
|
||||
display.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,9 +488,11 @@ updateState = function(state, statusMsg) {
|
|||
|
||||
if ((oldstate === 'failed') && (state === 'disconnected')) {
|
||||
// Leave the failed message
|
||||
conf.updateState(that, state, oldstate);
|
||||
conf.updateState(that, state, oldstate); // Obsolete
|
||||
conf.onUpdateState(that, state, oldstate);
|
||||
} else {
|
||||
conf.updateState(that, state, oldstate, statusMsg);
|
||||
conf.updateState(that, state, oldstate, statusMsg); // Obsolete
|
||||
conf.onUpdateState(that, state, oldstate, statusMsg);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -681,7 +695,10 @@ init_msg = function() {
|
|||
break;
|
||||
case 2: // VNC authentication
|
||||
if (rfb_password.length === 0) {
|
||||
// Notify via both callbacks since it is kind of
|
||||
// a RFB state change and a UI interface issue.
|
||||
updateState('password', "Password Required");
|
||||
conf.onPasswordRequired(that);
|
||||
return;
|
||||
}
|
||||
if (ws.rQwait("auth challenge", 16)) { return false; }
|
||||
|
@ -759,7 +776,8 @@ init_msg = function() {
|
|||
name_length = ws.rQshift32();
|
||||
fb_name = ws.rQshiftStr(name_length);
|
||||
|
||||
canvas.resize(fb_width, fb_height, conf.true_color);
|
||||
display.set_true_color(conf.true_color);
|
||||
display.resize(fb_width, fb_height);
|
||||
keyboard.grab();
|
||||
mouse.grab();
|
||||
|
||||
|
@ -796,7 +814,7 @@ init_msg = function() {
|
|||
normal_msg = function() {
|
||||
//Util.Debug(">> normal_msg");
|
||||
|
||||
var ret = true, msg_type, length,
|
||||
var ret = true, msg_type, length, text,
|
||||
c, first_colour, num_colours, red, green, blue;
|
||||
|
||||
if (FBU.rects > 0) {
|
||||
|
@ -820,13 +838,15 @@ normal_msg = function() {
|
|||
//Util.Debug("red after: " + red);
|
||||
green = parseInt(ws.rQshift16() / 256, 10);
|
||||
blue = parseInt(ws.rQshift16() / 256, 10);
|
||||
canvas.set_colourMap([red, green, blue], first_colour + c);
|
||||
Util.Debug("*** colourMap: " + display.get_colourMap());
|
||||
display.set_colourMap([red, green, blue], first_colour + c);
|
||||
}
|
||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
||||
//Util.Debug("colourMap: " + canvas.get_colourMap());
|
||||
//Util.Debug("colourMap: " + display.get_colourMap());
|
||||
break;
|
||||
case 2: // Bell
|
||||
Util.Warn("Bell (unsupported)");
|
||||
Util.Debug("Bell");
|
||||
conf.onBell(that);
|
||||
break;
|
||||
case 3: // ServerCutText
|
||||
Util.Debug("ServerCutText");
|
||||
|
@ -835,7 +855,9 @@ normal_msg = function() {
|
|||
length = ws.rQshift32();
|
||||
if (ws.rQwait("ServerCutText", length, 8)) { return false; }
|
||||
|
||||
conf.clipboardReceive(that, ws.rQshiftStr(length));
|
||||
text = ws.rQshiftStr(length);
|
||||
conf.clipboardReceive(that, text); // Obsolete
|
||||
conf.onClipboard(that, text);
|
||||
break;
|
||||
default:
|
||||
fail("Disconnected: illegal server message type " + msg_type);
|
||||
|
@ -883,6 +905,12 @@ framebufferUpdate = function() {
|
|||
FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
|
||||
(hdr[10] << 8) + hdr[11], 10);
|
||||
|
||||
conf.onFBUReceive(that,
|
||||
{'x': FBU.x, 'y': FBU.y,
|
||||
'width': FBU.width, 'height': FBU.height,
|
||||
'encoding': FBU.encoding,
|
||||
'encodingName': encNames[FBU.encoding]});
|
||||
|
||||
if (encNames[FBU.encoding]) {
|
||||
// Debug:
|
||||
/*
|
||||
|
@ -943,6 +971,13 @@ framebufferUpdate = function() {
|
|||
return ret; // false ret means need more data
|
||||
}
|
||||
}
|
||||
|
||||
conf.onFBUComplete(that,
|
||||
{'x': FBU.x, 'y': FBU.y,
|
||||
'width': FBU.width, 'height': FBU.height,
|
||||
'encoding': FBU.encoding,
|
||||
'encodingName': encNames[FBU.encoding]});
|
||||
|
||||
return true; // We finished this FBU
|
||||
};
|
||||
|
||||
|
@ -963,7 +998,7 @@ encHandlers.RAW = function display_raw() {
|
|||
cur_y = FBU.y + (FBU.height - FBU.lines);
|
||||
cur_height = Math.min(FBU.lines,
|
||||
Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));
|
||||
canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height,
|
||||
display.blitImage(FBU.x, cur_y, FBU.width, cur_height,
|
||||
ws.get_rQ(), ws.get_rQi());
|
||||
ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);
|
||||
FBU.lines -= cur_height;
|
||||
|
@ -986,7 +1021,7 @@ encHandlers.COPYRECT = function display_copy_rect() {
|
|||
if (ws.rQwait("COPYRECT", 4)) { return false; }
|
||||
old_x = ws.rQshift16();
|
||||
old_y = ws.rQshift16();
|
||||
canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
|
||||
display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
|
||||
FBU.rects -= 1;
|
||||
FBU.bytes = 0;
|
||||
return true;
|
||||
|
@ -1000,7 +1035,7 @@ encHandlers.RRE = function display_rre() {
|
|||
if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
|
||||
FBU.subrects = ws.rQshift32();
|
||||
color = ws.rQshiftBytes(fb_Bpp); // Background
|
||||
canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
|
||||
display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
|
||||
}
|
||||
while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {
|
||||
color = ws.rQshiftBytes(fb_Bpp);
|
||||
|
@ -1008,7 +1043,7 @@ encHandlers.RRE = function display_rre() {
|
|||
y = ws.rQshift16();
|
||||
width = ws.rQshift16();
|
||||
height = ws.rQshift16();
|
||||
canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
|
||||
display.fillRect(FBU.x + x, FBU.y + y, width, height, color);
|
||||
FBU.subrects -= 1;
|
||||
}
|
||||
//Util.Debug(" display_rre: rects: " + FBU.rects +
|
||||
|
@ -1101,10 +1136,10 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||
/* Weird: ignore blanks after RAW */
|
||||
Util.Debug(" Ignoring blank after RAW");
|
||||
} else {
|
||||
canvas.fillRect(x, y, w, h, FBU.background);
|
||||
display.fillRect(x, y, w, h, FBU.background);
|
||||
}
|
||||
} else if (FBU.subencoding & 0x01) { // Raw
|
||||
canvas.blitImage(x, y, w, h, rQ, rQi);
|
||||
display.blitImage(x, y, w, h, rQ, rQi);
|
||||
rQi += FBU.bytes - 1;
|
||||
} else {
|
||||
if (FBU.subencoding & 0x02) { // Background
|
||||
|
@ -1116,7 +1151,7 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||
rQi += fb_Bpp;
|
||||
}
|
||||
|
||||
tile = canvas.getTile(x, y, w, h, FBU.background);
|
||||
tile = display.getTile(x, y, w, h, FBU.background);
|
||||
if (FBU.subencoding & 0x08) { // AnySubrects
|
||||
subrects = rQ[rQi];
|
||||
rQi += 1;
|
||||
|
@ -1137,10 +1172,10 @@ encHandlers.HEXTILE = function display_hextile() {
|
|||
sw = (wh >> 4) + 1;
|
||||
sh = (wh & 0x0f) + 1;
|
||||
|
||||
canvas.setSubTile(tile, sx, sy, sw, sh, color);
|
||||
display.setSubTile(tile, sx, sy, sw, sh, color);
|
||||
}
|
||||
}
|
||||
canvas.putTile(tile);
|
||||
display.putTile(tile);
|
||||
}
|
||||
ws.set_rQi(rQi);
|
||||
FBU.lastsubencoding = FBU.subencoding;
|
||||
|
@ -1205,7 +1240,7 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
|
|||
case "fill":
|
||||
ws.rQshift8(); // shift off ctl
|
||||
color = ws.rQshiftBytes(fb_depth);
|
||||
canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
|
||||
display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
|
||||
break;
|
||||
case "jpeg":
|
||||
case "png":
|
||||
|
@ -1242,7 +1277,7 @@ extract_data_uri = function(arr) {
|
|||
|
||||
scan_tight_imgQ = function() {
|
||||
var img, imgQ, ctx;
|
||||
ctx = canvas.getContext();
|
||||
ctx = display.get_context();
|
||||
if (rfb_state === 'normal') {
|
||||
imgQ = FBU.imgQ;
|
||||
while ((imgQ.length > 0) && (imgQ[0][0].complete)) {
|
||||
|
@ -1257,8 +1292,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
|
|||
Util.Debug(">> set_desktopsize");
|
||||
fb_width = FBU.width;
|
||||
fb_height = FBU.height;
|
||||
canvas.clear();
|
||||
canvas.resize(fb_width, fb_height);
|
||||
display.resize(fb_width, fb_height);
|
||||
timing.fbu_rt_start = (new Date()).getTime();
|
||||
// Send a new non-incremental request
|
||||
ws.send(fbUpdateRequest(0));
|
||||
|
@ -1286,7 +1320,7 @@ encHandlers.Cursor = function set_cursor() {
|
|||
|
||||
//Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
|
||||
|
||||
canvas.changeCursor(ws.rQshiftBytes(pixelslength),
|
||||
display.changeCursor(ws.rQshiftBytes(pixelslength),
|
||||
ws.rQshiftBytes(masklength),
|
||||
x, y, w, h);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
"use strict";
|
||||
/*jslint white: false, browser: true */
|
||||
/*global window, $D, Util, WebUtil, RFB, Canvas, Element, Fx */
|
||||
/*global window, $D, Util, WebUtil, RFB, Display */
|
||||
|
||||
var UI = {
|
||||
|
||||
|
@ -59,8 +59,8 @@ load: function(target) {
|
|||
html += ' id="menuButton"';
|
||||
html += ' onclick="UI.clickSettingsMenu();">';
|
||||
html += ' <span id="VNC_settings_menu"';
|
||||
html += ' onmouseover="UI.canvasBlur();"';
|
||||
html += ' onmouseout="UI.canvasFocus();">';
|
||||
html += ' onmouseover="UI.displayBlur();"';
|
||||
html += ' onmouseout="UI.displayFocus();">';
|
||||
html += ' <ul>';
|
||||
html += ' <li><input id="VNC_encrypt"';
|
||||
html += ' type="checkbox"> Encrypt</li>';
|
||||
|
@ -115,8 +115,8 @@ load: function(target) {
|
|||
html += ' onclick="UI.clipClear();">';
|
||||
html += ' <br>';
|
||||
html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5';
|
||||
html += ' onfocus="UI.canvasBlur();"';
|
||||
html += ' onblur="UI.canvasFocus();"';
|
||||
html += ' onfocus="UI.displayBlur();"';
|
||||
html += ' onblur="UI.displayFocus();"';
|
||||
html += ' onchange="UI.clipSend();"></textarea>';
|
||||
html += '</div>';
|
||||
target.innerHTML = html;
|
||||
|
@ -140,8 +140,8 @@ load: function(target) {
|
|||
UI.initSetting('connectTimeout', 2);
|
||||
|
||||
UI.rfb = RFB({'target': $D('VNC_canvas'),
|
||||
'updateState': UI.updateState,
|
||||
'clipboardReceive': UI.clipReceive});
|
||||
'onUpdateState': UI.updateState,
|
||||
'onClipboard': UI.clipReceive});
|
||||
|
||||
// Unfocus clipboard when over the VNC area
|
||||
$D('VNC_screen').onmousemove = function () {
|
||||
|
@ -233,7 +233,7 @@ clickSettingsMenu: function() {
|
|||
} else {
|
||||
UI.updateSetting('encrypt');
|
||||
UI.updateSetting('true_color');
|
||||
if (UI.rfb.get_canvas().get_cursor_uri()) {
|
||||
if (UI.rfb.get_display().get_cursor_uri()) {
|
||||
UI.updateSetting('cursor');
|
||||
} else {
|
||||
UI.updateSetting('cursor', false);
|
||||
|
@ -265,7 +265,7 @@ settingsDisabled: function(disabled, rfb) {
|
|||
//Util.Debug(">> settingsDisabled");
|
||||
$D('VNC_encrypt').disabled = disabled;
|
||||
$D('VNC_true_color').disabled = disabled;
|
||||
if (rfb && rfb.get_canvas() && rfb.get_canvas().get_cursor_uri()) {
|
||||
if (rfb && rfb.get_display() && rfb.get_display().get_cursor_uri()) {
|
||||
$D('VNC_cursor').disabled = disabled;
|
||||
} else {
|
||||
UI.updateSetting('cursor', false);
|
||||
|
@ -281,7 +281,7 @@ settingsApply: function() {
|
|||
//Util.Debug(">> settingsApply");
|
||||
UI.saveSetting('encrypt');
|
||||
UI.saveSetting('true_color');
|
||||
if (UI.rfb.get_canvas().get_cursor_uri()) {
|
||||
if (UI.rfb.get_display().get_cursor_uri()) {
|
||||
UI.saveSetting('cursor');
|
||||
}
|
||||
UI.saveSetting('shared');
|
||||
|
@ -398,12 +398,12 @@ disconnect: function() {
|
|||
UI.rfb.disconnect();
|
||||
},
|
||||
|
||||
canvasBlur: function() {
|
||||
displayBlur: function() {
|
||||
UI.rfb.get_keyboard().set_focused(false);
|
||||
UI.rfb.get_mouse().set_focused(false);
|
||||
},
|
||||
|
||||
canvasFocus: function() {
|
||||
displayFocus: function() {
|
||||
UI.rfb.get_keyboard().set_focused(true);
|
||||
UI.rfb.get_mouse().set_focused(true);
|
||||
},
|
||||
|
|
|
@ -89,32 +89,47 @@ Util.conf_default = function(cfg, api, v, type, defval, desc) {
|
|||
api['get_' + v + '_desc'] = desc;
|
||||
// Default getter
|
||||
if (typeof api['get_' + v] === 'undefined') {
|
||||
api['get_' + v] = function () {
|
||||
api['get_' + v] = function (idx) {
|
||||
if ((type in {'arr':1, 'array':1}) &&
|
||||
(typeof idx !== 'undefined')) {
|
||||
return cfg[v][idx];
|
||||
} else {
|
||||
return cfg[v];
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Default setter
|
||||
if (typeof api['set_' + v] === 'undefined') {
|
||||
api['set_' + v] = function (val) {
|
||||
if (type in {'boolean':1, 'bool':1}) {
|
||||
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
|
||||
val = false;
|
||||
} else {
|
||||
val = true;
|
||||
}
|
||||
} else if (type in {'integer':1, 'int':1}) {
|
||||
val = parseInt(val, 10);
|
||||
} else if (type === 'func') {
|
||||
if (!val) {
|
||||
val = function () {};
|
||||
}
|
||||
api['set_' + v] = function (val, idx) {
|
||||
if (type in {'boolean':1, 'bool':1}) {
|
||||
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
|
||||
val = false;
|
||||
} else {
|
||||
val = true;
|
||||
}
|
||||
} else if (type in {'integer':1, 'int':1}) {
|
||||
val = parseInt(val, 10);
|
||||
} else if (type === 'func') {
|
||||
if (!val) {
|
||||
val = function () {};
|
||||
}
|
||||
}
|
||||
if (typeof idx !== 'undefined') {
|
||||
cfg[v][idx] = val;
|
||||
} else {
|
||||
cfg[v] = val;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof cfg[v] === 'undefined') {
|
||||
// Set to default
|
||||
if (type in {'arr':1, 'array':1}) {
|
||||
if (! (defval instanceof Array)) {
|
||||
defval = [];
|
||||
}
|
||||
}
|
||||
api['set_' + v](defval);
|
||||
} else {
|
||||
// Coerce existing setting to the right type
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint evil: true */
|
||||
/*global window, document, INCLUDE_URI */
|
||||
|
||||
|
@ -18,6 +17,8 @@ function get_INCLUDE_URI() {
|
|||
}
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var extra = "", start, end;
|
||||
|
||||
start = "<script src='" + get_INCLUDE_URI();
|
||||
|
@ -34,7 +35,7 @@ function get_INCLUDE_URI() {
|
|||
extra += start + "websock.js" + end;
|
||||
extra += start + "des.js" + end;
|
||||
extra += start + "input.js" + end;
|
||||
extra += start + "canvas.js" + end;
|
||||
extra += start + "display.js" + end;
|
||||
extra += start + "rfb.js" + end;
|
||||
|
||||
document.write(extra);
|
||||
|
|
|
@ -45,6 +45,7 @@ if (window.WebSocket) {
|
|||
|
||||
|
||||
function Websock() {
|
||||
"use strict";
|
||||
|
||||
var api = {}, // Public API
|
||||
websocket = null, // WebSocket object
|
||||
|
@ -75,7 +76,7 @@ function get_rQ() {
|
|||
function get_rQi() {
|
||||
return rQi;
|
||||
}
|
||||
set_rQi = function(val) {
|
||||
function set_rQi(val) {
|
||||
rQi = val;
|
||||
};
|
||||
|
||||
|
@ -252,23 +253,28 @@ function init() {
|
|||
function open(uri) {
|
||||
init();
|
||||
|
||||
websocket = new WebSocket(uri);
|
||||
websocket = new WebSocket(uri, 'base64');
|
||||
// TODO: future native binary support
|
||||
//websocket = new WebSocket(uri, ['binary', 'base64']);
|
||||
|
||||
websocket.onmessage = recv_message;
|
||||
websocket.onopen = function(e) {
|
||||
websocket.onopen = function() {
|
||||
Util.Debug(">> WebSock.onopen");
|
||||
if (websocket.protocol) {
|
||||
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
||||
}
|
||||
eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
};
|
||||
websocket.onclose = function(e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
eventHandlers.close();
|
||||
eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
};
|
||||
websocket.onerror = function(e) {
|
||||
Util.Debug("<< WebSock.onerror: " + e);
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror: ");
|
||||
Util.Debug("<< WebSock.onerror");
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -309,7 +315,6 @@ function constructor() {
|
|||
api.send = send;
|
||||
api.send_string = send_string;
|
||||
|
||||
api.recv_message = recv_message;
|
||||
api.on = on;
|
||||
api.init = init;
|
||||
api.open = open;
|
||||
|
|
|
@ -42,6 +42,16 @@
|
|||
|
||||
var rfb;
|
||||
|
||||
function passwordRequired(rfb) {
|
||||
var msg;
|
||||
msg = '<form onsubmit="return setPassword();"';
|
||||
msg += ' style="margin-bottom: 0px">';
|
||||
msg += 'Password Required: ';
|
||||
msg += '<input type=password size=10 id="password_input" class="VNC_status">';
|
||||
msg += '<\/form>';
|
||||
$D('VNC_status_bar').setAttribute("class", "VNC_status_warn");
|
||||
$D('VNC_status').innerHTML = msg;
|
||||
}
|
||||
function setPassword() {
|
||||
rfb.sendPassword($D('password_input').value);
|
||||
return false;
|
||||
|
@ -51,39 +61,24 @@
|
|||
return false;
|
||||
}
|
||||
function updateState(rfb, state, oldstate, msg) {
|
||||
var s, sb, cad, klass;
|
||||
var s, sb, cad, level;
|
||||
s = $D('VNC_status');
|
||||
sb = $D('VNC_status_bar');
|
||||
cad = $D('sendCtrlAltDelButton');
|
||||
switch (state) {
|
||||
case 'failed':
|
||||
case 'fatal':
|
||||
klass = "VNC_status_error";
|
||||
break;
|
||||
case 'normal':
|
||||
klass = "VNC_status_normal";
|
||||
break;
|
||||
case 'disconnected':
|
||||
case 'loaded':
|
||||
klass = "VNC_status_normal";
|
||||
break;
|
||||
case 'password':
|
||||
msg = '<form onsubmit="return setPassword();"';
|
||||
msg += ' style="margin-bottom: 0px">';
|
||||
msg += 'Password Required: ';
|
||||
msg += '<input type=password size=10 id="password_input" class="VNC_status">';
|
||||
msg += '<\/form>';
|
||||
klass = "VNC_status_warn";
|
||||
break;
|
||||
default:
|
||||
klass = "VNC_status_warn";
|
||||
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", klass);
|
||||
sb.setAttribute("class", "VNC_status_" + level);
|
||||
s.innerHTML = msg;
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +102,8 @@
|
|||
'true_color': WebUtil.getQueryVar('true_color', true),
|
||||
'local_cursor': WebUtil.getQueryVar('cursor', true),
|
||||
'shared': WebUtil.getQueryVar('shared', true),
|
||||
'updateState': updateState});
|
||||
'updateState': updateState,
|
||||
'onPasswordRequired': passwordRequired});
|
||||
rfb.connect(host, port, password);
|
||||
};
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue