diff options
Diffstat (limited to 'webclients/novnc/include')
-rw-r--r-- | webclients/novnc/include/base.css | 7 | ||||
-rw-r--r-- | webclients/novnc/include/black.css | 7 | ||||
-rw-r--r-- | webclients/novnc/include/blue.css | 6 | ||||
-rw-r--r-- | webclients/novnc/include/display.js | 61 | ||||
-rw-r--r-- | webclients/novnc/include/input.js | 8 | ||||
-rw-r--r-- | webclients/novnc/include/playback.js | 2 | ||||
-rw-r--r-- | webclients/novnc/include/rfb.js | 120 | ||||
-rw-r--r-- | webclients/novnc/include/ui.js | 8 | ||||
-rw-r--r-- | webclients/novnc/include/util.js | 19 | ||||
-rw-r--r-- | webclients/novnc/include/vnc.js | 2 | ||||
-rw-r--r-- | webclients/novnc/include/web-socket-js/web_socket.js | 2 | ||||
-rw-r--r-- | webclients/novnc/include/websock.js | 12 | ||||
-rw-r--r-- | webclients/novnc/include/webutil.js | 2 |
13 files changed, 181 insertions, 75 deletions
diff --git a/webclients/novnc/include/base.css b/webclients/novnc/include/base.css index 105984d..3a2feb3 100644 --- a/webclients/novnc/include/base.css +++ b/webclients/novnc/include/base.css @@ -1,3 +1,10 @@ +/* + * noVNC base CSS + * Copyright (C) 2012 Joel Martin + * noVNC is licensed under the LGPL-3 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + body { margin:0; padding:0; diff --git a/webclients/novnc/include/black.css b/webclients/novnc/include/black.css index 8f80f66..e958ee3 100644 --- a/webclients/novnc/include/black.css +++ b/webclients/novnc/include/black.css @@ -1,3 +1,10 @@ +/* + * noVNC base CSS + * Copyright (C) 2012 Joel Martin + * noVNC is licensed under the LGPL-3 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + #keyboardinput { background-color:#000; } diff --git a/webclients/novnc/include/blue.css b/webclients/novnc/include/blue.css index a8baf70..3dad0b4 100644 --- a/webclients/novnc/include/blue.css +++ b/webclients/novnc/include/blue.css @@ -1,3 +1,9 @@ +/* + * noVNC base CSS + * Copyright (C) 2012 Joel Martin + * noVNC is licensed under the LGPL-3 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ #noVNC-control-bar { background-color:#04073d; diff --git a/webclients/novnc/include/display.js b/webclients/novnc/include/display.js index f2ecdba..5ad99ba 100644 --- a/webclients/novnc/include/display.js +++ b/webclients/novnc/include/display.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -19,9 +19,12 @@ var that = {}, // Public API methods c_ctx = null, c_forceCanvas = false, + // Queued drawing actions for in-order rendering + renderQ = [], + // Predefine function variables (jslint) imageDataGet, rgbImageData, bgrxImageData, cmapImageData, - setFillColor, rescale, + setFillColor, rescale, scan_renderQ, // The full frame buffer (logical canvas) size fb_width = 0, @@ -412,6 +415,8 @@ that.clear = function() { c_ctx.clearRect(0, 0, viewport.w, viewport.h); } + renderQ = []; + // No benefit over default ("source-over") in Chrome and firefox //c_ctx.globalCompositeOperation = "copy"; }; @@ -577,6 +582,58 @@ that.blitStringImage = function(str, x, y) { img.src = str; }; +// Wrap ctx.drawImage but relative to viewport +that.drawImage = function(img, x, y) { + c_ctx.drawImage(img, x - viewport.x, y - viewport.y); +}; + +that.renderQ_push = function(action) { + renderQ.push(action); + if (renderQ.length === 1) { + // If this can be rendered immediately it will be, otherwise + // the scanner will start polling the queue (every + // requestAnimationFrame interval) + scan_renderQ(); + } +}; + +scan_renderQ = function() { + var a, ready = true; + while (ready && renderQ.length > 0) { + a = renderQ[0]; + switch (a.type) { + case 'copy': + that.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height); + break; + case 'fill': + that.fillRect(a.x, a.y, a.width, a.height, a.color); + break; + case 'blit': + that.blitImage(a.x, a.y, a.width, a.height, a.data, 0); + break; + case 'blitRgb': + that.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0); + break; + case 'img': + if (a.img.complete) { + that.drawImage(a.img, a.x, a.y); + } else { + // We need to wait for this image to 'load' + // to keep things in-order + ready = false; + } + break; + } + if (ready) { + a = renderQ.shift(); + } + } + if (renderQ.length > 0) { + requestAnimFrame(scan_renderQ); + } +}; + + that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { if (conf.cursor_uri === false) { Util.Warn("changeCursor called but no cursor data URI support"); diff --git a/webclients/novnc/include/input.js b/webclients/novnc/include/input.js index 1dfe719..9298dfe 100644 --- a/webclients/novnc/include/input.js +++ b/webclients/novnc/include/input.js @@ -1167,14 +1167,14 @@ unicodeTable = { 0x21D4 : 0x08cd, 0x21D2 : 0x08ce, 0x2261 : 0x08cf, - 0x221A : 0x08d6, + //0x221A : 0x08d6, 0x2282 : 0x08da, 0x2283 : 0x08db, 0x2229 : 0x08dc, 0x222A : 0x08dd, 0x2227 : 0x08de, 0x2228 : 0x08df, - 0x2202 : 0x08ef, + //0x2202 : 0x08ef, 0x0192 : 0x08f6, 0x2190 : 0x08fb, 0x2191 : 0x08fc, @@ -1509,7 +1509,7 @@ unicodeTable = { 0x012D : 0x100012d, 0x01B6 : 0x10001b6, 0x01E7 : 0x10001e7, - 0x01D2 : 0x10001d2, + //0x01D2 : 0x10001d2, 0x0275 : 0x1000275, 0x018F : 0x100018f, 0x0259 : 0x1000259, @@ -1908,4 +1908,4 @@ unicodeTable = { 0x28fd : 0x10028fd, 0x28fe : 0x10028fe, 0x28ff : 0x10028ff -};
\ No newline at end of file +}; diff --git a/webclients/novnc/include/playback.js b/webclients/novnc/include/playback.js index 22a00a3..a21c7b6 100644 --- a/webclients/novnc/include/playback.js +++ b/webclients/novnc/include/playback.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.LGPL-3) */ diff --git a/webclients/novnc/include/rfb.js b/webclients/novnc/include/rfb.js index 75b9797..00fb7d8 100644 --- a/webclients/novnc/include/rfb.js +++ b/webclients/novnc/include/rfb.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -26,7 +26,7 @@ var that = {}, // Public API methods pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests, keyEvent, pointerEvent, clientCutText, - getTightCLength, extract_data_uri, scan_tight_imgQ, + getTightCLength, extract_data_uri, keyPress, mouseButton, mouseMove, checkEvents, // Overridable for testing @@ -93,7 +93,6 @@ var that = {}, // Public API methods encoding : 0, subencoding : -1, background : null, - imgQ : [], // TIGHT_PNG image queue zlibs : [] // TIGHT zlib streams }, @@ -103,7 +102,6 @@ var that = {}, // Public API methods fb_height = 0, fb_name = "", - scan_imgQ_rate = 40, // 25 times per second or so last_req_time = 0, rre_chunk_sz = 100, @@ -144,6 +142,9 @@ Util.conf_defaults(conf, that, defaults, [ ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'], ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'], + // UltraVNC repeater ID to connect to + ['repeaterID', 'rw', 'str', '', 'RepeaterID to connect to'], + ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'], ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'], @@ -234,21 +235,28 @@ function constructor() { } }); ws.on('close', function(e) { + Util.Warn("WebSocket on-close event"); + var msg = ""; if (e.code) { - Util.Info("Close code: " + e.code + ", reason: " + e.reason + ", wasClean: " + e.wasClean); + msg = " (code: " + e.code; + if (e.reason) { + msg += ", reason: " + e.reason; + } + msg += ")"; } if (rfb_state === 'disconnect') { - updateState('disconnected', 'VNC disconnected'); + updateState('disconnected', 'VNC disconnected' + msg); } else if (rfb_state === 'ProtocolVersion') { - fail('Failed to connect to server'); + fail('Failed to connect to server' + msg); } else if (rfb_state in {'failed':1, 'disconnected':1}) { - Util.Error("Received onclose while disconnected"); + Util.Error("Received onclose while disconnected" + msg); } else { - fail('Server disconnected'); + fail('Server disconnected' + msg); } }); ws.on('error', function(e) { - fail("WebSock error: " + e); + Util.Warn("WebSocket on-error event"); + //fail("WebSock reported an error"); }); @@ -307,7 +315,6 @@ init_vars = function() { 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 = []; @@ -419,16 +426,16 @@ updateState = function(state, statusMsg) { func = Util.Warn; } + cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : ""; + func("New state '" + state + "', was '" + oldstate + "'." + cmsg); + if ((oldstate === 'failed') && (state === 'disconnected')) { - // Do disconnect action, but stay in failed state. + // 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); @@ -655,10 +662,10 @@ mouseMove = function(x, y) { init_msg = function() { //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']"); - var strlen, reason, length, sversion, cversion, + var strlen, reason, length, sversion, cversion, repeaterID, 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; + green_shift, blue_shift, true_color, name_length, is_repeater; //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0)); switch (rfb_state) { @@ -669,16 +676,27 @@ init_msg = function() { } sversion = ws.rQshiftStr(12).substr(4,7); Util.Info("Server ProtocolVersion: " + sversion); + is_repeater = 0; switch (sversion) { + case "000.000": is_repeater = 1; break; // UltraVNC repeater case "003.003": rfb_version = 3.3; break; case "003.006": rfb_version = 3.3; break; // UltraVNC case "003.889": rfb_version = 3.3; break; // Apple Remote Desktop 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 + case "004.001": rfb_version = 3.8; break; // RealVNC 4.6 default: return fail("Invalid server version " + sversion); } + if (is_repeater) { + repeaterID = conf.repeaterID; + while (repeaterID.length < 250) { + repeaterID += "\0"; + } + ws.send_string(repeaterID); + break; + } if (rfb_version > rfb_max_version) { rfb_version = rfb_max_version; } @@ -876,11 +894,11 @@ init_msg = function() { response = response.concat(clientEncodings()); response = response.concat(fbUpdateRequests()); timing.fbu_rt_start = (new Date()).getTime(); + timing.pixels = 0; 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); @@ -1027,7 +1045,7 @@ framebufferUpdate = function() { timing.pixels += FBU.width * FBU.height; } - if (FBU.rects === 0 || (timing.pixels >= (fb_width * fb_height))) { + if (timing.pixels >= (fb_width * fb_height)) { if (((FBU.width === fb_width) && (FBU.height === fb_height)) || (timing.fbu_rt_start > 0)) { @@ -1105,9 +1123,14 @@ encHandlers.COPYRECT = function 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); + display.renderQ_push({ + 'type': 'copy', + 'old_x': ws.rQshift16(), + 'old_y': ws.rQshift16(), + 'x': FBU.x, + 'y': FBU.y, + 'width': FBU.width, + 'height': FBU.height}); FBU.rects -= 1; FBU.bytes = 0; return true; @@ -1401,9 +1424,9 @@ function display_tight(isTightPNG) { } } - FBU.imgQ.push({ - 'type': 'rgb', - 'img': {'complete': true, 'data': dest}, + display.renderQ_push({ + 'type': 'blitRgb', + 'data': dest, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1432,9 +1455,9 @@ function display_tight(isTightPNG) { data = decompress(ws.rQshiftBytes(clength[1])); } - FBU.imgQ.push({ - 'type': 'rgb', - 'img': {'complete': true, 'data': data}, + display.renderQ_push({ + 'type': 'blitRgb', + 'data': data, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1456,10 +1479,10 @@ function display_tight(isTightPNG) { 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); + else return fail("Illegal tight compression received, ctl: " + ctl); if (isTightPNG && (cmode === "filter" || cmode === "copy")) { - throw("filter/copy received in tightPNG mode"); + return fail("filter/copy received in tightPNG mode"); } switch (cmode) { @@ -1481,9 +1504,8 @@ function display_tight(isTightPNG) { case "fill": ws.rQshift8(); // shift off ctl color = ws.rQshiftBytes(fb_depth); - FBU.imgQ.push({ + display.renderQ_push({ 'type': 'fill', - 'img': {'complete': true}, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1501,14 +1523,13 @@ function display_tight(isTightPNG) { // 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({ + img.src = "data:image/" + cmode + + extract_data_uri(ws.rQshiftBytes(clength[1])); + display.renderQ_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": @@ -1542,32 +1563,13 @@ extract_data_uri = function(arr) { 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"); + //Util.Debug(">> last_rect"); FBU.rects = 0; - Util.Debug("<< set_desktopsize"); + //Util.Debug("<< last_rect"); return true; }; @@ -1589,7 +1591,7 @@ encHandlers.DesktopSize = function set_desktopsize() { encHandlers.Cursor = function set_cursor() { var x, y, w, h, pixelslength, masklength; - //Util.Debug(">> set_cursor"); + Util.Debug(">> set_cursor"); x = FBU.x; // hotspot-x y = FBU.y; // hotspot-y w = FBU.width; @@ -1610,7 +1612,7 @@ encHandlers.Cursor = function set_cursor() { FBU.bytes = 0; FBU.rects -= 1; - //Util.Debug("<< set_cursor"); + Util.Debug("<< set_cursor"); return true; }; diff --git a/webclients/novnc/include/ui.js b/webclients/novnc/include/ui.js index eddfa6c..dc837e6 100644 --- a/webclients/novnc/include/ui.js +++ b/webclients/novnc/include/ui.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -55,6 +55,7 @@ load: function() { UI.initSetting('view_only', false); UI.initSetting('connectTimeout', 2); UI.initSetting('path', 'websockify'); + UI.initSetting('repeaterID', ''); UI.rfb = RFB({'target': $D('noVNC_canvas'), 'onUpdateState': UI.updateState, @@ -270,6 +271,7 @@ toggleSettingsPanel: function() { UI.updateSetting('view_only'); UI.updateSetting('connectTimeout'); UI.updateSetting('path'); + UI.updateSetting('repeaterID'); UI.updateSetting('stylesheet'); UI.updateSetting('logging'); @@ -313,6 +315,7 @@ settingsApply: function() { UI.saveSetting('view_only'); UI.saveSetting('connectTimeout'); UI.saveSetting('path'); + UI.saveSetting('repeaterID'); UI.saveSetting('stylesheet'); UI.saveSetting('logging'); @@ -427,6 +430,7 @@ updateVisualState: function() { $D('noVNC_view_only').disabled = connected; $D('noVNC_connectTimeout').disabled = connected; $D('noVNC_path').disabled = connected; + $D('noVNC_repeaterID').disabled = connected; if (connected) { UI.setViewClip(); @@ -489,8 +493,10 @@ connect: function() { 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.set_repeaterID(UI.getSetting('repeaterID')); UI.rfb.connect(host, port, password, path); + //Close dialog. setTimeout(UI.setBarPosition, 100); $D('noVNC_logo').style.display = "none"; diff --git a/webclients/novnc/include/util.js b/webclients/novnc/include/util.js index ddc1914..57ccb54 100644 --- a/webclients/novnc/include/util.js +++ b/webclients/novnc/include/util.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -57,6 +57,21 @@ if (!Array.prototype.map) }; } +// +// requestAnimationFrame shim with setTimeout fallback +// + +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback){ + window.setTimeout(callback, 1000 / 60); + }; +})(); + /* * ------------------------------------------------------ * Namespaced in Util @@ -131,6 +146,8 @@ Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { } } else if (type in {'integer':1, 'int':1}) { val = parseInt(val, 10); + } else if (type === 'str') { + val = String(val); } else if (type === 'func') { if (!val) { val = function () {}; diff --git a/webclients/novnc/include/vnc.js b/webclients/novnc/include/vnc.js index 2b31f45..7679d84 100644 --- a/webclients/novnc/include/vnc.js +++ b/webclients/novnc/include/vnc.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. diff --git a/webclients/novnc/include/web-socket-js/web_socket.js b/webclients/novnc/include/web-socket-js/web_socket.js index ec2a8b7..a133013 100644 --- a/webclients/novnc/include/web-socket-js/web_socket.js +++ b/webclients/novnc/include/web-socket-js/web_socket.js @@ -5,7 +5,7 @@ (function() { - if (window.WebSocket) return; + if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) return; var console = window.console; if (!console || !console.log || !console.error) { diff --git a/webclients/novnc/include/websock.js b/webclients/novnc/include/websock.js index 33350df..20d51d6 100644 --- a/webclients/novnc/include/websock.js +++ b/webclients/novnc/include/websock.js @@ -1,6 +1,6 @@ /* * Websock: high-performance binary WebSockets - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * Websock is similar to the standard WebSocket object but Websock @@ -20,6 +20,11 @@ // Load Flash WebSocket emulator if needed +// To force WebSocket emulator even when native WebSocket available +//window.WEB_SOCKET_FORCE_FLASH = true; +// To enable WebSocket emulator debug: +//window.WEB_SOCKET_DEBUG=1; + if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) { Websock_native = true; } else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) { @@ -28,9 +33,6 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) { } else { /* no builtin WebSocket so load web_socket.js */ - // To enable debug: - // window.WEB_SOCKET_DEBUG=1; - Websock_native = false; (function () { function get_INCLUDE_URI() { @@ -280,6 +282,8 @@ function open(uri) { Util.Debug(">> WebSock.onopen"); if (websocket.protocol) { Util.Info("Server chose sub-protocol: " + websocket.protocol); + } else { + Util.Error("Server select no sub-protocol!: " + websocket.protocol); } eventHandlers.open(); Util.Debug("<< WebSock.onopen"); diff --git a/webclients/novnc/include/webutil.js b/webclients/novnc/include/webutil.js index 7a0a076..6722fe3 100644 --- a/webclients/novnc/include/webutil.js +++ b/webclients/novnc/include/webutil.js @@ -1,6 +1,6 @@ /* * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin + * Copyright (C) 2012 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * * See README.md for usage and integration instructions. |