You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by we...@apache.org on 2013/06/26 11:30:29 UTC
[19/25] add patches for leaseweb
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a0571d1e/patches/systemvm/debian/config/root/lswcp/include/rfb.js
----------------------------------------------------------------------
diff --git a/patches/systemvm/debian/config/root/lswcp/include/rfb.js b/patches/systemvm/debian/config/root/lswcp/include/rfb.js
new file mode 100644
index 0000000..d87040c
--- /dev/null
+++ b/patches/systemvm/debian/config/root/lswcp/include/rfb.js
@@ -0,0 +1,1863 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2011 Joel Martin
+ * Licensed under LGPL-3 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ *
+ * TIGHT decoder portion:
+ * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
+ */
+
+/*jslint white: false, browser: true, bitwise: false, plusplus: false */
+/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */
+
+
+function RFB(defaults) {
+"use strict";
+
+var that = {}, // Public API methods
+ conf = {}, // Configuration attributes
+
+ // Pre-declare private functions used before definitions (jslint)
+ init_vars, updateState, fail, handle_message,
+ init_msg, normal_msg, framebufferUpdate, print_stats,
+
+ pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests,
+ keyEvent, pointerEvent, clientCutText,
+
+ getTightCLength, extract_data_uri, scan_tight_imgQ,
+ keyPress, mouseButton, mouseMove,
+
+ checkEvents, // Overridable for testing
+
+
+ //
+ // Private RFB namespace variables
+ //
+ rfb_host = '',
+ rfb_port = 5900,
+ rfb_password = '',
+ rfb_path = '',
+
+ rfb_state = 'disconnected',
+ rfb_version = 0,
+ rfb_max_version= 3.8,
+ rfb_auth_scheme= '',
+
+
+ // In preference order
+ encodings = [
+ ['COPYRECT', 0x01 ],
+ ['TIGHT', 0x07 ],
+ ['TIGHT_PNG', -260 ],
+ ['HEXTILE', 0x05 ],
+ ['RRE', 0x02 ],
+ ['RAW', 0x00 ],
+ ['DesktopSize', -223 ],
+ ['Cursor', -239 ],
+
+ // Psuedo-encoding settings
+ //['JPEG_quality_lo', -32 ],
+ ['JPEG_quality_med', -26 ],
+ //['JPEG_quality_hi', -23 ],
+ //['compress_lo', -255 ],
+ ['compress_hi', -247 ],
+ ['last_rect', -224 ]
+ ],
+
+ encHandlers = {},
+ encNames = {},
+ encStats = {}, // [rectCnt, rectCntTot]
+
+ ws = null, // Websock object
+ display = null, // Display object
+ keyboard = null, // Keyboard input handler object
+ mouse = null, // Mouse input handler object
+ sendTimer = null, // Send Queue check timer
+ connTimer = null, // connection timer
+ disconnTimer = null, // disconnection timer
+ msgTimer = null, // queued handle_message timer
+
+ // Frame buffer update state
+ FBU = {
+ rects : 0,
+ subrects : 0, // RRE
+ lines : 0, // RAW
+ tiles : 0, // HEXTILE
+ bytes : 0,
+ x : 0,
+ y : 0,
+ width : 0,
+ height : 0,
+ encoding : 0,
+ subencoding : -1,
+ background : null,
+ imgQ : [], // TIGHT_PNG image queue
+ zlibs : [] // TIGHT zlib streams
+ },
+
+ fb_Bpp = 4,
+ fb_depth = 3,
+ fb_width = 0,
+ fb_height = 0,
+ fb_name = "",
+
+ scan_imgQ_rate = 40, // 25 times per second or so
+ last_req_time = 0,
+ rre_chunk_sz = 100,
+
+ timing = {
+ last_fbu : 0,
+ fbu_total : 0,
+ fbu_total_cnt : 0,
+ full_fbu_total : 0,
+ full_fbu_cnt : 0,
+
+ fbu_rt_start : 0,
+ fbu_rt_total : 0,
+ fbu_rt_cnt : 0,
+ pixels : 0
+ },
+
+ test_mode = false,
+
+ def_con_timeout = Websock_native ? 2 : 5,
+
+ /* Mouse state */
+ mouse_buttonMask = 0,
+ mouse_arr = [],
+ viewportDragging = false,
+ viewportDragPos = {};
+
+// Configuration attributes
+Util.conf_defaults(conf, that, defaults, [
+ ['target', 'wo', 'dom', null, 'VNC display rendering Canvas object'],
+ ['focusContainer', 'wo', 'dom', document, 'DOM element that captures keyboard input'],
+
+ ['encrypt', 'rw', 'bool', false, 'Use TLS/SSL/wss encryption'],
+ ['true_color', 'rw', 'bool', true, 'Request true color pixel data'],
+ ['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'],
+ ['shared', 'rw', 'bool', true, 'Request shared mode'],
+ ['view_only', 'rw', 'bool', false, 'Disable client mouse/keyboard'],
+
+ ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'],
+ ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
+
+ ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'],
+
+ ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
+ ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
+
+ // Callback functions
+ ['onUpdateState', 'rw', 'func', function() { },
+ 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '],
+ ['onPasswordRequired', 'rw', 'func', function() { },
+ 'onPasswordRequired(rfb): VNC password is required '],
+ ['onClipboard', 'rw', 'func', function() { },
+ 'onClipboard(rfb, text): RFB clipboard contents received'],
+ ['onBell', 'rw', 'func', function() { },
+ 'onBell(rfb): RFB Bell message received '],
+ ['onFBUReceive', 'rw', 'func', function() { },
+ 'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '],
+ ['onFBUComplete', 'rw', 'func', function() { },
+ 'onFBUComplete(rfb, fbu): RFB FBU received and processed '],
+
+ // These callback names are deprecated
+ ['updateState', 'rw', 'func', function() { },
+ 'obsolete, use onUpdateState'],
+ ['clipboardReceive', 'rw', 'func', function() { },
+ 'obsolete, use onClipboard']
+ ]);
+
+
+// Override/add some specific configuration getters/setters
+that.set_local_cursor = function(cursor) {
+ if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
+ conf.local_cursor = false;
+ } else {
+ if (display.get_cursor_uri()) {
+ conf.local_cursor = true;
+ } else {
+ Util.Warn("Browser does not support local cursor");
+ }
+ }
+};
+
+// These are fake configuration getters
+that.get_display = function() { return display; };
+
+that.get_keyboard = function() { return keyboard; };
+
+that.get_mouse = function() { return mouse; };
+
+
+
+//
+// Setup routines
+//
+
+// Create the public API interface and initialize values that stay
+// constant across connect/disconnect
+function constructor() {
+ var i, rmode;
+ Util.Debug(">> RFB.constructor");
+
+ // Create lookup tables based encoding number
+ for (i=0; i < encodings.length; i+=1) {
+ encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]];
+ encNames[encodings[i][1]] = encodings[i][0];
+ encStats[encodings[i][1]] = [0, 0];
+ }
+ // Initialize display, mouse, keyboard, and websock
+ try {
+ display = new Display({'target': conf.target});
+ } catch (exc) {
+ Util.Error("Display exception: " + exc);
+ updateState('fatal', "No working Display");
+ }
+ keyboard = new Keyboard({'target': conf.focusContainer,
+ 'onKeyPress': keyPress});
+ mouse = new Mouse({'target': conf.target,
+ 'onMouseButton': mouseButton,
+ 'onMouseMove': mouseMove});
+
+ rmode = display.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(e) {
+ if (e.code) {
+ Util.Info("Close code: " + e.code + ", reason: " + e.reason + ", wasClean: " + e.wasClean);
+ }
+ 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();
+
+ /* Check web-socket-js if no builtin WebSocket support */
+ if (Websock_native) {
+ Util.Info("Using native WebSockets");
+ updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
+ } else {
+ Util.Warn("Using web-socket-js bridge. Flash version: " +
+ Util.Flash.version);
+ if ((! Util.Flash) ||
+ (Util.Flash.version < 9)) {
+ updateState('fatal', "WebSockets or <a href='http://get.adobe.com/flashplayer'>Adobe Flash<\/a> is required");
+ } else if (document.location.href.substr(0, 7) === "file://") {
+ updateState('fatal',
+ "'file://' URL is incompatible with Adobe Flash");
+ } else {
+ updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
+ }
+ }
+
+ Util.Debug("<< RFB.constructor");
+ return that; // Return the public API interface
+}
+
+function connect() {
+ Util.Debug(">> RFB.connect");
+ var uri;
+
+ if (typeof UsingSocketIO !== "undefined") {
+ uri = "http://" + rfb_host + ":" + rfb_port + "/" + rfb_path;
+ } else {
+ if (conf.encrypt) {
+ uri = "wss://";
+ } else {
+ uri = "ws://";
+ }
+ uri += rfb_host + ":" + rfb_port + "/" + rfb_path;
+ }
+ Util.Info("connecting to " + uri);
+ ws.open(uri);
+
+ Util.Debug("<< RFB.connect");
+}
+
+// Initialize variables that are reset before each connection
+init_vars = function() {
+ var i;
+
+ /* Reset state */
+ ws.init();
+
+ FBU.rects = 0;
+ FBU.subrects = 0; // RRE and HEXTILE
+ FBU.lines = 0; // RAW
+ FBU.tiles = 0; // HEXTILE
+ FBU.imgQ = []; // TIGHT_PNG image queue
+ FBU.zlibs = []; // TIGHT zlib encoders
+ mouse_buttonMask = 0;
+ mouse_arr = [];
+
+ // Clear the per connection encoding stats
+ for (i=0; i < encodings.length; i+=1) {
+ encStats[encodings[i][1]][0] = 0;
+ }
+
+ for (i=0; i < 4; i++) {
+ //FBU.zlibs[i] = new InflateStream();
+ FBU.zlibs[i] = new TINF();
+ FBU.zlibs[i].init();
+ }
+};
+
+// Print statistics
+print_stats = function() {
+ var i, s;
+ Util.Info("Encoding stats for this connection:");
+ for (i=0; i < encodings.length; i+=1) {
+ s = encStats[encodings[i][1]];
+ if ((s[0] + s[1]) > 0) {
+ Util.Info(" " + encodings[i][0] + ": " +
+ s[0] + " rects");
+ }
+ }
+ Util.Info("Encoding stats since page load:");
+ for (i=0; i < encodings.length; i+=1) {
+ s = encStats[encodings[i][1]];
+ if ((s[0] + s[1]) > 0) {
+ Util.Info(" " + encodings[i][0] + ": " +
+ s[1] + " rects");
+ }
+ }
+};
+
+//
+// Utility routines
+//
+
+
+/*
+ * Page states:
+ * loaded - page load, equivalent 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
+ *
+ * RFB protocol initialization states:
+ * ProtocolVersion
+ * Security
+ * Authentication
+ * password - waiting for password, not part of RFB
+ * SecurityResult
+ * ClientInitialization - not triggered by server message
+ * ServerInitialization (to normal)
+ */
+updateState = function(state, statusMsg) {
+ var func, cmsg, oldstate = rfb_state;
+
+ if (state === oldstate) {
+ /* Already here, ignore */
+ Util.Debug("Already in state '" + state + "', ignoring.");
+ return;
+ }
+
+ /*
+ * These are disconnected states. A previous connect may
+ * asynchronously cause a connection so make sure we are closed.
+ */
+ if (state in {'disconnected':1, 'loaded':1, 'connect':1,
+ 'disconnect':1, 'failed':1, 'fatal':1}) {
+ if (sendTimer) {
+ clearInterval(sendTimer);
+ sendTimer = null;
+ }
+
+ if (msgTimer) {
+ clearInterval(msgTimer);
+ msgTimer = null;
+ }
+
+ if (display && display.get_context()) {
+ keyboard.ungrab();
+ mouse.ungrab();
+ display.defaultCursor();
+ if ((Util.get_logging() !== 'debug') ||
+ (state === 'loaded')) {
+ // Show noVNC logo on load and when disconnected if
+ // debug is off
+ display.clear();
+ }
+ }
+
+ ws.close();
+ }
+
+ if (oldstate === 'fatal') {
+ Util.Error("Fatal error, cannot continue");
+ }
+
+ if ((state === 'failed') || (state === 'fatal')) {
+ func = Util.Error;
+ } else {
+ func = Util.Warn;
+ }
+
+ if ((oldstate === 'failed') && (state === 'disconnected')) {
+ // Do disconnect action, but stay in failed state.
+ rfb_state = 'failed';
+ } else {
+ rfb_state = state;
+ }
+
+ cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
+ func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg);
+
+ if (connTimer && (rfb_state !== 'connect')) {
+ Util.Debug("Clearing connect timer");
+ clearInterval(connTimer);
+ connTimer = null;
+ }
+
+ if (disconnTimer && (rfb_state !== 'disconnect')) {
+ Util.Debug("Clearing disconnect timer");
+ clearInterval(disconnTimer);
+ disconnTimer = null;
+ }
+
+ switch (state) {
+ case 'normal':
+ if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
+ Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
+ }
+
+ break;
+
+
+ case 'connect':
+
+ connTimer = setTimeout(function () {
+ fail("Connect timeout");
+ }, conf.connectTimeout * 1000);
+
+ init_vars();
+ connect();
+
+ // WebSocket.onopen transitions to 'ProtocolVersion'
+ break;
+
+
+ case 'disconnect':
+
+ if (! test_mode) {
+ disconnTimer = setTimeout(function () {
+ fail("Disconnect timeout");
+ }, conf.disconnectTimeout * 1000);
+ }
+
+ print_stats();
+
+ // WebSocket.onclose transitions to 'disconnected'
+ break;
+
+
+ case 'failed':
+ if (oldstate === 'disconnected') {
+ Util.Error("Invalid transition from 'disconnected' to 'failed'");
+ }
+ if (oldstate === 'normal') {
+ Util.Error("Error while connected.");
+ }
+ if (oldstate === 'init') {
+ Util.Error("Error while initializing.");
+ }
+
+ // Make sure we transition to disconnected
+ setTimeout(function() { updateState('disconnected'); }, 50);
+
+ break;
+
+
+ default:
+ // No state change action to take
+
+ }
+
+ if ((oldstate === 'failed') && (state === 'disconnected')) {
+ // Leave the failed message
+ conf.updateState(that, state, oldstate); // Obsolete
+ conf.onUpdateState(that, state, oldstate);
+ } else {
+ conf.updateState(that, state, oldstate, statusMsg); // Obsolete
+ conf.onUpdateState(that, state, oldstate, statusMsg);
+ }
+};
+
+fail = function(msg) {
+ updateState('failed', msg);
+ return false;
+};
+
+handle_message = function() {
+ //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen());
+ //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
+ if (ws.rQlen() === 0) {
+ Util.Warn("handle_message called on empty receive queue");
+ return;
+ }
+ switch (rfb_state) {
+ case 'disconnected':
+ case 'failed':
+ Util.Error("Got data while disconnected");
+ break;
+ case 'normal':
+ if (normal_msg() && ws.rQlen() > 0) {
+ // true means we can continue processing
+ // Give other events a chance to run
+ if (msgTimer === null) {
+ Util.Debug("More data to process, creating timer");
+ msgTimer = setTimeout(function () {
+ msgTimer = null;
+ handle_message();
+ }, 10);
+ } else {
+ Util.Debug("More data to process, existing timer");
+ }
+ }
+ break;
+ default:
+ init_msg();
+ break;
+ }
+};
+
+
+function genDES(password, challenge) {
+ var i, passwd = [];
+ for (i=0; i < password.length; i += 1) {
+ passwd.push(password.charCodeAt(i));
+ }
+ return (new DES(passwd)).encrypt(challenge);
+}
+
+function flushClient() {
+ if (mouse_arr.length > 0) {
+ //send(mouse_arr.concat(fbUpdateRequests()));
+ ws.send(mouse_arr);
+ setTimeout(function() {
+ ws.send(fbUpdateRequests());
+ }, 50);
+
+ mouse_arr = [];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// overridable for testing
+checkEvents = function() {
+ var now;
+ if (rfb_state === 'normal' && !viewportDragging) {
+ if (! flushClient()) {
+ now = new Date().getTime();
+ if (now > last_req_time + conf.fbu_req_rate) {
+ last_req_time = now;
+ ws.send(fbUpdateRequests());
+ }
+ }
+ }
+ setTimeout(checkEvents, conf.check_rate);
+};
+
+keyPress = function(keysym, down) {
+ var arr;
+
+ if (conf.view_only) { return; } // View only, skip keyboard events
+
+ arr = keyEvent(keysym, down);
+ arr = arr.concat(fbUpdateRequests());
+ ws.send(arr);
+};
+
+mouseButton = function(x, y, down, bmask) {
+ if (down) {
+ mouse_buttonMask |= bmask;
+ } else {
+ mouse_buttonMask ^= bmask;
+ }
+
+ if (conf.viewportDrag) {
+ if (down && !viewportDragging) {
+ viewportDragging = true;
+ viewportDragPos = {'x': x, 'y': y};
+
+ // Skip sending mouse events
+ return;
+ } else {
+ viewportDragging = false;
+ ws.send(fbUpdateRequests()); // Force immediate redraw
+ }
+ }
+
+ if (conf.view_only) { return; } // View only, skip mouse events
+
+ mouse_arr = mouse_arr.concat(
+ pointerEvent(display.absX(x), display.absY(y)) );
+ flushClient();
+};
+
+mouseMove = function(x, y) {
+ //Util.Debug('>> mouseMove ' + x + "," + y);
+ var deltaX, deltaY;
+
+ if (viewportDragging) {
+ //deltaX = x - viewportDragPos.x; // drag viewport
+ deltaX = viewportDragPos.x - x; // drag frame buffer
+ //deltaY = y - viewportDragPos.y; // drag viewport
+ deltaY = viewportDragPos.y - y; // drag frame buffer
+ viewportDragPos = {'x': x, 'y': y};
+
+ display.viewportChange(deltaX, deltaY);
+
+ // Skip sending mouse events
+ return;
+ }
+
+ if (conf.view_only) { return; } // View only, skip mouse events
+
+ mouse_arr = mouse_arr.concat(
+ pointerEvent(display.absX(x), display.absY(y)) );
+};
+
+
+//
+// Server message handlers
+//
+
+// RFB/VNC initialisation message handler
+init_msg = function() {
+ //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
+
+ var strlen, reason, length, sversion, cversion,
+ i, types, num_types, challenge, response, bpp, depth,
+ big_endian, red_max, green_max, blue_max, red_shift,
+ green_shift, blue_shift, true_color, name_length;
+
+ //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));
+ switch (rfb_state) {
+
+ case 'ProtocolVersion' :
+ if (ws.rQlen() < 12) {
+ return fail("Incomplete protocol version");
+ }
+ sversion = ws.rQshiftStr(12).substr(4,7);
+ Util.Info("Server ProtocolVersion: " + sversion);
+ switch (sversion) {
+ case "003.003": rfb_version = 3.3; break;
+ case "003.006": rfb_version = 3.3; break; // UltraVNC
+ case "003.007": rfb_version = 3.7; break;
+ case "003.008": rfb_version = 3.8; break;
+ case "004.000": rfb_version = 3.8; break; // Intel AMT KVM
+ default:
+ return fail("Invalid server version " + sversion);
+ }
+ if (rfb_version > rfb_max_version) {
+ rfb_version = rfb_max_version;
+ }
+
+ if (! test_mode) {
+ sendTimer = setInterval(function() {
+ // Send updates either at a rate of one update
+ // every 50ms, or whatever slower rate the network
+ // can handle.
+ ws.flush();
+ }, 50);
+ }
+
+ cversion = "00" + parseInt(rfb_version,10) +
+ ".00" + ((rfb_version * 10) % 10);
+ ws.send_string("RFB " + cversion + "\n");
+ updateState('Security', "Sent ProtocolVersion: " + cversion);
+ break;
+
+ case 'Security' :
+ if (rfb_version >= 3.7) {
+ // Server sends supported list, client decides
+ num_types = ws.rQshift8();
+ if (ws.rQwait("security type", num_types, 1)) { return false; }
+ if (num_types === 0) {
+ strlen = ws.rQshift32();
+ reason = ws.rQshiftStr(strlen);
+ return fail("Security failure: " + reason);
+ }
+ rfb_auth_scheme = 0;
+ types = ws.rQshiftBytes(num_types);
+ Util.Debug("Server security types: " + types);
+ for (i=0; i < types.length; i+=1) {
+ if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {
+ rfb_auth_scheme = types[i];
+ }
+ }
+ if (rfb_auth_scheme === 0) {
+ return fail("Unsupported security types: " + types);
+ }
+
+ ws.send([rfb_auth_scheme]);
+ } else {
+ // Server decides
+ if (ws.rQwait("security scheme", 4)) { return false; }
+ rfb_auth_scheme = ws.rQshift32();
+ }
+ updateState('Authentication',
+ "Authenticating using scheme: " + rfb_auth_scheme);
+ init_msg(); // Recursive fallthrough (workaround JSLint complaint)
+ break;
+
+ // Triggered by fallthough, not by server message
+ case 'Authentication' :
+ //Util.Debug("Security auth scheme: " + rfb_auth_scheme);
+ switch (rfb_auth_scheme) {
+ case 0: // connection failed
+ if (ws.rQwait("auth reason", 4)) { return false; }
+ strlen = ws.rQshift32();
+ reason = ws.rQshiftStr(strlen);
+ return fail("Auth failure: " + reason);
+ case 1: // no authentication
+ if (rfb_version >= 3.8) {
+ updateState('SecurityResult');
+ return;
+ }
+ // Fall through to ClientInitialisation
+ 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; }
+ challenge = ws.rQshiftBytes(16);
+ //Util.Debug("Password: " + rfb_password);
+ //Util.Debug("Challenge: " + challenge +
+ // " (" + challenge.length + ")");
+ response = genDES(rfb_password, challenge);
+ //Util.Debug("Response: " + response +
+ // " (" + response.length + ")");
+
+ //Util.Debug("Sending DES encrypted auth response");
+ ws.send(response);
+ updateState('SecurityResult');
+ return;
+ default:
+ fail("Unsupported auth scheme: " + rfb_auth_scheme);
+ return;
+ }
+ updateState('ClientInitialisation', "No auth required");
+ init_msg(); // Recursive fallthrough (workaround JSLint complaint)
+ break;
+
+ case 'SecurityResult' :
+ if (ws.rQwait("VNC auth response ", 4)) { return false; }
+ switch (ws.rQshift32()) {
+ case 0: // OK
+ // Fall through to ClientInitialisation
+ break;
+ case 1: // failed
+ if (rfb_version >= 3.8) {
+ length = ws.rQshift32();
+ if (ws.rQwait("SecurityResult reason", length, 8)) {
+ return false;
+ }
+ reason = ws.rQshiftStr(length);
+ fail(reason);
+ } else {
+ fail("Authentication failed");
+ }
+ return;
+ case 2: // too-many
+ return fail("Too many auth attempts");
+ }
+ updateState('ClientInitialisation', "Authentication OK");
+ init_msg(); // Recursive fallthrough (workaround JSLint complaint)
+ break;
+
+ // Triggered by fallthough, not by server message
+ case 'ClientInitialisation' :
+ ws.send([conf.shared ? 1 : 0]); // ClientInitialisation
+ updateState('ServerInitialisation', "Authentication OK");
+ break;
+
+ case 'ServerInitialisation' :
+ if (ws.rQwait("server initialization", 24)) { return false; }
+
+ /* Screen size */
+ fb_width = ws.rQshift16();
+ fb_height = ws.rQshift16();
+
+ /* PIXEL_FORMAT */
+ bpp = ws.rQshift8();
+ depth = ws.rQshift8();
+ big_endian = ws.rQshift8();
+ true_color = ws.rQshift8();
+
+ red_max = ws.rQshift16();
+ green_max = ws.rQshift16();
+ blue_max = ws.rQshift16();
+ red_shift = ws.rQshift8();
+ green_shift = ws.rQshift8();
+ blue_shift = ws.rQshift8();
+ ws.rQshiftStr(3); // padding
+
+ Util.Info("Screen: " + fb_width + "x" + fb_height +
+ ", bpp: " + bpp + ", depth: " + depth +
+ ", big_endian: " + big_endian +
+ ", true_color: " + true_color +
+ ", red_max: " + red_max +
+ ", green_max: " + green_max +
+ ", blue_max: " + blue_max +
+ ", red_shift: " + red_shift +
+ ", green_shift: " + green_shift +
+ ", blue_shift: " + blue_shift);
+
+ if (big_endian !== 0) {
+ Util.Warn("Server native endian is not little endian");
+ }
+ if (red_shift !== 16) {
+ Util.Warn("Server native red-shift is not 16");
+ }
+ if (blue_shift !== 0) {
+ Util.Warn("Server native blue-shift is not 0");
+ }
+
+ /* Connection name/title */
+ name_length = ws.rQshift32();
+ fb_name = ws.rQshiftStr(name_length);
+
+ if (conf.true_color && fb_name === "Intel(r) AMT KVM")
+ {
+ Util.Warn("Intel AMT KVM only support 8/16 bit depths. Disabling true color");
+ conf.true_color = false;
+ }
+
+ display.set_true_color(conf.true_color);
+ display.resize(fb_width, fb_height);
+ keyboard.grab();
+ mouse.grab();
+
+ if (conf.true_color) {
+ fb_Bpp = 4;
+ fb_depth = 3;
+ } else {
+ fb_Bpp = 1;
+ fb_depth = 1;
+ }
+
+ response = pixelFormat();
+ response = response.concat(clientEncodings());
+ response = response.concat(fbUpdateRequests());
+ timing.fbu_rt_start = (new Date()).getTime();
+ ws.send(response);
+
+ /* Start pushing/polling */
+ setTimeout(checkEvents, conf.check_rate);
+ setTimeout(scan_tight_imgQ, scan_imgQ_rate);
+
+ if (conf.encrypt) {
+ updateState('normal', "Connected (encrypted) to: " + fb_name);
+ } else {
+ updateState('normal', "Connected (unencrypted) to: " + fb_name);
+ }
+ break;
+ }
+ //Util.Debug("<< init_msg");
+};
+
+
+/* Normal RFB/VNC server message handler */
+normal_msg = function() {
+ //Util.Debug(">> normal_msg");
+
+ var ret = true, msg_type, length, text,
+ c, first_colour, num_colours, red, green, blue;
+
+ if (FBU.rects > 0) {
+ msg_type = 0;
+ } else {
+ msg_type = ws.rQshift8();
+ }
+ switch (msg_type) {
+ case 0: // FramebufferUpdate
+ ret = framebufferUpdate(); // false means need more data
+ break;
+ case 1: // SetColourMapEntries
+ Util.Debug("SetColourMapEntries");
+ ws.rQshift8(); // Padding
+ first_colour = ws.rQshift16(); // First colour
+ num_colours = ws.rQshift16();
+ if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }
+
+ for (c=0; c < num_colours; c+=1) {
+ red = ws.rQshift16();
+ //Util.Debug("red before: " + red);
+ red = parseInt(red / 256, 10);
+ //Util.Debug("red after: " + red);
+ green = parseInt(ws.rQshift16() / 256, 10);
+ blue = parseInt(ws.rQshift16() / 256, 10);
+ display.set_colourMap([blue, green, red], first_colour + c);
+ }
+ Util.Debug("colourMap: " + display.get_colourMap());
+ Util.Info("Registered " + num_colours + " colourMap entries");
+ //Util.Debug("colourMap: " + display.get_colourMap());
+ break;
+ case 2: // Bell
+ Util.Debug("Bell");
+ conf.onBell(that);
+ break;
+ case 3: // ServerCutText
+ Util.Debug("ServerCutText");
+ if (ws.rQwait("ServerCutText header", 7, 1)) { return false; }
+ ws.rQshiftBytes(3); // Padding
+ length = ws.rQshift32();
+ if (ws.rQwait("ServerCutText", length, 8)) { return false; }
+
+ text = ws.rQshiftStr(length);
+ conf.clipboardReceive(that, text); // Obsolete
+ conf.onClipboard(that, text);
+ break;
+ default:
+ fail("Disconnected: illegal server message type " + msg_type);
+ Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
+ break;
+ }
+ //Util.Debug("<< normal_msg");
+ return ret;
+};
+
+framebufferUpdate = function() {
+ var now, hdr, fbu_rt_diff, ret = true;
+
+ if (FBU.rects === 0) {
+ //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20));
+ if (ws.rQwait("FBU header", 3)) {
+ ws.rQunshift8(0); // FBU msg_type
+ return false;
+ }
+ ws.rQshift8(); // padding
+ FBU.rects = ws.rQshift16();
+ //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);
+ FBU.bytes = 0;
+ timing.cur_fbu = 0;
+ if (timing.fbu_rt_start > 0) {
+ now = (new Date()).getTime();
+ Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));
+ }
+ }
+
+ while (FBU.rects > 0) {
+ if (rfb_state !== "normal") {
+ return false;
+ }
+ if (ws.rQwait("FBU", FBU.bytes)) { return false; }
+ if (FBU.bytes === 0) {
+ if (ws.rQwait("rect header", 12)) { return false; }
+ /* New FramebufferUpdate */
+
+ hdr = ws.rQshiftBytes(12);
+ FBU.x = (hdr[0] << 8) + hdr[1];
+ FBU.y = (hdr[2] << 8) + hdr[3];
+ FBU.width = (hdr[4] << 8) + hdr[5];
+ FBU.height = (hdr[6] << 8) + hdr[7];
+ 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:
+ /*
+ var msg = "FramebufferUpdate rects:" + FBU.rects;
+ msg += " x: " + FBU.x + " y: " + FBU.y;
+ msg += " width: " + FBU.width + " height: " + FBU.height;
+ msg += " encoding:" + FBU.encoding;
+ msg += "(" + encNames[FBU.encoding] + ")";
+ msg += ", ws.rQlen(): " + ws.rQlen();
+ Util.Debug(msg);
+ */
+ } else {
+ fail("Disconnected: unsupported encoding " +
+ FBU.encoding);
+ return false;
+ }
+ }
+
+ timing.last_fbu = (new Date()).getTime();
+
+ ret = encHandlers[FBU.encoding]();
+
+ now = (new Date()).getTime();
+ timing.cur_fbu += (now - timing.last_fbu);
+
+ if (ret) {
+ encStats[FBU.encoding][0] += 1;
+ encStats[FBU.encoding][1] += 1;
+ timing.pixels += FBU.width * FBU.height;
+ }
+
+ if (FBU.rects === 0 || (timing.pixels >= (fb_width * fb_height))) {
+ if (((FBU.width === fb_width) &&
+ (FBU.height === fb_height)) ||
+ (timing.fbu_rt_start > 0)) {
+ timing.full_fbu_total += timing.cur_fbu;
+ timing.full_fbu_cnt += 1;
+ Util.Info("Timing of full FBU, cur: " +
+ timing.cur_fbu + ", total: " +
+ timing.full_fbu_total + ", cnt: " +
+ timing.full_fbu_cnt + ", avg: " +
+ (timing.full_fbu_total /
+ timing.full_fbu_cnt));
+ }
+ if (timing.fbu_rt_start > 0) {
+ fbu_rt_diff = now - timing.fbu_rt_start;
+ timing.fbu_rt_total += fbu_rt_diff;
+ timing.fbu_rt_cnt += 1;
+ Util.Info("full FBU round-trip, cur: " +
+ fbu_rt_diff + ", total: " +
+ timing.fbu_rt_total + ", cnt: " +
+ timing.fbu_rt_cnt + ", avg: " +
+ (timing.fbu_rt_total /
+ timing.fbu_rt_cnt));
+ timing.fbu_rt_start = 0;
+ }
+ }
+ if (! ret) {
+ 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
+};
+
+//
+// FramebufferUpdate encodings
+//
+
+encHandlers.RAW = function display_raw() {
+ //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)");
+
+ var cur_y, cur_height;
+
+ if (FBU.lines === 0) {
+ FBU.lines = FBU.height;
+ }
+ FBU.bytes = FBU.width * fb_Bpp; // At least a line
+ if (ws.rQwait("RAW", FBU.bytes)) { return false; }
+ cur_y = FBU.y + (FBU.height - FBU.lines);
+ cur_height = Math.min(FBU.lines,
+ Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));
+ 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;
+
+ if (FBU.lines > 0) {
+ FBU.bytes = FBU.width * fb_Bpp; // At least another line
+ } else {
+ FBU.rects -= 1;
+ FBU.bytes = 0;
+ }
+ //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)");
+ return true;
+};
+
+encHandlers.COPYRECT = function display_copy_rect() {
+ //Util.Debug(">> display_copy_rect");
+
+ var old_x, old_y;
+
+ if (ws.rQwait("COPYRECT", 4)) { return false; }
+ old_x = ws.rQshift16();
+ old_y = ws.rQshift16();
+ display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
+ FBU.rects -= 1;
+ FBU.bytes = 0;
+ return true;
+};
+
+encHandlers.RRE = function display_rre() {
+ //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)");
+ var color, x, y, width, height, chunk;
+
+ if (FBU.subrects === 0) {
+ if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
+ FBU.subrects = ws.rQshift32();
+ color = ws.rQshiftBytes(fb_Bpp); // Background
+ 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);
+ x = ws.rQshift16();
+ y = ws.rQshift16();
+ width = ws.rQshift16();
+ height = ws.rQshift16();
+ display.fillRect(FBU.x + x, FBU.y + y, width, height, color);
+ FBU.subrects -= 1;
+ }
+ //Util.Debug(" display_rre: rects: " + FBU.rects +
+ // ", FBU.subrects: " + FBU.subrects);
+
+ if (FBU.subrects > 0) {
+ chunk = Math.min(rre_chunk_sz, FBU.subrects);
+ FBU.bytes = (fb_Bpp + 8) * chunk;
+ } else {
+ FBU.rects -= 1;
+ FBU.bytes = 0;
+ }
+ //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);
+ return true;
+};
+
+encHandlers.HEXTILE = function display_hextile() {
+ //Util.Debug(">> display_hextile");
+ var subencoding, subrects, color, cur_tile,
+ tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,
+ rQ = ws.get_rQ(), rQi = ws.get_rQi();
+
+ if (FBU.tiles === 0) {
+ FBU.tiles_x = Math.ceil(FBU.width/16);
+ FBU.tiles_y = Math.ceil(FBU.height/16);
+ FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
+ FBU.tiles = FBU.total_tiles;
+ }
+
+ /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */
+ while (FBU.tiles > 0) {
+ FBU.bytes = 1;
+ if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; }
+ subencoding = rQ[rQi]; // Peek
+ if (subencoding > 30) { // Raw
+ fail("Disconnected: illegal hextile subencoding " + subencoding);
+ //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
+ return false;
+ }
+ subrects = 0;
+ cur_tile = FBU.total_tiles - FBU.tiles;
+ tile_x = cur_tile % FBU.tiles_x;
+ tile_y = Math.floor(cur_tile / FBU.tiles_x);
+ x = FBU.x + tile_x * 16;
+ y = FBU.y + tile_y * 16;
+ w = Math.min(16, (FBU.x + FBU.width) - x);
+ h = Math.min(16, (FBU.y + FBU.height) - y);
+
+ /* Figure out how much we are expecting */
+ if (subencoding & 0x01) { // Raw
+ //Util.Debug(" Raw subencoding");
+ FBU.bytes += w * h * fb_Bpp;
+ } else {
+ if (subencoding & 0x02) { // Background
+ FBU.bytes += fb_Bpp;
+ }
+ if (subencoding & 0x04) { // Foreground
+ FBU.bytes += fb_Bpp;
+ }
+ if (subencoding & 0x08) { // AnySubrects
+ FBU.bytes += 1; // Since we aren't shifting it off
+ if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; }
+ subrects = rQ[rQi + FBU.bytes-1]; // Peek
+ if (subencoding & 0x10) { // SubrectsColoured
+ FBU.bytes += subrects * (fb_Bpp + 2);
+ } else {
+ FBU.bytes += subrects * 2;
+ }
+ }
+ }
+
+ /*
+ Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
+ " (" + tile_x + "," + tile_y + ")" +
+ " [" + x + "," + y + "]@" + w + "x" + h +
+ ", subenc:" + subencoding +
+ "(last: " + FBU.lastsubencoding + "), subrects:" +
+ subrects +
+ ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes +
+ " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) +
+ " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10));
+ */
+ if (ws.rQwait("hextile", FBU.bytes)) { return false; }
+
+ /* We know the encoding and have a whole tile */
+ FBU.subencoding = rQ[rQi];
+ rQi += 1;
+ if (FBU.subencoding === 0) {
+ if (FBU.lastsubencoding & 0x01) {
+ /* Weird: ignore blanks after RAW */
+ Util.Debug(" Ignoring blank after RAW");
+ } else {
+ display.fillRect(x, y, w, h, FBU.background);
+ }
+ } else if (FBU.subencoding & 0x01) { // Raw
+ display.blitImage(x, y, w, h, rQ, rQi);
+ rQi += FBU.bytes - 1;
+ } else {
+ if (FBU.subencoding & 0x02) { // Background
+ FBU.background = rQ.slice(rQi, rQi + fb_Bpp);
+ rQi += fb_Bpp;
+ }
+ if (FBU.subencoding & 0x04) { // Foreground
+ FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);
+ rQi += fb_Bpp;
+ }
+
+ display.startTile(x, y, w, h, FBU.background);
+ if (FBU.subencoding & 0x08) { // AnySubrects
+ subrects = rQ[rQi];
+ rQi += 1;
+ for (s = 0; s < subrects; s += 1) {
+ if (FBU.subencoding & 0x10) { // SubrectsColoured
+ color = rQ.slice(rQi, rQi + fb_Bpp);
+ rQi += fb_Bpp;
+ } else {
+ color = FBU.foreground;
+ }
+ xy = rQ[rQi];
+ rQi += 1;
+ sx = (xy >> 4);
+ sy = (xy & 0x0f);
+
+ wh = rQ[rQi];
+ rQi += 1;
+ sw = (wh >> 4) + 1;
+ sh = (wh & 0x0f) + 1;
+
+ display.subTile(sx, sy, sw, sh, color);
+ }
+ }
+ display.finishTile();
+ }
+ ws.set_rQi(rQi);
+ FBU.lastsubencoding = FBU.subencoding;
+ FBU.bytes = 0;
+ FBU.tiles -= 1;
+ }
+
+ if (FBU.tiles === 0) {
+ FBU.rects -= 1;
+ }
+
+ //Util.Debug("<< display_hextile");
+ return true;
+};
+
+
+// Get 'compact length' header and data size
+getTightCLength = function (arr) {
+ var header = 1, data = 0;
+ data += arr[0] & 0x7f;
+ if (arr[0] & 0x80) {
+ header += 1;
+ data += (arr[1] & 0x7f) << 7;
+ if (arr[1] & 0x80) {
+ header += 1;
+ data += arr[2] << 14;
+ }
+ }
+ return [header, data];
+};
+
+function display_tight(isTightPNG) {
+ //Util.Debug(">> display_tight");
+
+ if (fb_depth === 1) {
+ fail("Tight protocol handler only implements true color mode");
+ }
+
+ var ctl, cmode, clength, color, img, data;
+ var filterId = -1, resetStreams = 0, streamId = -1;
+ var rQ = ws.get_rQ(), rQi = ws.get_rQi();
+
+ FBU.bytes = 1; // compression-control byte
+ if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }
+
+ var checksum = function(data) {
+ var sum=0, i;
+ for (i=0; i<data.length;i++) {
+ sum += data[i];
+ if (sum > 65536) sum -= 65536;
+ }
+ return sum;
+ }
+
+ var decompress = function(data) {
+ for (var i=0; i<4; i++) {
+ if ((resetStreams >> i) & 1) {
+ FBU.zlibs[i].reset();
+ Util.Info("Reset zlib stream " + i);
+ }
+ }
+ var uncompressed = FBU.zlibs[streamId].uncompress(data, 0);
+ if (uncompressed.status !== 0) {
+ Util.Error("Invalid data in zlib stream");
+ }
+ //Util.Warn("Decompressed " + data.length + " to " +
+ // uncompressed.data.length + " checksums " +
+ // checksum(data) + ":" + checksum(uncompressed.data));
+
+ return uncompressed.data;
+ }
+
+ var handlePalette = function() {
+ var numColors = rQ[rQi + 2] + 1;
+ var paletteSize = numColors * fb_depth;
+ FBU.bytes += paletteSize;
+ if (ws.rQwait("TIGHT palette " + cmode, FBU.bytes)) { return false; }
+
+ var bpp = (numColors <= 2) ? 1 : 8;
+ var rowSize = Math.floor((FBU.width * bpp + 7) / 8);
+ var raw = false;
+ if (rowSize * FBU.height < 12) {
+ raw = true;
+ clength = [0, rowSize * FBU.height];
+ } else {
+ clength = getTightCLength(ws.rQslice(3 + paletteSize,
+ 3 + paletteSize + 3));
+ }
+ FBU.bytes += clength[0] + clength[1];
+ if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
+
+ // Shift ctl, filter id, num colors, palette entries, and clength off
+ ws.rQshiftBytes(3);
+ var palette = ws.rQshiftBytes(paletteSize);
+ ws.rQshiftBytes(clength[0]);
+
+ if (raw) {
+ data = ws.rQshiftBytes(clength[1]);
+ } else {
+ data = decompress(ws.rQshiftBytes(clength[1]));
+ }
+
+ // Convert indexed (palette based) image data to RGB
+ // TODO: reduce number of calculations inside loop
+ var dest = [];
+ var x, y, b, w, w1, dp, sp;
+ if (numColors === 2) {
+ w = Math.floor((FBU.width + 7) / 8);
+ w1 = Math.floor(FBU.width / 8);
+ for (y = 0; y < FBU.height; y++) {
+ for (x = 0; x < w1; x++) {
+ for (b = 7; b >= 0; b--) {
+ dp = (y*FBU.width + x*8 + 7-b) * 3;
+ sp = (data[y*w + x] >> b & 1) * 3;
+ dest[dp ] = palette[sp ];
+ dest[dp+1] = palette[sp+1];
+ dest[dp+2] = palette[sp+2];
+ }
+ }
+ for (b = 7; b >= 8 - FBU.width % 8; b--) {
+ dp = (y*FBU.width + x*8 + 7-b) * 3;
+ sp = (data[y*w + x] >> b & 1) * 3;
+ dest[dp ] = palette[sp ];
+ dest[dp+1] = palette[sp+1];
+ dest[dp+2] = palette[sp+2];
+ }
+ }
+ } else {
+ for (y = 0; y < FBU.height; y++) {
+ for (x = 0; x < FBU.width; x++) {
+ dp = (y*FBU.width + x) * 3;
+ sp = data[y*FBU.width + x] * 3;
+ dest[dp ] = palette[sp ];
+ dest[dp+1] = palette[sp+1];
+ dest[dp+2] = palette[sp+2];
+ }
+ }
+ }
+
+ FBU.imgQ.push({
+ 'type': 'rgb',
+ 'img': {'complete': true, 'data': dest},
+ 'x': FBU.x,
+ 'y': FBU.y,
+ 'width': FBU.width,
+ 'height': FBU.height});
+ return true;
+ }
+
+ var handleCopy = function() {
+ var raw = false;
+ var uncompressedSize = FBU.width * FBU.height * fb_depth;
+ if (uncompressedSize < 12) {
+ raw = true;
+ clength = [0, uncompressedSize];
+ } else {
+ clength = getTightCLength(ws.rQslice(1, 4));
+ }
+ FBU.bytes = 1 + clength[0] + clength[1];
+ if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
+
+ // Shift ctl, clength off
+ ws.rQshiftBytes(1 + clength[0]);
+
+ if (raw) {
+ data = ws.rQshiftBytes(clength[1]);
+ } else {
+ data = decompress(ws.rQshiftBytes(clength[1]));
+ }
+
+ FBU.imgQ.push({
+ 'type': 'rgb',
+ 'img': {'complete': true, 'data': data},
+ 'x': FBU.x,
+ 'y': FBU.y,
+ 'width': FBU.width,
+ 'height': FBU.height});
+ return true;
+ }
+
+ ctl = ws.rQpeek8();
+
+ // Keep tight reset bits
+ resetStreams = ctl & 0xF;
+
+ // Figure out filter
+ ctl = ctl >> 4;
+ streamId = ctl & 0x3;
+
+ if (ctl === 0x08) cmode = "fill";
+ else if (ctl === 0x09) cmode = "jpeg";
+ else if (ctl === 0x0A) cmode = "png";
+ else if (ctl & 0x04) cmode = "filter";
+ else if (ctl < 0x04) cmode = "copy";
+ else throw("Illegal tight compression received, ctl: " + ctl);
+
+ if (isTightPNG && (cmode === "filter" || cmode === "copy")) {
+ throw("filter/copy received in tightPNG mode");
+ }
+
+ switch (cmode) {
+ // fill uses fb_depth because TPIXELs drop the padding byte
+ case "fill": FBU.bytes += fb_depth; break; // TPIXEL
+ case "jpeg": FBU.bytes += 3; break; // max clength
+ case "png": FBU.bytes += 3; break; // max clength
+ case "filter": FBU.bytes += 2; break; // filter id + num colors if palette
+ case "copy": break;
+ }
+
+ if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
+
+ //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
+ //Util.Debug(" cmode: " + cmode);
+
+ // Determine FBU.bytes
+ switch (cmode) {
+ case "fill":
+ ws.rQshift8(); // shift off ctl
+ color = ws.rQshiftBytes(fb_depth);
+ FBU.imgQ.push({
+ 'type': 'fill',
+ 'img': {'complete': true},
+ 'x': FBU.x,
+ 'y': FBU.y,
+ 'width': FBU.width,
+ 'height': FBU.height,
+ 'color': [color[2], color[1], color[0]] });
+ break;
+ case "png":
+ case "jpeg":
+ clength = getTightCLength(ws.rQslice(1, 4));
+ FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
+ if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
+
+ // We have everything, render it
+ //Util.Debug(" jpeg, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " +
+ // clength[0] + ", clength[1]: " + clength[1]);
+ ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
+ img = new Image();
+ //img.onload = scan_tight_imgQ;
+ FBU.imgQ.push({
+ 'type': 'img',
+ 'img': img,
+ 'x': FBU.x,
+ 'y': FBU.y});
+ img.src = "data:image/" + cmode +
+ extract_data_uri(ws.rQshiftBytes(clength[1]));
+ img = null;
+ break;
+ case "filter":
+ filterId = rQ[rQi + 1];
+ if (filterId === 1) {
+ if (!handlePalette()) { return false; }
+ } else {
+ // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter
+ // Filter 2, Gradient is valid but not used if jpeg is enabled
+ throw("Unsupported tight subencoding received, filter: " + filterId);
+ }
+ break;
+ case "copy":
+ if (!handleCopy()) { return false; }
+ break;
+ }
+
+ FBU.bytes = 0;
+ FBU.rects -= 1;
+ //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
+ //Util.Debug("<< display_tight_png");
+ return true;
+}
+
+extract_data_uri = function(arr) {
+ //var i, stra = [];
+ //for (i=0; i< arr.length; i += 1) {
+ // stra.push(String.fromCharCode(arr[i]));
+ //}
+ //return "," + escape(stra.join(''));
+ return ";base64," + Base64.encode(arr);
+};
+
+scan_tight_imgQ = function() {
+ var data, imgQ, ctx;
+ ctx = display.get_context();
+ if (rfb_state === 'normal') {
+ imgQ = FBU.imgQ;
+ while ((imgQ.length > 0) && (imgQ[0].img.complete)) {
+ data = imgQ.shift();
+ if (data.type === 'fill') {
+ display.fillRect(data.x, data.y, data.width, data.height, data.color);
+ } else if (data.type === 'rgb') {
+ display.blitRgbImage(data.x, data.y, data.width, data.height, data.img.data, 0);
+ } else {
+ ctx.drawImage(data.img, data.x, data.y);
+ }
+ }
+ setTimeout(scan_tight_imgQ, scan_imgQ_rate);
+ }
+};
+
+encHandlers.TIGHT = function () { return display_tight(false); };
+encHandlers.TIGHT_PNG = function () { return display_tight(true); };
+
+encHandlers.last_rect = function last_rect() {
+ Util.Debug(">> set_desktopsize");
+ FBU.rects = 0;
+ Util.Debug("<< set_desktopsize");
+ return true;
+};
+
+encHandlers.DesktopSize = function set_desktopsize() {
+ Util.Debug(">> set_desktopsize");
+ fb_width = FBU.width;
+ fb_height = FBU.height;
+ display.resize(fb_width, fb_height);
+ timing.fbu_rt_start = (new Date()).getTime();
+ // Send a new non-incremental request
+ ws.send(fbUpdateRequests());
+
+ FBU.bytes = 0;
+ FBU.rects -= 1;
+
+ Util.Debug("<< set_desktopsize");
+ return true;
+};
+
+encHandlers.Cursor = function set_cursor() {
+ var x, y, w, h, pixelslength, masklength;
+ //Util.Debug(">> set_cursor");
+ x = FBU.x; // hotspot-x
+ y = FBU.y; // hotspot-y
+ w = FBU.width;
+ h = FBU.height;
+
+ pixelslength = w * h * fb_Bpp;
+ masklength = Math.floor((w + 7) / 8) * h;
+
+ FBU.bytes = pixelslength + masklength;
+ if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; }
+
+ //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
+
+ display.changeCursor(ws.rQshiftBytes(pixelslength),
+ ws.rQshiftBytes(masklength),
+ x, y, w, h);
+
+ FBU.bytes = 0;
+ FBU.rects -= 1;
+
+ //Util.Debug("<< set_cursor");
+ return true;
+};
+
+encHandlers.JPEG_quality_lo = function set_jpeg_quality() {
+ Util.Error("Server sent jpeg_quality pseudo-encoding");
+};
+
+encHandlers.compress_lo = function set_compress_level() {
+ Util.Error("Server sent compress level pseudo-encoding");
+};
+
+/*
+ * Client message routines
+ */
+
+pixelFormat = function() {
+ //Util.Debug(">> pixelFormat");
+ var arr;
+ arr = [0]; // msg-type
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+
+ arr.push8(fb_Bpp * 8); // bits-per-pixel
+ arr.push8(fb_depth * 8); // depth
+ arr.push8(0); // little-endian
+ arr.push8(conf.true_color ? 1 : 0); // true-color
+
+ arr.push16(255); // red-max
+ arr.push16(255); // green-max
+ arr.push16(255); // blue-max
+ arr.push8(16); // red-shift
+ arr.push8(8); // green-shift
+ arr.push8(0); // blue-shift
+
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+ //Util.Debug("<< pixelFormat");
+ return arr;
+};
+
+clientEncodings = function() {
+ //Util.Debug(">> clientEncodings");
+ var arr, i, encList = [];
+
+ for (i=0; i<encodings.length; i += 1) {
+ if ((encodings[i][0] === "Cursor") &&
+ (! conf.local_cursor)) {
+ Util.Debug("Skipping Cursor pseudo-encoding");
+ } else {
+ //Util.Debug("Adding encoding: " + encodings[i][0]);
+ encList.push(encodings[i][1]);
+ }
+ }
+
+ arr = [2]; // msg-type
+ arr.push8(0); // padding
+
+ arr.push16(encList.length); // encoding count
+ for (i=0; i < encList.length; i += 1) {
+ arr.push32(encList[i]);
+ }
+ //Util.Debug("<< clientEncodings: " + arr);
+ return arr;
+};
+
+fbUpdateRequest = function(incremental, x, y, xw, yw) {
+ //Util.Debug(">> fbUpdateRequest");
+ if (typeof(x) === "undefined") { x = 0; }
+ if (typeof(y) === "undefined") { y = 0; }
+ if (typeof(xw) === "undefined") { xw = fb_width; }
+ if (typeof(yw) === "undefined") { yw = fb_height; }
+ var arr;
+ arr = [3]; // msg-type
+ arr.push8(incremental);
+ arr.push16(x);
+ arr.push16(y);
+ arr.push16(xw);
+ arr.push16(yw);
+ //Util.Debug("<< fbUpdateRequest");
+ return arr;
+};
+
+// Based on clean/dirty areas, generate requests to send
+fbUpdateRequests = function() {
+ var cleanDirty = display.getCleanDirtyReset(),
+ arr = [], i, cb, db;
+
+ cb = cleanDirty.cleanBox;
+ if (cb.w > 0 && cb.h > 0) {
+ // Request incremental for clean box
+ arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h));
+ }
+ for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
+ db = cleanDirty.dirtyBoxes[i];
+ // Force all (non-incremental for dirty box
+ arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h));
+ }
+ return arr;
+};
+
+
+
+keyEvent = function(keysym, down) {
+ //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
+ var arr;
+ arr = [4]; // msg-type
+ arr.push8(down);
+ arr.push16(0);
+ arr.push32(keysym);
+ //Util.Debug("<< keyEvent");
+ return arr;
+};
+
+pointerEvent = function(x, y) {
+ //Util.Debug(">> pointerEvent, x,y: " + x + "," + y +
+ // " , mask: " + mouse_buttonMask);
+ var arr;
+ arr = [5]; // msg-type
+ arr.push8(mouse_buttonMask);
+ arr.push16(x);
+ arr.push16(y);
+ //Util.Debug("<< pointerEvent");
+ return arr;
+};
+
+clientCutText = function(text) {
+ //Util.Debug(">> clientCutText");
+ var arr, i, n;
+ arr = [6]; // msg-type
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+ arr.push8(0); // padding
+ arr.push32(text.length);
+ n = text.length;
+ for (i=0; i < n; i+=1) {
+ arr.push(text.charCodeAt(i));
+ }
+ //Util.Debug("<< clientCutText:" + arr);
+ return arr;
+};
+
+
+
+//
+// Public API interface functions
+//
+
+that.connect = function(host, port, password, path) {
+ //Util.Debug(">> connect");
+
+ rfb_host = host;
+ rfb_port = port;
+ rfb_password = (password !== undefined) ? password : "";
+ rfb_path = (path !== undefined) ? path : "";
+
+ if ((!rfb_host) || (!rfb_port)) {
+ return fail("Must set host and port");
+ }
+
+ updateState('connect');
+ //Util.Debug("<< connect");
+
+};
+
+that.disconnect = function() {
+ //Util.Debug(">> disconnect");
+ updateState('disconnect', 'Disconnecting');
+ //Util.Debug("<< disconnect");
+};
+
+that.sendPassword = function(passwd) {
+ rfb_password = passwd;
+ rfb_state = "Authentication";
+ setTimeout(init_msg, 1);
+};
+
+that.sendString = function(str) {
+ if (rfb_state !== "normal" || conf.view_only) { return false; }
+ Util.Info("Sending: "+str);
+ var arr = [];
+ for(var i=0;i<str.length;i++)
+ {
+ var char = str.substring(i,i+1);
+ var code = char.charCodeAt(0);
+ if (shifted[char]) arr = arr.concat(keyEvent(0xFFE1, 1)); // Shift
+ arr = arr.concat(keyEvent(code, 1)); // (key)
+ arr = arr.concat(keyEvent(code, 0)); // (key)
+ if (shifted[char]) arr = arr.concat(keyEvent(0xFFE1, 0)); // Shift
+ }
+ arr = arr.concat(fbUpdateRequests());
+ ws.send(arr);
+};
+
+that.sendCtrlAltDel = function() {
+ if (rfb_state !== "normal" || conf.view_only) { return false; }
+ Util.Info("Sending Ctrl-Alt-Del");
+ var arr = [];
+ arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
+ arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt
+ arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete
+ arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
+ arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
+ arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
+ arr = arr.concat(fbUpdateRequests());
+ ws.send(arr);
+};
+
+// Send a key press. If 'down' is not specified then send a down key
+// followed by an up key.
+that.sendKey = function(code, down) {
+ if (rfb_state !== "normal" || conf.view_only) { return false; }
+ var arr = [];
+ if (typeof down !== 'undefined') {
+ Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
+ arr = arr.concat(keyEvent(code, down ? 1 : 0));
+ } else {
+ Util.Info("Sending key code (down + up): " + code);
+ arr = arr.concat(keyEvent(code, 1));
+ arr = arr.concat(keyEvent(code, 0));
+ }
+ arr = arr.concat(fbUpdateRequests());
+ ws.send(arr);
+};
+
+that.clipboardPasteFrom = function(text) {
+ if (rfb_state !== "normal") { return; }
+ //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
+ ws.send(clientCutText(text));
+ //Util.Debug("<< clipboardPasteFrom");
+};
+
+// Override internal functions for testing
+that.testMode = function(override_send) {
+ test_mode = true;
+ that.recv_message = ws.testMode(override_send);
+
+ checkEvents = function () { /* Stub Out */ };
+ that.connect = function(host, port, password) {
+ rfb_host = host;
+ rfb_port = port;
+ rfb_password = password;
+ updateState('ProtocolVersion', "Starting VNC handshake");
+ };
+};
+
+
+return constructor(); // Return the public API interface
+
+} // End of RFB()
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a0571d1e/patches/systemvm/debian/config/root/lswcp/include/ui.js
----------------------------------------------------------------------
diff --git a/patches/systemvm/debian/config/root/lswcp/include/ui.js b/patches/systemvm/debian/config/root/lswcp/include/ui.js
new file mode 100644
index 0000000..b5d7ce3
--- /dev/null
+++ b/patches/systemvm/debian/config/root/lswcp/include/ui.js
@@ -0,0 +1,653 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2011 Joel Martin
+ * Licensed under LGPL-3 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+"use strict";
+/*jslint white: false, browser: true */
+/*global window, $D, Util, WebUtil, RFB, Display */
+
+var UI = {
+
+rfb_state : 'loaded',
+settingsOpen : false,
+connSettingsOpen : false,
+clipboardOpen: false,
+keyboardVisible: false,
+
+// Render default UI and initialize settings menu
+load: function() {
+ var html = '', i, sheet, sheets, llevels;
+
+ // Stylesheet selection dropdown
+ sheet = WebUtil.selectStylesheet();
+ sheets = WebUtil.getStylesheets();
+ for (i = 0; i < sheets.length; i += 1) {
+ UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
+ }
+
+ // Logging selection dropdown
+ llevels = ['error', 'warn', 'info', 'debug'];
+ for (i = 0; i < llevels.length; i += 1) {
+ UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
+ }
+
+ // Settings with immediate effects
+ UI.initSetting('logging', 'warn');
+ WebUtil.init_logging(UI.getSetting('logging'));
+
+ UI.initSetting('stylesheet', 'default');
+ WebUtil.selectStylesheet(null);
+ // call twice to get around webkit bug
+ WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
+
+ /* Populate the controls if defaults are provided in the URL */
+ UI.initSetting('host', '');
+ UI.initSetting('port', '');
+ UI.initSetting('password', '');
+ UI.initSetting('encrypt', (window.location.protocol === "https:"));
+ UI.initSetting('true_color', true);
+ UI.initSetting('cursor', false);
+ UI.initSetting('shared', true);
+ UI.initSetting('view_only', false);
+ UI.initSetting('connectTimeout', 2);
+ UI.initSetting('path', 'websockify');
+
+ UI.rfb = RFB({'target': $D('noVNC_canvas'),
+ 'onUpdateState': UI.updateState,
+ 'onClipboard': UI.clipReceive});
+ UI.updateVisualState();
+
+ // Unfocus clipboard when over the VNC area
+ //$D('VNC_screen').onmousemove = function () {
+ // var keyboard = UI.rfb.get_keyboard();
+ // if ((! keyboard) || (! keyboard.get_focused())) {
+ // $D('VNC_clipboard_text').blur();
+ // }
+ // };
+
+ // Show mouse selector buttons on touch screen devices
+ if ('ontouchstart' in document.documentElement) {
+ // Show mobile buttons
+ $D('noVNC_mobile_buttons').style.display = "inline";
+ UI.setMouseButton();
+ // Remove the address bar
+ setTimeout(function() { window.scrollTo(0, 1); }, 100);
+ UI.forceSetting('clip', true);
+ $D('noVNC_clip').disabled = true;
+ } else {
+ UI.initSetting('clip', false);
+ }
+
+ //iOS Safari does not support CSS position:fixed.
+ //This detects iOS devices and enables javascript workaround.
+ if ((navigator.userAgent.match(/iPhone/i)) ||
+ (navigator.userAgent.match(/iPod/i)) ||
+ (navigator.userAgent.match(/iPad/i))) {
+ //UI.setOnscroll();
+ //UI.setResize();
+ }
+
+ $D('noVNC_host').focus();
+
+ UI.setViewClip();
+ Util.addEvent(window, 'resize', UI.setViewClip);
+
+ Util.addEvent(window, 'beforeunload', function () {
+ if (UI.rfb_state === 'normal') {
+ return "You are currently connected.";
+ }
+ } );
+
+ // Show description by default when hosted at for kanaka.github.com
+ if (location.host === "kanaka.github.com") {
+ // Open the description dialog
+ $D('noVNC_description').style.display = "block";
+ } else {
+ // Open the connect panel on first load
+ UI.toggleConnectPanel();
+ }
+},
+
+// Read form control compatible setting from cookie
+getSetting: function(name) {
+ var val, ctrl = $D('noVNC_' + name);
+ val = WebUtil.readCookie(name);
+ if (ctrl.type === 'checkbox') {
+ if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
+ val = false;
+ } else {
+ val = true;
+ }
+ }
+ return val;
+},
+
+// Update cookie and form control setting. If value is not set, then
+// updates from control to current cookie setting.
+updateSetting: function(name, value) {
+
+ var i, ctrl = $D('noVNC_' + name);
+ // Save the cookie for this session
+ if (typeof value !== 'undefined') {
+ WebUtil.createCookie(name, value);
+ }
+
+ // Update the settings control
+ value = UI.getSetting(name);
+
+ if (ctrl.type === 'checkbox') {
+ ctrl.checked = value;
+
+ } else if (typeof ctrl.options !== 'undefined') {
+ for (i = 0; i < ctrl.options.length; i += 1) {
+ if (ctrl.options[i].value === value) {
+ ctrl.selectedIndex = i;
+ break;
+ }
+ }
+ } else {
+ /*Weird IE9 error leads to 'null' appearring
+ in textboxes instead of ''.*/
+ if (value === null) {
+ value = "";
+ }
+ ctrl.value = value;
+ }
+},
+
+// Save control setting to cookie
+saveSetting: function(name) {
+ var val, ctrl = $D('noVNC_' + name);
+ if (ctrl.type === 'checkbox') {
+ val = ctrl.checked;
+ } else if (typeof ctrl.options !== 'undefined') {
+ val = ctrl.options[ctrl.selectedIndex].value;
+ } else {
+ val = ctrl.value;
+ }
+ WebUtil.createCookie(name, val);
+ //Util.Debug("Setting saved '" + name + "=" + val + "'");
+ return val;
+},
+
+// Initial page load read/initialization of settings
+initSetting: function(name, defVal) {
+ var val;
+
+ // Check Query string followed by cookie
+ val = WebUtil.getQueryVar(name);
+ if (val === null) {
+ val = WebUtil.readCookie(name, defVal);
+ }
+ UI.updateSetting(name, val);
+ //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
+ return val;
+},
+
+// Force a setting to be a certain value
+forceSetting: function(name, val) {
+ UI.updateSetting(name, val);
+ return val;
+},
+
+
+// Show the clipboard panel
+toggleClipboardPanel: function() {
+ // Close the description panel
+ $D('noVNC_description').style.display = "none";
+ //Close settings if open
+ if (UI.settingsOpen === true) {
+ UI.settingsApply();
+ UI.closeSettingsMenu();
+ }
+ //Close connection settings if open
+ if (UI.connSettingsOpen === true) {
+ UI.toggleConnectPanel();
+ }
+ //Toggle Clipboard Panel
+ if (UI.clipboardOpen === true) {
+ $D('noVNC_clipboard').style.display = "none";
+ $D('clipboardButton').className = "noVNC_status_button";
+ UI.clipboardOpen = false;
+ } else {
+ $D('noVNC_clipboard').style.display = "block";
+ $D('clipboardButton').className = "noVNC_status_button_selected";
+ UI.clipboardOpen = true;
+ }
+},
+
+// Show the connection settings panel/menu
+toggleConnectPanel: function() {
+ // Close the description panel
+ $D('noVNC_description').style.display = "none";
+ //Close connection settings if open
+ if (UI.settingsOpen === true) {
+ UI.settingsApply();
+ UI.closeSettingsMenu();
+ $D('connectButton').className = "noVNC_status_button";
+ }
+ if (UI.clipboardOpen === true) {
+ UI.toggleClipboardPanel();
+ }
+
+ //Toggle Connection Panel
+ if (UI.connSettingsOpen === true) {
+ $D('noVNC_controls').style.display = "none";
+ $D('connectButton').className = "noVNC_status_button";
+ UI.connSettingsOpen = false;
+ } else {
+ $D('noVNC_controls').style.display = "block";
+ $D('connectButton').className = "noVNC_status_button_selected";
+ UI.connSettingsOpen = true;
+ $D('noVNC_host').focus();
+ }
+},
+
+// Toggle the settings menu:
+// On open, settings are refreshed from saved cookies.
+// On close, settings are applied
+toggleSettingsPanel: function() {
+ // Close the description panel
+ $D('noVNC_description').style.display = "none";
+ if (UI.settingsOpen) {
+ UI.settingsApply();
+ UI.closeSettingsMenu();
+ } else {
+ UI.updateSetting('encrypt');
+ UI.updateSetting('true_color');
+ if (UI.rfb.get_display().get_cursor_uri()) {
+ UI.updateSetting('cursor');
+ } else {
+ UI.updateSetting('cursor', false);
+ $D('noVNC_cursor').disabled = true;
+ }
+ UI.updateSetting('clip');
+ UI.updateSetting('shared');
+ UI.updateSetting('view_only');
+ UI.updateSetting('connectTimeout');
+ UI.updateSetting('path');
+ UI.updateSetting('stylesheet');
+ UI.updateSetting('logging');
+
+ UI.openSettingsMenu();
+ }
+},
+
+// Open menu
+openSettingsMenu: function() {
+ // Close the description panel
+ $D('noVNC_description').style.display = "none";
+ if (UI.clipboardOpen === true) {
+ UI.toggleClipboardPanel();
+ }
+ //Close connection settings if open
+ if (UI.connSettingsOpen === true) {
+ UI.toggleConnectPanel();
+ }
+ $D('noVNC_settings').style.display = "block";
+ $D('settingsButton').className = "noVNC_status_button_selected";
+ UI.settingsOpen = true;
+},
+
+// Close menu (without applying settings)
+closeSettingsMenu: function() {
+ $D('noVNC_settings').style.display = "none";
+ $D('settingsButton').className = "noVNC_status_button";
+ UI.settingsOpen = false;
+},
+
+// Save/apply settings when 'Apply' button is pressed
+settingsApply: function() {
+ //Util.Debug(">> settingsApply");
+ UI.saveSetting('encrypt');
+ UI.saveSetting('true_color');
+ if (UI.rfb.get_display().get_cursor_uri()) {
+ UI.saveSetting('cursor');
+ }
+ UI.saveSetting('clip');
+ UI.saveSetting('shared');
+ UI.saveSetting('view_only');
+ UI.saveSetting('connectTimeout');
+ UI.saveSetting('path');
+ UI.saveSetting('stylesheet');
+ UI.saveSetting('logging');
+
+ // Settings with immediate (non-connected related) effect
+ WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
+ WebUtil.init_logging(UI.getSetting('logging'));
+ UI.setViewClip();
+ UI.setViewDrag(UI.rfb.get_viewportDrag());
+ //Util.Debug("<< settingsApply");
+},
+
+
+
+setPassword: function() {
+ UI.rfb.sendPassword($D('noVNC_password').value);
+ //Reset connect button.
+ $D('noVNC_connect_button').value = "Connect";
+ $D('noVNC_connect_button').onclick = UI.Connect;
+ //Hide connection panel.
+ UI.toggleConnectPanel();
+ return false;
+},
+
+sendCtrlAltDel: function() {
+ UI.rfb.sendCtrlAltDel();
+},
+
+setMouseButton: function(num) {
+ var b, blist = [0, 1,2,4], button;
+
+ if (typeof num === 'undefined') {
+ // Disable mouse buttons
+ num = -1;
+ }
+ if (UI.rfb) {
+ UI.rfb.get_mouse().set_touchButton(num);
+ }
+
+ for (b = 0; b < blist.length; b++) {
+ button = $D('noVNC_mouse_button' + blist[b]);
+ if (blist[b] === num) {
+ button.style.display = "";
+ } else {
+ button.style.display = "none";
+ /*
+ button.style.backgroundColor = "black";
+ button.style.color = "lightgray";
+ button.style.backgroundColor = "";
+ button.style.color = "";
+ */
+ }
+ }
+},
+
+updateState: function(rfb, state, oldstate, msg) {
+ var s, sb, c, d, cad, vd, klass;
+ UI.rfb_state = state;
+ s = $D('noVNC_status');
+ sb = $D('noVNC_status_bar');
+ switch (state) {
+ case 'failed':
+ case 'fatal':
+ klass = "noVNC_status_error";
+ break;
+ case 'normal':
+ klass = "noVNC_status_normal";
+ break;
+ case 'disconnected':
+ $D('noVNC_logo').style.display = "block";
+ // Fall through
+ case 'loaded':
+ klass = "noVNC_status_normal";
+ break;
+ case 'password':
+ UI.toggleConnectPanel();
+
+ $D('noVNC_connect_button').value = "Send Password";
+ $D('noVNC_connect_button').onclick = UI.setPassword;
+ $D('noVNC_password').focus();
+
+ klass = "noVNC_status_warn";
+ break;
+ default:
+ klass = "noVNC_status_warn";
+ break;
+ }
+
+ if (typeof(msg) !== 'undefined') {
+ s.setAttribute("class", klass);
+ sb.setAttribute("class", klass);
+ s.innerHTML = msg;
+ }
+
+ UI.updateVisualState();
+},
+
+// Disable/enable controls depending on connection state
+updateVisualState: function() {
+ var connected = UI.rfb_state === 'normal' ? true : false;
+
+ //Util.Debug(">> updateVisualState");
+ $D('noVNC_encrypt').disabled = connected;
+ $D('noVNC_true_color').disabled = connected;
+ if (UI.rfb && UI.rfb.get_display() &&
+ UI.rfb.get_display().get_cursor_uri()) {
+ $D('noVNC_cursor').disabled = connected;
+ } else {
+ UI.updateSetting('cursor', false);
+ $D('noVNC_cursor').disabled = true;
+ }
+ $D('noVNC_shared').disabled = connected;
+ $D('noVNC_view_only').disabled = connected;
+ $D('noVNC_connectTimeout').disabled = connected;
+ $D('noVNC_path').disabled = connected;
+
+ if (connected) {
+ UI.setViewClip();
+ UI.setMouseButton(1);
+ $D('clipboardButton').style.display = "inline";
+ $D('showKeyboard').style.display = "inline";
+ $D('sendCtrlAltDelButton').style.display = "inline";
+ } else {
+ UI.setMouseButton();
+ $D('clipboardButton').style.display = "none";
+ $D('showKeyboard').style.display = "none";
+ $D('sendCtrlAltDelButton').style.display = "none";
+ }
+ // State change disables viewport dragging.
+ // It is enabled (toggled) by direct click on the button
+ UI.setViewDrag(false);
+
+ switch (UI.rfb_state) {
+ case 'fatal':
+ case 'failed':
+ case 'loaded':
+ case 'disconnected':
+ $D('connectButton').style.display = "";
+ $D('disconnectButton').style.display = "none";
+ break;
+ default:
+ $D('connectButton').style.display = "none";
+ $D('disconnectButton').style.display = "";
+ break;
+ }
+
+ //Util.Debug("<< updateVisualState");
+},
+
+
+clipReceive: function(rfb, text) {
+ Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
+ $D('noVNC_clipboard_text').value = text;
+ Util.Debug("<< UI.clipReceive");
+},
+
+
+connect: function() {
+ var host, port, password, path;
+
+ UI.closeSettingsMenu();
+ UI.toggleConnectPanel();
+
+ host = $D('noVNC_host').value;
+ port = $D('noVNC_port').value;
+ password = $D('noVNC_password').value;
+ path = $D('noVNC_path').value;
+ if ((!host) || (!port)) {
+ throw("Must set host and port");
+ }
+
+ UI.rfb.set_encrypt(UI.getSetting('encrypt'));
+ UI.rfb.set_true_color(UI.getSetting('true_color'));
+ UI.rfb.set_local_cursor(UI.getSetting('cursor'));
+ UI.rfb.set_shared(UI.getSetting('shared'));
+ UI.rfb.set_view_only(UI.getSetting('view_only'));
+ UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
+
+ UI.rfb.connect(host, port, password, path);
+ //Close dialog.
+ setTimeout(UI.setBarPosition, 100);
+ $D('noVNC_logo').style.display = "none";
+},
+
+disconnect: function() {
+ UI.closeSettingsMenu();
+ UI.rfb.disconnect();
+
+ $D('noVNC_logo').style.display = "block";
+ UI.connSettingsOpen = false;
+ UI.toggleConnectPanel();
+},
+
+displayBlur: function() {
+ UI.rfb.get_keyboard().set_focused(false);
+ UI.rfb.get_mouse().set_focused(false);
+},
+
+displayFocus: function() {
+ UI.rfb.get_keyboard().set_focused(true);
+ UI.rfb.get_mouse().set_focused(true);
+},
+
+clipClear: function() {
+ $D('noVNC_clipboard_text').value = "";
+ UI.rfb.clipboardPasteFrom("");
+},
+
+clipSend: function() {
+ var text = $D('noVNC_clipboard_text').value;
+ Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
+ UI.rfb.clipboardPasteFrom(text);
+ Util.Debug("<< UI.clipSend");
+},
+
+
+// Enable/disable and configure viewport clipping
+setViewClip: function(clip) {
+ var display, cur_clip, pos, new_w, new_h;
+
+ if (UI.rfb) {
+ display = UI.rfb.get_display();
+ } else {
+ return;
+ }
+
+ cur_clip = display.get_viewport();
+
+ if (typeof(clip) !== 'boolean') {
+ // Use current setting
+ clip = UI.getSetting('clip');
+ }
+
+ if (clip && !cur_clip) {
+ // Turn clipping on
+ UI.updateSetting('clip', true);
+ } else if (!clip && cur_clip) {
+ // Turn clipping off
+ UI.updateSetting('clip', false);
+ display.set_viewport(false);
+ $D('noVNC_canvas').style.position = 'static';
+ display.viewportChange();
+ }
+ if (UI.getSetting('clip')) {
+ // If clipping, update clipping settings
+ $D('noVNC_canvas').style.position = 'absolute';
+ pos = Util.getPosition($D('noVNC_canvas'));
+ new_w = window.innerWidth - pos.x;
+ new_h = window.innerHeight - pos.y;
+ display.set_viewport(true);
+ display.viewportChange(0, 0, new_w, new_h);
+ }
+},
+
+// Toggle/set/unset the viewport drag/move button
+setViewDrag: function(drag) {
+ var vmb = $D('noVNC_view_drag_button');
+ if (!UI.rfb) { return; }
+
+ if (UI.rfb_state === 'normal' &&
+ UI.rfb.get_display().get_viewport()) {
+ vmb.style.display = "inline";
+ } else {
+ vmb.style.display = "none";
+ }
+
+ if (typeof(drag) === "undefined") {
+ // If not specified, then toggle
+ drag = !UI.rfb.get_viewportDrag();
+ }
+ if (drag) {
+ vmb.className = "noVNC_status_button_selected";
+ UI.rfb.set_viewportDrag(true);
+ } else {
+ vmb.className = "noVNC_status_button";
+ UI.rfb.set_viewportDrag(false);
+ }
+},
+
+// On touch devices, show the OS keyboard
+showKeyboard: function() {
+ if(UI.keyboardVisible === false) {
+ $D('keyboardinput').focus();
+ UI.keyboardVisible = true;
+ $D('showKeyboard').className = "noVNC_status_button_selected";
+ } else if(UI.keyboardVisible === true) {
+ $D('keyboardinput').blur();
+ $D('showKeyboard').className = "noVNC_status_button";
+ UI.keyboardVisible = false;
+ }
+},
+
+keyInputBlur: function() {
+ $D('showKeyboard').className = "noVNC_status_button";
+ //Weird bug in iOS if you change keyboardVisible
+ //here it does not actually occur so next time
+ //you click keyboard icon it doesnt work.
+ setTimeout(function() { UI.setKeyboard(); },100);
+},
+
+setKeyboard: function() {
+ UI.keyboardVisible = false;
+},
+
+// iOS < Version 5 does not support position fixed. Javascript workaround:
+setOnscroll: function() {
+ window.onscroll = function() {
+ UI.setBarPosition();
+ };
+},
+
+setResize: function () {
+ window.onResize = function() {
+ UI.setBarPosition();
+ };
+},
+
+//Helper to add options to dropdown.
+addOption: function(selectbox,text,value )
+{
+ var optn = document.createElement("OPTION");
+ optn.text = text;
+ optn.value = value;
+ selectbox.options.add(optn);
+},
+
+setBarPosition: function() {
+ $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
+ $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
+
+ var vncwidth = $D('noVNC_screen').style.offsetWidth;
+ $D('noVNC-control-bar').style.width = vncwidth + 'px';
+}
+
+};
+
+
+
+