Commit d890e864 authored by Joel Martin's avatar Joel Martin

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