import * as Log from '../core/util/logging.js';
import _, { l10n } from '../core/util/localization.js';
- import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
+ import { isTouchDevice } from '../core/util/browsers.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
import keysyms from "../core/input/keysymdef.js";
UI.addControlbarHandlers();
UI.addTouchSpecificHandlers();
UI.addExtraKeysHandlers();
- UI.addXvpHandlers();
+ UI.addMachineHandlers();
UI.addConnectionControlHandlers();
UI.addClipboardHandlers();
UI.addSettingsHandlers();
UI.initSetting('host', window.location.hostname);
UI.initSetting('port', port);
UI.initSetting('encrypt', (window.location.protocol === "https:"));
- UI.initSetting('cursor', !isTouchDevice);
UI.initSetting('view_clip', false);
UI.initSetting('resize', 'off');
UI.initSetting('shared', true);
}
},
- initRFB: function() {
- try {
- UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
- 'onNotification': UI.notification,
- 'onUpdateState': UI.updateState,
- 'onDisconnected': UI.disconnectFinished,
- 'onPasswordRequired': UI.passwordRequired,
- 'onXvpInit': UI.updateXvpButton,
- 'onClipboard': UI.clipboardReceive,
- 'onBell': UI.bell,
- 'onFBUComplete': UI.initialResize,
- 'onFBResize': UI.updateSessionSize,
- 'onDesktopName': UI.updateDesktopName});
- return true;
- } catch (exc) {
- var msg = "Unable to create RFB client -- " + exc;
- Log.Error(msg);
- UI.showStatus(msg, 'error');
- return false;
- }
- },
-
/* ------^-------
* /INIT
* ==============
document.getElementById("noVNC_keyboard_button")
.addEventListener('click', UI.toggleVirtualKeyboard);
- UI.touchKeyboard = new Keyboard({target: document.getElementById('noVNC_keyboardinput'),
- onKeyEvent: UI.keyEvent});
+ UI.touchKeyboard = new Keyboard(document.getElementById('noVNC_keyboardinput'));
+ UI.touchKeyboard.onkeyevent = UI.keyEvent;
UI.touchKeyboard.grab();
document.getElementById("noVNC_keyboardinput")
.addEventListener('input', UI.keyInput);
document.documentElement
.addEventListener('mousedown', UI.keepVirtualKeyboard, true);
- document.documentElement
- .addEventListener('touchstart', UI.keepVirtualKeyboard, true);
document.getElementById("noVNC_control_bar")
.addEventListener('touchstart', UI.activateControlbar);
.addEventListener('click', UI.sendCtrlAltDel);
},
- addXvpHandlers: function() {
- document.getElementById("noVNC_xvp_shutdown_button")
- .addEventListener('click', function() { UI.rfb.xvpShutdown(); });
- document.getElementById("noVNC_xvp_reboot_button")
- .addEventListener('click', function() { UI.rfb.xvpReboot(); });
- document.getElementById("noVNC_xvp_reset_button")
- .addEventListener('click', function() { UI.rfb.xvpReset(); });
- document.getElementById("noVNC_xvp_button")
- .addEventListener('click', UI.toggleXvpPanel);
+ addMachineHandlers: function() {
+ document.getElementById("noVNC_shutdown_button")
+ .addEventListener('click', function() { UI.rfb.machineShutdown(); });
+ document.getElementById("noVNC_reboot_button")
+ .addEventListener('click', function() { UI.rfb.machineReboot(); });
+ document.getElementById("noVNC_reset_button")
+ .addEventListener('click', function() { UI.rfb.machineReset(); });
+ document.getElementById("noVNC_power_button")
+ .addEventListener('click', UI.togglePowerPanel);
},
addConnectionControlHandlers: function() {
.addEventListener('click', UI.toggleSettingsPanel);
UI.addSettingChangeHandler('encrypt');
- UI.addSettingChangeHandler('cursor');
- UI.addSettingChangeHandler('cursor', UI.updateLocalCursor);
UI.addSettingChangeHandler('resize');
UI.addSettingChangeHandler('resize', UI.enableDisableViewClip);
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
* VISUAL
* ------v------*/
- updateState: function(rfb, state, oldstate) {
+ updateState: function(event) {
var msg;
document.documentElement.classList.remove("noVNC_connecting");
document.documentElement.classList.remove("noVNC_disconnecting");
document.documentElement.classList.remove("noVNC_reconnecting");
- switch (state) {
+ switch (event.detail.state) {
case 'connecting':
document.getElementById("noVNC_transition_text").textContent = _("Connecting...");
document.documentElement.classList.add("noVNC_connecting");
case 'connected':
UI.connected = true;
UI.inhibit_reconnect = false;
+ UI.doneInitialResize = false;
document.documentElement.classList.add("noVNC_connected");
- if (rfb && rfb.get_encrypt()) {
+ if (UI.getSetting('encrypt')) {
msg = _("Connected (encrypted) to ") + UI.desktopName;
} else {
msg = _("Connected (unencrypted) to ") + UI.desktopName;
UI.enableDisableViewClip();
- if (cursorURIsSupported() && !isTouchDevice) {
- UI.enableSetting('cursor');
- } else {
- UI.disableSetting('cursor');
- }
-
if (UI.connected) {
UI.disableSetting('encrypt');
UI.disableSetting('shared');
UI.enableSetting('port');
UI.enableSetting('path');
UI.enableSetting('repeaterID');
- UI.updateXvpButton(0);
+ UI.updatePowerButton();
UI.keepControlbar();
}
// Hide input related buttons in view only mode
- if (UI.rfb && UI.rfb.get_view_only()) {
+ if (UI.rfb && UI.rfb.viewOnly) {
document.getElementById('noVNC_keyboard_button')
.classList.add('noVNC_hidden');
document.getElementById('noVNC_toggle_extra_keys_button')
document.getElementById('noVNC_status').classList.remove("noVNC_open");
},
- notification: function (rfb, msg, level, options) {
- UI.showStatus(msg, level);
+ notification: function (e) {
+ UI.showStatus(e.detail.message, e.detail.level);
},
activateControlbar: function(event) {
closeAllPanels: function() {
UI.closeSettingsPanel();
- UI.closeXvpPanel();
+ UI.closePowerPanel();
UI.closeClipboardPanel();
UI.closeExtraKeys();
},
// Refresh UI elements from saved cookies
UI.updateSetting('encrypt');
- if (cursorURIsSupported()) {
- UI.updateSetting('cursor');
- } else {
- UI.updateSetting('cursor', !isTouchDevice);
- UI.disableSetting('cursor');
- }
UI.updateSetting('view_clip');
UI.updateSetting('resize');
UI.updateSetting('shared');
/* ------^-------
* /SETTINGS
* ==============
- * XVP
+ * POWER
* ------v------*/
- openXvpPanel: function() {
+ openPowerPanel: function() {
UI.closeAllPanels();
UI.openControlbar();
- document.getElementById('noVNC_xvp')
+ document.getElementById('noVNC_power')
.classList.add("noVNC_open");
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.add("noVNC_selected");
},
- closeXvpPanel: function() {
- document.getElementById('noVNC_xvp')
+ closePowerPanel: function() {
+ document.getElementById('noVNC_power')
.classList.remove("noVNC_open");
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.remove("noVNC_selected");
},
- toggleXvpPanel: function() {
- if (document.getElementById('noVNC_xvp')
+ togglePowerPanel: function() {
+ if (document.getElementById('noVNC_power')
.classList.contains("noVNC_open")) {
- UI.closeXvpPanel();
+ UI.closePowerPanel();
} else {
- UI.openXvpPanel();
+ UI.openPowerPanel();
}
},
- // Disable/enable XVP button
- updateXvpButton: function(ver) {
- if (ver >= 1 && !UI.rfb.get_view_only()) {
- document.getElementById('noVNC_xvp_button')
+ // Disable/enable power button
+ updatePowerButton: function() {
+ if (UI.connected &&
+ UI.rfb.capabilities.power &&
+ !UI.rfb.viewOnly) {
+ document.getElementById('noVNC_power_button')
.classList.remove("noVNC_hidden");
} else {
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.add("noVNC_hidden");
- // Close XVP panel if open
- UI.closeXvpPanel();
+ // Close power panel if open
+ UI.closePowerPanel();
}
},
/* ------^-------
- * /XVP
+ * /POWER
* ==============
* CLIPBOARD
* ------v------*/
}
},
- clipboardReceive: function(rfb, text) {
- Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
- document.getElementById('noVNC_clipboard_text').value = text;
+ clipboardReceive: function(e) {
+ Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0,40) + "...");
+ document.getElementById('noVNC_clipboard_text').value = e.detail.text;
Log.Debug("<< UI.clipboardReceive");
},
return;
}
- if (!UI.initRFB()) return;
-
UI.closeAllPanels();
UI.closeConnectPanel();
- UI.rfb.set_encrypt(UI.getSetting('encrypt'));
- UI.rfb.set_shared(UI.getSetting('shared'));
- UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
-
- UI.updateLocalCursor();
UI.updateViewOnly();
- UI.rfb.connect(host, port, password, path);
+ var url;
+
+ url = UI.getSetting('encrypt') ? 'wss' : 'ws';
+
+ url += '://' + host;
+ if(port) {
+ url += ':' + port;
+ }
+ url += '/' + path;
+
+ UI.rfb = new RFB(document.getElementById('noVNC_canvas'), url,
+ { shared: UI.getSetting('shared'),
+ repeaterID: UI.getSetting('repeaterID'),
+ credentials: { password: password } });
+ UI.rfb.addEventListener("notification", UI.notification);
+ UI.rfb.addEventListener("updatestate", UI.updateState);
+ UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
+ UI.rfb.addEventListener("credentialsrequired", UI.credentials);
+ UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); UI.initialResize(); });
+ UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
+ UI.rfb.addEventListener("bell", UI.bell);
+ UI.rfb.addEventListener("fbresize", UI.updateSessionSize);
+ UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
},
disconnect: function() {
// Disable automatic reconnecting
UI.inhibit_reconnect = true;
- // Restore the callback used for initial resize
- UI.rfb.set_onFBUComplete(UI.initialResize);
-
// Don't display the connection settings until we're actually disconnected
},
UI.connect(null, UI.reconnect_password);
},
- disconnectFinished: function (rfb, reason) {
- if (typeof reason !== 'undefined') {
- UI.showStatus(reason, 'error');
+ disconnectFinished: function (e) {
+ if (typeof e.detail.reason !== 'undefined') {
+ UI.showStatus(e.detail.reason, 'error');
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
document.getElementById("noVNC_transition_text").textContent = _("Reconnecting...");
document.documentElement.classList.add("noVNC_reconnecting");
* PASSWORD
* ------v------*/
- passwordRequired: function(rfb, msg) {
-
+ credentials: function(e) {
+ // FIXME: handle more types
document.getElementById('noVNC_password_dlg')
.classList.add('noVNC_open');
document.getElementById('noVNC_password_input').focus();
}, 100);
- if (typeof msg === 'undefined') {
- msg = _("Password is required");
- }
+ var msg = _("Password is required");
Log.Warn(msg);
UI.showStatus(msg, "warning");
},
setPassword: function(e) {
+ // Prevent actually submitting the form
+ e.preventDefault();
+
var inputElem = document.getElementById('noVNC_password_input');
var password = inputElem.value;
// Clear the input after reading the password
inputElem.value = "";
- UI.rfb.sendPassword(password);
+ UI.rfb.sendCredentials({ password: password });
UI.reconnect_password = password;
document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open');
- // Prevent actually submitting the form
- e.preventDefault();
},
/* ------^-------
var screen = UI.screenSize();
- if (screen && UI.connected && UI.rfb.get_display()) {
+ if (screen && UI.connected) {
- var display = UI.rfb.get_display();
var resizeMode = UI.getSetting('resize');
- display.set_scale(1);
+ UI.rfb.viewportScale = 1.0;
// Make sure the viewport is adjusted first
UI.updateViewClip();
if (!UI.rfb) return;
var resizeMode = UI.getSetting('resize');
- if (resizeMode !== 'scale' && resizeMode !== 'downscale') {
+ if (resizeMode !== 'scale') {
return;
}
var screen = UI.screenSize();
- if (!screen || !UI.connected || !UI.rfb.get_display()) {
+ if (!screen || !UI.connected) {
return;
}
- var display = UI.rfb.get_display();
- var downscaleOnly = resizeMode === 'downscale';
- display.autoscale(screen.w, screen.h, downscaleOnly);
+ UI.rfb.autoscale(screen.w, screen.h);
UI.fixScrollbars();
},
// Normally we only apply the current resize mode after a window resize
// event. This means that when a new connection is opened, there is no
// resize mode active.
- // We have to wait until the first FBU because this is where the client
- // will find the supported encodings of the server. Some calls later in
- // the chain is dependant on knowing the server-capabilities.
- initialResize: function(rfb, fbu) {
+ // We have to wait until we know the capabilities of the server as
+ // some calls later in the chain is dependant on knowing the
+ // server-capabilities.
+ initialResize: function() {
+ if (UI.doneInitialResize) return;
+
UI.applyResizeMode();
- // After doing this once, we remove the callback.
- UI.rfb.set_onFBUComplete(function() { });
+ UI.doneInitialResize = true;
},
/* ------^-------
updateViewClip: function() {
if (!UI.rfb) return;
- var display = UI.rfb.get_display();
- var cur_clip = display.get_viewport();
+ var cur_clip = UI.rfb.clipViewport;
var new_clip = UI.getSetting('view_clip');
var resizeSetting = UI.getSetting('resize');
- if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
+ if (resizeSetting === 'scale') {
// Disable viewport clipping if we are scaling
new_clip = false;
} else if (isTouchDevice) {
}
if (cur_clip !== new_clip) {
- display.set_viewport(new_clip);
+ UI.rfb.clipViewport = new_clip;
}
var size = UI.screenSize();
if (new_clip && size) {
// When clipping is enabled, the screen is limited to
// the size of the browser window.
- display.viewportChangeSize(size.w, size.h);
+ UI.rfb.viewportChangeSize(size.w, size.h);
UI.fixScrollbars();
}
enableDisableViewClip: function() {
var resizeSetting = UI.getSetting('resize');
// Disable clipping if we are scaling, connected or on touch
- if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
+ if (resizeSetting === 'scale' ||
isTouchDevice) {
UI.disableSetting('view_clip');
} else {
toggleViewDrag: function() {
if (!UI.rfb) return;
- var drag = UI.rfb.get_viewportDrag();
+ var drag = UI.rfb.dragViewport;
UI.setViewDrag(!drag);
},
setViewDrag: function(drag) {
if (!UI.rfb) return;
- UI.rfb.set_viewportDrag(drag);
+ UI.rfb.dragViewport = drag;
UI.updateViewDrag();
},
// Check if viewport drag is possible. It is only possible
// if the remote display is clipping the client display.
- if (UI.rfb.get_display().get_viewport() &&
- UI.rfb.get_display().clippingDisplay()) {
+ if (UI.rfb.clipViewport && UI.rfb.isClipped) {
clipping = true;
}
var viewDragButton = document.getElementById('noVNC_view_drag_button');
if (!clipping &&
- UI.rfb.get_viewportDrag()) {
+ UI.rfb.dragViewport) {
// The size of the remote display is the same or smaller
// than the client display. Make sure viewport drag isn't
// active when it can't be used.
- UI.rfb.set_viewportDrag(false);
+ UI.rfb.dragViewport = false;
}
- if (UI.rfb.get_viewportDrag()) {
+ if (UI.rfb.dragViewport) {
viewDragButton.classList.add("noVNC_selected");
} else {
viewDragButton.classList.remove("noVNC_selected");
onfocusVirtualKeyboard: function(event) {
document.getElementById('noVNC_keyboard_button')
.classList.add("noVNC_selected");
+ if (UI.rfb) {
+ UI.rfb.set_focus_on_click(false);
+ }
},
onblurVirtualKeyboard: function(event) {
document.getElementById('noVNC_keyboard_button')
.classList.remove("noVNC_selected");
+ if (UI.rfb) {
+ UI.rfb.set_focus_on_click(true);
+ }
},
keepVirtualKeyboard: function(event) {
}
}
- // The default action of touchstart is to generate other
- // events, which other elements might depend on. So we can't
- // blindly prevent that. Instead restore focus right away.
- if (event.type === "touchstart") {
- setTimeout(input.focus.bind(input));
- } else {
- event.preventDefault();
- }
+ event.preventDefault();
},
keyboardinputReset: function() {
* ------v------*/
setMouseButton: function(num) {
- var view_only = UI.rfb.get_view_only();
+ var view_only = UI.rfb.viewOnly;
if (UI.rfb && !view_only) {
- UI.rfb.get_mouse().set_touchButton(num);
+ UI.rfb.touchButton = num;
}
var blist = [0, 1,2,4];
}
},
- updateLocalCursor: function() {
- if (!UI.rfb) return;
- UI.rfb.set_local_cursor(UI.getSetting('cursor'));
- },
-
updateViewOnly: function() {
if (!UI.rfb) return;
- UI.rfb.set_view_only(UI.getSetting('view_only'));
+ UI.rfb.viewOnly = UI.getSetting('view_only');
},
updateLogging: function() {
WebUtil.init_logging(UI.getSetting('logging'));
},
- updateSessionSize: function(rfb, width, height) {
+ updateSessionSize: function(e) {
UI.updateViewClip();
UI.updateScaling();
UI.fixScrollbars();
screen.style.overflow = "";
},
- updateDesktopName: function(rfb, name) {
- UI.desktopName = name;
+ updateDesktopName: function(e) {
+ UI.desktopName = e.detail.name;
// Display the desktop name in the document title
- document.title = name + " - noVNC";
+ document.title = e.detail.name + " - noVNC";
},
- bell: function(rfb) {
+ bell: function(e) {
if (WebUtil.getConfigVar('bell', 'on') === 'on') {
var promise = document.getElementById('noVNC_bell').play();
// The standards disagree on the return value here
import * as Log from './util/logging.js';
import _ from './util/localization.js';
import { decodeUTF8 } from './util/strings.js';
- import { set_defaults, make_properties } from './util/properties.js';
+ import { browserSupportsCursorURIs, isTouchDevice } from './util/browsers.js';
+ import EventTargetMixin from './util/eventtarget.js';
import Display from "./display.js";
import Keyboard from "./input/keyboard.js";
import Mouse from "./input/mouse.js";
import XtScancode from "./input/xtscancodes.js";
import Inflator from "./inflator.js";
import { encodings, encodingName } from "./encodings.js";
+ import "./util/polyfill.js";
/*jslint white: false, browser: true */
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */
- export default function RFB(defaults) {
- "use strict";
- if (!defaults) {
- defaults = {};
+ // How many seconds to wait for a disconnect to finish
+ var DISCONNECT_TIMEOUT = 3;
+
+ export default function RFB(target, url, options) {
+ if (!target) {
+ throw Error("Must specify target");
+ }
+ if (!url) {
+ throw Error("Must specify URL");
}
- this._rfb_host = '';
- this._rfb_port = 5900;
- this._rfb_password = '';
- this._rfb_path = '';
+ this._target = target;
+ this._url = url;
+
+ // Connection details
+ options = options || {}
+ this._rfb_credentials = options.credentials || {};
+ this._shared = 'shared' in options ? !!options.shared : true;
+ this._repeaterID = options.repeaterID || '';
+ // Internal state
this._rfb_connection_state = '';
this._rfb_init_state = '';
- this._rfb_version = 0;
- this._rfb_max_version = 3.8;
this._rfb_auth_scheme = '';
this._rfb_disconnect_reason = "";
+ // Server capabilities
+ this._rfb_version = 0;
+ this._rfb_max_version = 3.8;
this._rfb_tightvnc = false;
this._rfb_xvp_ver = 0;
- this._encHandlers = {};
- this._encStats = {};
+ this._fb_width = 0;
+ this._fb_height = 0;
+
+ this._fb_name = "";
+ this._capabilities = { power: false, resize: false };
+
+ this._supportsFence = false;
+
+ this._supportsContinuousUpdates = false;
+ this._enabledContinuousUpdates = false;
+
+ this._supportsSetDesktopSize = false;
+ this._screen_id = 0;
+ this._screen_flags = 0;
+
+ this._qemuExtKeyEventSupported = false;
+
+ // Internal objects
this._sock = null; // Websock object
this._display = null; // Display object
this._flushing = false; // Display flushing state
this._keyboard = null; // Keyboard input handler object
this._mouse = null; // Mouse input handler object
- this._disconnTimer = null; // disconnection timer
- this._supportsFence = false;
+ // Timers
+ this._disconnTimer = null; // disconnection timer
- this._supportsContinuousUpdates = false;
- this._enabledContinuousUpdates = false;
+ // Decoder states and stats
+ this._encHandlers = {};
+ this._encStats = {};
- // Frame buffer update state
this._FBU = {
rects: 0,
- subrects: 0, // RRE
+ subrects: 0, // RRE and HEXTILE
lines: 0, // RAW
tiles: 0, // HEXTILE
bytes: 0,
encoding: 0,
subencoding: -1,
background: null,
- zlib: [] // TIGHT zlib streams
+ zlibs: [] // TIGHT zlib streams
};
-
- this._fb_width = 0;
- this._fb_height = 0;
- this._fb_name = "";
+ for (var i = 0; i < 4; i++) {
+ this._FBU.zlibs[i] = new Inflator();
+ }
this._destBuff = null;
this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
pixels: 0
};
- this._supportsSetDesktopSize = false;
- this._screen_id = 0;
- this._screen_flags = 0;
-
// Mouse state
this._mouse_buttonMask = 0;
this._mouse_arr = [];
this._viewportDragPos = {};
this._viewportHasMoved = false;
- // QEMU Extended Key Event support - default to false
- this._qemuExtKeyEventSupported = false;
-
- // set the default value on user-facing properties
- set_defaults(this, defaults, {
- 'target': 'null', // VNC display rendering Canvas object
- 'encrypt': false, // Use TLS/SSL/wss encryption
- 'local_cursor': false, // Request locally rendered cursor
- 'shared': true, // Request shared mode
- 'view_only': false, // Disable client mouse/keyboard
- 'focus_on_click': true, // Grab focus on canvas on mouse click
- 'xvp_password_sep': '@', // Separator for XVP password fields
- 'disconnectTimeout': 3, // Time (s) to wait for disconnection
- 'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
- 'repeaterID': '', // [UltraVNC] RepeaterID to connect to
- 'viewportDrag': false, // Move the viewport on mouse drags
-
- // Callback functions
- 'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change
- 'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI
- 'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished
- 'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required
- 'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received
- 'onBell': function () { }, // onBell(rfb): RFB Bell message received
- 'onFBUReceive': function () { }, // onFBUReceive(rfb, rect): RFB FBU rect received but not yet processed
- 'onFBUComplete': function () { }, // onFBUComplete(rfb): RFB FBU received and processed
- 'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized
- 'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received
- 'onXvpInit': function () { } // onXvpInit(version): XVP extensions active for this connection
- });
-
+ // Bound event handlers
+ this._eventHandlers = {
+ focusCanvas: this._focusCanvas.bind(this),
+ };
+
// main setup
Log.Debug(">> RFB.constructor");
// NB: nothing that needs explicit teardown should be done
// before this point, since this can throw an exception
try {
- this._display = new Display({target: this._target,
- onFlush: this._onFlush.bind(this)});
+ this._display = new Display(this._target);
} catch (exc) {
Log.Error("Display exception: " + exc);
throw exc;
}
+ this._display.onflush = this._onFlush.bind(this);
+ this._display.clear();
- this._keyboard = new Keyboard({target: this._target,
- onKeyEvent: this._handleKeyEvent.bind(this)});
+ this._keyboard = new Keyboard(this._target);
+ this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
- this._mouse = new Mouse({target: this._target,
- onMouseButton: this._handleMouseButton.bind(this),
- onMouseMove: this._handleMouseMove.bind(this)});
+ this._mouse = new Mouse(this._target);
+ this._mouse.onmousebutton = this._handleMouseButton.bind(this);
+ this._mouse.onmousemove = this._handleMouseMove.bind(this);
this._sock = new Websock();
this._sock.on('message', this._handle_message.bind(this));
Log.Warn("WebSocket on-error event");
});
- this._init_vars();
- this._cleanup();
-
- var rmode = this._display.get_render_mode();
- Log.Info("Using native WebSockets, render mode: " + rmode);
+ // Slight delay of the actual connection so that the caller has
+ // time to set up callbacks
+ setTimeout(this._updateConnectionState.bind(this, 'connecting'));
Log.Debug("<< RFB.constructor");
};
RFB.prototype = {
- // Public methods
- connect: function (host, port, password, path) {
- this._rfb_host = host;
- this._rfb_port = port;
- this._rfb_password = (password !== undefined) ? password : "";
- this._rfb_path = (path !== undefined) ? path : "";
+ // ===== PROPERTIES =====
- if (!this._rfb_host) {
- return this._fail(
- _("Must set host"));
- }
+ dragViewport: false,
++ focusOnClick: true,
- this._rfb_init_state = '';
- this._updateConnectionState('connecting');
- return true;
+ _viewOnly: false,
+ get viewOnly() { return this._viewOnly; },
+ set viewOnly(viewOnly) {
+ this._viewOnly = viewOnly;
+
+ if (this._rfb_connection_state === "connecting" ||
+ this._rfb_connection_state === "connected") {
+ if (viewOnly) {
+ this._keyboard.ungrab();
+ this._mouse.ungrab();
+ } else {
+ this._keyboard.grab();
+ this._mouse.grab();
+ }
+ }
},
+ get capabilities() { return this._capabilities; },
+
+ get touchButton() { return this._mouse.touchButton; },
+ set touchButton(button) { this._mouse.touchButton = button; },
+
+ get viewportScale() { return this._display.scale; },
+ set viewportScale(scale) { this._display.scale = scale; },
+
+ get clipViewport() { return this._display.clipViewport; },
+ set clipViewport(viewport) { this._display.clipViewport = viewport; },
+
+ get isClipped() { return this._display.isClipped; },
+
+ // ===== PUBLIC METHODS =====
+
disconnect: function () {
this._updateConnectionState('disconnecting');
this._sock.off('error');
this._sock.off('open');
},
- sendPassword: function (passwd) {
- this._rfb_password = passwd;
+ sendCredentials: function (creds) {
+ this._rfb_credentials = creds;
setTimeout(this._init_msg.bind(this), 0);
},
sendCtrlAltDel: function () {
- if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
+ if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
Log.Info("Sending Ctrl-Alt-Del");
this.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
this.sendKey(KeyTable.XK_Delete, "Delete", false);
this.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
this.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
-
- return true;
- },
-
- xvpOp: function (ver, op) {
- if (this._rfb_xvp_ver < ver) { return false; }
- Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
- this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
- return true;
},
- xvpShutdown: function () {
- return this.xvpOp(1, 2);
+ machineShutdown: function () {
+ this._xvpOp(1, 2);
},
- xvpReboot: function () {
- return this.xvpOp(1, 3);
+ machineReboot: function () {
+ this._xvpOp(1, 3);
},
- xvpReset: function () {
- return this.xvpOp(1, 4);
+ machineReset: function () {
+ this._xvpOp(1, 4);
},
// Send a key press. If 'down' is not specified then send a down key
// followed by an up key.
sendKey: function (keysym, code, down) {
- if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
+ if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
if (down === undefined) {
this.sendKey(keysym, code, true);
this.sendKey(keysym, code, false);
- return true;
+ return;
}
var scancode = XtScancode[code];
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
} else {
if (!keysym) {
- return false;
+ return;
}
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
}
-
- return true;
},
clipboardPasteFrom: function (text) {
- if (this._rfb_connection_state !== 'connected' || this._view_only) { return; }
+ if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
RFB.messages.clientCutText(this._sock, text);
},
+ autoscale: function (width, height) {
+ if (this._rfb_connection_state !== 'connected') { return; }
+ this._display.autoscale(width, height);
+ },
+
+ viewportChangeSize: function(width, height) {
+ if (this._rfb_connection_state !== 'connected') { return; }
+ this._display.viewportChangeSize(width, height);
+ },
+
// Requests a change of remote desktop size. This message is an extension
// and may only be sent if we have received an ExtendedDesktopSize message
requestDesktopSize: function (width, height) {
if (this._rfb_connection_state !== 'connected' ||
- this._view_only) {
- return false;
+ this._viewOnly) {
+ return;
}
- if (this._supportsSetDesktopSize) {
- RFB.messages.setDesktopSize(this._sock, width, height,
- this._screen_id, this._screen_flags);
- this._sock.flush();
- return true;
- } else {
- return false;
+ if (!this._supportsSetDesktopSize) {
+ return;
}
+
+ RFB.messages.setDesktopSize(this._sock, width, height,
+ this._screen_id, this._screen_flags);
},
- // Private methods
+ // ===== PRIVATE METHODS =====
_connect: function () {
Log.Debug(">> RFB.connect");
- this._init_vars();
- var uri;
- if (typeof UsingSocketIO !== 'undefined') {
- uri = 'http';
- } else {
- uri = this._encrypt ? 'wss' : 'ws';
- }
-
- uri += '://' + this._rfb_host;
- if(this._rfb_port) {
- uri += ':' + this._rfb_port;
- }
- uri += '/' + this._rfb_path;
-
- Log.Info("connecting to " + uri);
+ Log.Info("connecting to " + this._url);
try {
// WebSocket.onopen transitions to the RFB init states
- this._sock.open(uri, this._wsProtocols);
+ this._sock.open(this._url, ['binary']);
} catch (e) {
if (e.name === 'SyntaxError') {
this._fail("Invalid host or port value given", e);
}
// Always grab focus on some kind of click event
- this._target.addEventListener("mousedown", this._focusCanvas);
- this._target.addEventListener("touchstart", this._focusCanvas);
+ this._target.addEventListener("mousedown", this._eventHandlers.focusCanvas);
+ this._target.addEventListener("touchstart", this._eventHandlers.focusCanvas);
Log.Debug("<< RFB.connect");
},
_disconnect: function () {
Log.Debug(">> RFB.disconnect");
- this._target.removeEventListener("mousedown", this._focusCanvas);
- this._target.removeEventListener("touchstart", this._focusCanvas);
+ this._target.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
+ this._target.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
this._cleanup();
this._sock.close();
this._print_stats();
Log.Debug("<< RFB.disconnect");
},
- _init_vars: function () {
- // reset state
- this._FBU.rects = 0;
- this._FBU.subrects = 0; // RRE and HEXTILE
- this._FBU.lines = 0; // RAW
- this._FBU.tiles = 0; // HEXTILE
- this._FBU.zlibs = []; // TIGHT zlib encoders
- this._mouse_buttonMask = 0;
- this._mouse_arr = [];
- this._rfb_tightvnc = false;
-
- // Clear the per connection encoding stats
- var stats = this._encStats;
- Object.keys(stats).forEach(function (key) {
- stats[key][0] = 0;
- });
-
- var i;
- for (i = 0; i < 4; i++) {
- this._FBU.zlibs[i] = new Inflator();
- }
- },
-
_print_stats: function () {
var stats = this._encStats;
},
_cleanup: function () {
- if (!this._view_only) { this._keyboard.ungrab(); }
- if (!this._view_only) { this._mouse.ungrab(); }
+ if (!this._viewOnly) { this._keyboard.ungrab(); }
+ if (!this._viewOnly) { this._mouse.ungrab(); }
this._display.defaultCursor();
if (Log.get_logging() !== 'debug') {
- // Show noVNC logo on load and when disconnected, unless in
+ // Show noVNC logo when disconnected, unless in
// debug mode
this._display.clear();
}
},
- // Event handler for canvas so this points to the canvas element
_focusCanvas: function(event) {
// Respect earlier handlers' request to not do side-effects
- if (!event.defaultPrevented)
- this.focus();
+ if (event.defaultPrevented) {
+ return;
+ }
+
+ if (!this._focus_on_click) {
+ return;
+ }
+
+ this._target.focus();
},
/*
// State change actions
this._rfb_connection_state = state;
- this._onUpdateState(this, state, oldstate);
+ var event = new CustomEvent("updatestate", { detail: { state: state } });
+ this.dispatchEvent(event);
var smsg = "New state '" + state + "', was '" + oldstate + "'.";
Log.Debug(smsg);
switch (state) {
case 'disconnected':
- // Call onDisconnected callback after onUpdateState since
+ // Fire disconnected event after updatestate event since
// we don't know if the UI only displays the latest message
if (this._rfb_disconnect_reason !== "") {
- this._onDisconnected(this, this._rfb_disconnect_reason);
+ event = new CustomEvent("disconnect",
+ { detail: { reason: this._rfb_disconnect_reason } });
} else {
// No reason means clean disconnect
- this._onDisconnected(this);
+ event = new CustomEvent("disconnect", { detail: {} });
}
+ this.dispatchEvent(event);
break;
case 'connecting':
this._disconnTimer = setTimeout(function () {
this._rfb_disconnect_reason = _("Disconnect timeout");
this._updateConnectionState('disconnected');
- }.bind(this), this._disconnectTimeout * 1000);
+ }.bind(this), DISCONNECT_TIMEOUT * 1000);
break;
}
},
* Send a notification to the UI. Valid levels are:
* 'normal'|'warn'|'error'
*
- * NOTE: Options could be added in the future.
* NOTE: If this function is called multiple times, remember that the
* interface could be only showing the latest notification.
*/
- _notification: function(msg, level, options) {
+ _notification: function(msg, level) {
switch (level) {
case 'normal':
case 'warn':
return;
}
- if (options) {
- this._onNotification(this, msg, level, options);
- } else {
- this._onNotification(this, msg, level);
- }
+ var event = new CustomEvent("notification",
+ { detail: { message: msg, level: level } });
+ this.dispatchEvent(event);
+ },
+
+ _setCapability: function (cap, val) {
+ this._capabilities[cap] = val;
+ var event = new CustomEvent("capabilities",
+ { detail: { capabilities: this._capabilities } });
+ this.dispatchEvent(event);
},
_handle_message: function () {
this._mouse_buttonMask &= ~bmask;
}
- if (this._viewportDrag) {
+ if (this.dragViewport) {
if (down && !this._viewportDragging) {
this._viewportDragging = true;
this._viewportDragPos = {'x': x, 'y': y};
// If the viewport didn't actually move, then treat as a mouse click event
// Send the button down event here, as the button up event is sent at the end of this function
- if (!this._viewportHasMoved && !this._view_only) {
+ if (!this._viewportHasMoved && !this._viewOnly) {
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), bmask);
}
this._viewportHasMoved = false;
}
}
- if (this._view_only) { return; } // View only, skip mouse events
+ if (this._viewOnly) { return; } // View only, skip mouse events
if (this._rfb_connection_state !== 'connected') { return; }
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
return;
}
- if (this._view_only) { return; } // View only, skip mouse events
+ if (this._viewOnly) { return; } // View only, skip mouse events
if (this._rfb_connection_state !== 'connected') { return; }
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
}
if (is_repeater) {
- var repeaterID = this._repeaterID;
+ var repeaterID = "ID:" + this._repeaterID;
while (repeaterID.length < 250) {
repeaterID += "\0";
}
// authentication
_negotiate_xvp_auth: function () {
- var xvp_sep = this._xvp_password_sep;
- var xvp_auth = this._rfb_password.split(xvp_sep);
- if (xvp_auth.length < 3) {
- var msg = 'XVP credentials required (user' + xvp_sep +
- 'target' + xvp_sep + 'password) -- got only ' + this._rfb_password;
- this._onPasswordRequired(this, msg);
+ if (!this._rfb_credentials.username ||
+ !this._rfb_credentials.password ||
+ !this._rfb_credentials.target) {
+ var event = new CustomEvent("credentialsrequired",
+ { detail: { types: ["username", "password", "target"] } });
+ this.dispatchEvent(event);
return false;
}
- var xvp_auth_str = String.fromCharCode(xvp_auth[0].length) +
- String.fromCharCode(xvp_auth[1].length) +
- xvp_auth[0] +
- xvp_auth[1];
+ var xvp_auth_str = String.fromCharCode(this._rfb_credentials.username.length) +
+ String.fromCharCode(this._rfb_credentials.target.length) +
+ this._rfb_credentials.username +
+ this._rfb_credentials.target;
this._sock.send_string(xvp_auth_str);
- this._rfb_password = xvp_auth.slice(2).join(xvp_sep);
this._rfb_auth_scheme = 2;
return this._negotiate_authentication();
},
_negotiate_std_vnc_auth: function () {
if (this._sock.rQwait("auth challenge", 16)) { return false; }
- if (this._rfb_password.length === 0) {
- this._onPasswordRequired(this);
+ if (!this._rfb_credentials.password) {
+ var event = new CustomEvent("credentialsrequired",
+ { detail: { types: ["password"] } });
+ this.dispatchEvent(event);
return false;
}
// TODO(directxman12): make genDES not require an Array
var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
- var response = RFB.genDES(this._rfb_password, challenge);
+ var response = RFB.genDES(this._rfb_credentials.password, challenge);
this._sock.send(response);
this._rfb_init_state = "SecurityResult";
return true;
}
// we're past the point where we could backtrack, so it's safe to call this
- this._onDesktopName(this, this._fb_name);
+ var event = new CustomEvent("desktopname",
+ { detail: { name: this._fb_name } });
+ this.dispatchEvent(event);
this._resize(width, height);
- if (!this._view_only) { this._keyboard.grab(); }
- if (!this._view_only) { this._mouse.grab(); }
+ if (!this._viewOnly) { this._keyboard.grab(); }
+ if (!this._viewOnly) { this._mouse.grab(); }
this._fb_depth = 24;
encs.push(encodings.pseudoEncodingFence);
encs.push(encodings.pseudoEncodingContinuousUpdates);
- if (this._local_cursor && this._fb_depth == 24) {
+ if (browserSupportsCursorURIs() &&
+ !isTouchDevice && this._fb_depth == 24) {
encs.push(encodings.pseudoEncodingCursor);
}
var text = this._sock.rQshiftStr(length);
- if (this._view_only) { return true; }
+ if (this._viewOnly) { return true; }
- this._onClipboard(this, text);
+ var event = new CustomEvent("clipboard",
+ { detail: { text: text } });
+ this.dispatchEvent(event);
return true;
},
case 1: // XVP_INIT
this._rfb_xvp_ver = xvp_ver;
Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
- this._onXvpInit(this._rfb_xvp_ver);
+ this._setCapability("power", true);
break;
default:
this._fail("Unexpected server message",
case 2: // Bell
Log.Debug("Bell");
- this._onBell(this);
+ var event = new CustomEvent("bell", { detail: {} });
+ this.dispatchEvent(event);
return true;
case 3: // ServerCutText
this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
(hdr[10] << 8) + hdr[11], 10);
- this._onFBUReceive(this,
- {'x': this._FBU.x, 'y': this._FBU.y,
- 'width': this._FBU.width, 'height': this._FBU.height,
- 'encoding': this._FBU.encoding,
- 'encodingName': encodingName(this._FBU.encoding)});
-
if (!this._encHandlers[this._FBU.encoding]) {
this._fail("Unexpected server message",
"Unsupported encoding " +
this._display.flip();
- this._onFBUComplete(this);
-
return true; // We finished this FBU
},
this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4);
this._display.resize(this._fb_width, this._fb_height);
- this._onFBResize(this, this._fb_width, this._fb_height);
+
+ var event = new CustomEvent("fbresize",
+ { detail: { width: this._fb_width,
+ height: this._fb_height } });
+ this.dispatchEvent(event);
this._timing.fbu_rt_start = (new Date()).getTime();
this._updateContinuousUpdates();
- }
- };
-
- make_properties(RFB, [
- ['target', 'wo', 'dom'], // VNC display rendering Canvas object
- ['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
- ['local_cursor', 'rw', 'bool'], // Request locally rendered cursor
- ['shared', 'rw', 'bool'], // Request shared mode
- ['view_only', 'rw', 'bool'], // Disable client mouse/keyboard
- ['focus_on_click', 'rw', 'bool'], // Grab focus on canvas on mouse click
- ['xvp_password_sep', 'rw', 'str'], // Separator for XVP password fields
- ['disconnectTimeout', 'rw', 'int'], // Time (s) to wait for disconnection
- ['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection
- ['repeaterID', 'rw', 'str'], // [UltraVNC] RepeaterID to connect to
- ['viewportDrag', 'rw', 'bool'], // Move the viewport on mouse drags
-
- // Callback functions
- ['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate): connection state change
- ['onNotification', 'rw', 'func'], // onNotification(rfb, msg, level, options): notification for the UI
- ['onDisconnected', 'rw', 'func'], // onDisconnected(rfb, reason): disconnection finished
- ['onPasswordRequired', 'rw', 'func'], // onPasswordRequired(rfb, msg): VNC password is required
- ['onClipboard', 'rw', 'func'], // onClipboard(rfb, text): RFB clipboard contents received
- ['onBell', 'rw', 'func'], // onBell(rfb): RFB Bell message received
- ['onFBUReceive', 'rw', 'func'], // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed
- ['onFBUComplete', 'rw', 'func'], // onFBUComplete(rfb, fbu): RFB FBU received and processed
- ['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized
- ['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received
- ['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection
- ]);
-
- RFB.prototype.set_local_cursor = function (cursor) {
- if (!cursor || (cursor in {'0': 1, 'no': 1, 'false': 1})) {
- this._local_cursor = false;
- this._display.disableLocalCursor(); //Only show server-side cursor
- } else {
- if (this._display.get_cursor_uri()) {
- this._local_cursor = true;
- } else {
- Log.Warn("Browser does not support local cursor");
- this._display.disableLocalCursor();
- }
- }
-
- // Need to send an updated list of encodings if we are connected
- if (this._rfb_connection_state === "connected") {
- this._sendEncodings();
- }
- };
-
- RFB.prototype.set_view_only = function (view_only) {
- this._view_only = view_only;
+ },
- if (this._rfb_connection_state === "connecting" ||
- this._rfb_connection_state === "connected") {
- if (view_only) {
- this._keyboard.ungrab();
- this._mouse.ungrab();
- } else {
- this._keyboard.grab();
- this._mouse.grab();
- }
- }
+ _xvpOp: function (ver, op) {
+ if (this._rfb_xvp_ver < ver) { return; }
+ Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
+ RFB.messages.xvpOp(this._sock, ver, op);
+ },
};
- RFB.prototype.get_display = function () { return this._display; };
- RFB.prototype.get_keyboard = function () { return this._keyboard; };
- RFB.prototype.get_mouse = function () { return this._mouse; };
+ Object.assign(RFB.prototype, EventTargetMixin);
// Class Methods
RFB.messages = {
sock._sQlen += 10;
sock.flush();
- }
+ },
+
+ xvpOp: function (sock, ver, op) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 250; // msg-type
+ buff[offset + 1] = 0; // padding
+
+ buff[offset + 2] = ver;
+ buff[offset + 3] = op;
+
+ sock._sQlen += 4;
+ sock.flush();
+ },
};
RFB.genDES = function (password, challenge) {
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
this._supportsSetDesktopSize = true;
+ this._setCapability("resize", true);
+
var number_of_screens = this._sock.rQpeek8();
this._FBU.bytes = 4 + (number_of_screens * 16);
- # 1. Modules / API
+ # noVNC API
- The noVNC client is a composed of several modular components that handle
- rendering, input, networking, etc. Each of the modules is designed to
- be cross-browser and be useful as a standalone library in other
- projects (see LICENSE.txt).
+ The interface of the noVNC client consists of a single RFB object that
+ is instantiated once per connection.
+ ## RFB
- ## 1.1 Module List
+ The `RFB` object represents a single connection to a VNC server. It
+ communicates using a WebSocket that must provide a standard RFB
+ protocol stream.
- * __Mouse__ (core/input/mouse.js): Mouse input event handler with
- limited touch support.
+ ### Constructor
- * __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
- non-US keyboard support. Translates keyDown and keyUp events to X11
- keysym values.
+ [`RFB()`](#rfb-1)
+ - Creates and returns a new `RFB` object.
- * __Display__ (core/display.js): Efficient 2D rendering abstraction
- layered on the HTML5 canvas element.
+ ### Properties
- * __Websock__ (core/websock.js): Websock client from websockify
- with transparent binary data support.
- [Websock API](https://github.com/novnc/websockify/wiki/websock.js) wiki page.
+ `viewOnly`
+ - Is a `boolean` indicating if any events (e.g. key presses or mouse
+ movement) should be prevented from being sent to the server.
+ Disabled by default.
- * __RFB__ (core/rfb.js): Main class that implements the RFB
- protocol and stitches the other classes together.
++`focusOnClick`
++ - Is a `boolean` indicating if keyboard focus should automatically be
++ moved to the canvas when a `mousedown` or `touchstart` event is
++ received.
+
+ `touchButton`
+ - Is a `long` controlling the button mask that should be simulated
+ when a touch event is recieved. Uses the same values as
+ [`MouseEvent.button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button).
+ Is set to `1` by default.
- ## 1.2 Configuration Attributes
+ `viewportScale`
+ - Is a `double` indicating how the framebuffer contents should be
+ scaled before being rendered on to the canvas. See also
+ [`RFB.autoscale()`](#rfbautoscale). Is set to `1.0` by default.
- The Mouse, Keyboard, Display and RFB classes have a similar API for
- configuration options. Each configuration option has a default value,
- which can be overridden by a a configuration object passed to the
- constructor. Configuration options can then be read and modified after
- initialization with "get_*" and "set_*" methods respectively. For
- example, the following initializes an RFB object with the 'encrypt'
- configuration option enabled, then confirms it was set, then disables
- it.
+ `clipViewport`
+ - Is a `boolean` indicating if the canvas should be clipped to its
+ container. When disabled the container must be able to handle the
+ resulting overflow. Disabled by default.
- var rfb = new RFB({'encrypt': true});
- if (rfb.get_encrypt()) {
- alert("Encryption is set");
- }
- rfb.set_encrypt(false);
+ `dragViewport`
+ - Is a `boolean` indicating if mouse events should control the
+ relative position of a clipped canvas. Only relevant if
+ `clipViewport` is enabled. Disabled by default.
- Some attributes are read-only and cannot be changed. For example, the
- Display 'render_mode' option will throw an exception if an attempt is
- made to set it. The attribute mode is one of the following:
+ `isClipped` *Read only*
+ - Is a `boolean` indicating if the framebuffer is larger than the
+ current canvas, i.e. it is being clipped.
- RO - read only
- RW - read write
- WO - write once
+ `capabilities` *Read only*
+ - Is an `Object` indicating which optional extensions are available
+ on the server. Some methods may only be called if the corresponding
+ capability is set. The following capabilities are defined:
+ | name | type | description
+ | -------- | --------- | -----------
+ | `power` | `boolean` | Machine power control is available
+ | `resize` | `boolean` | The framebuffer can be resized
- ## 1.3 Methods
+ ### Events
- In addition to the getter and setter methods to modify configuration
- attributes, each of the modules has other methods that are available
- in the object instance. For example, the Display module has method
- named 'blitImage' which takes an array of pixel data and draws it to
- the 2D canvas.
+ [`updatestate`](#updatestate)
+ - The `updatestate` event is fired when the connection state of the
+ `RFB` object changes.
- ## 1.4 Callbacks
+ [`notification`](#notification)
+ - The `notification` event is fired when the `RFB` usage has a
+ message to display to the user.
- Each of the modules has certain events that can be hooked with
- callback functions. For the Mouse, Keyboard, Display and RFB classes
- the callback functions are assigned to configuration attributes. The
- WebSock module has a method named 'on' that takes two parameters: the
- callback event name, and the callback function.
+ [`disconnect`](#disconnected)
+ - The `disconnect` event is fired when the `RFB` object disconnects.
- ## 2. Modules
+ [`credentialsrequired`](#credentialsrequired)
+ - The `credentialsrequired` event is fired when more credentials must
+ be given to continue.
- ## 2.1 Mouse Module
+ [`clipboard`](#clipboard)
+ - The `clipboard` event is fired when clipboard data is received from
+ the server.
- ### 2.1.1 Configuration Attributes
+ [`bell`](#bell)
+ - The `bell` event is fired when a audible bell request is received
+ from the server.
- | name | type | mode | default | description
- | ----------- | ---- | ---- | -------- | ------------
- | target | DOM | WO | document | DOM element that captures mouse input
- | touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
+ [`fbresize`](#fbresize)
+ - The `fbresize` event is fired when the framebuffer size is changed.
- ### 2.1.2 Methods
+ [`desktopname`](#desktopname)
+ - The `desktopname` event is fired when the remote desktop name
+ changes.
- | name | parameters | description
- | ------ | ---------- | ------------
- | grab | () | Begin capturing mouse events
- | ungrab | () | Stop capturing mouse events
+ [`capabilities`](#capabilities)
+ - The `capabilities` event is fired when `RFB.capabilities` is
+ updated.
- ### 2.1.2 Callbacks
+ ### Methods
- | name | parameters | description
- | ------------- | ------------------- | ------------
- | onMouseButton | (x, y, down, bmask) | Handler for mouse button click/release
- | onMouseMove | (x, y) | Handler for mouse movement
+ [`RFB.disconnect()`](#rfbdisconnect)
+ - Disconnect from the server.
+ [`RFB.sendCredentials()`](#rfbsendcredentials)
+ - Send credentials to server. Should be called after the
+ [`credentialsrequired`](#credentialsrequired) event has fired.
- ## 2.2 Keyboard Module
+ [`RFB.sendKey()`](#rfbsendKey)
+ - Send a key event.
- ### 2.2.1 Configuration Attributes
-
- | name | type | mode | default | description
- | ------- | ---- | ---- | -------- | ------------
- | target | DOM | WO | document | DOM element that captures keyboard input
-
- ### 2.2.2 Methods
-
- | name | parameters | description
- | ------ | ---------- | ------------
- | grab | () | Begin capturing keyboard events
- | ungrab | () | Stop capturing keyboard events
-
- ### 2.2.3 Callbacks
-
- | name | parameters | description
- | ---------- | -------------------- | ------------
- | onKeyPress | (keysym, code, down) | Handler for key press/release
-
-
- ## 2.3 Display Module
-
- ### 2.3.1 Configuration Attributes
-
- | name | type | mode | default | description
- | ----------- | ----- | ---- | ------- | ------------
- | target | DOM | WO | | Canvas element for rendering
- | context | raw | RO | | Canvas 2D context for rendering
- | logo | raw | RW | | Logo to display when cleared: {"width": width, "height": height, "type": mime-type, "data": data}
- | scale | float | RW | 1.0 | Display area scale factor 0.0 - 1.0
- | viewport | bool | RW | false | Use viewport clipping
- | width | int | RO | | Display area width
- | height | int | RO | | Display area height
- | render_mode | str | RO | '' | Canvas rendering mode
- | prefer_js | str | RW | | Prefer JavaScript over canvas methods
- | cursor_uri | raw | RW | | Can we render cursor using data URI
-
- ### 2.3.2 Methods
-
- | name | parameters | description
- | ------------------ | ------------------------------------------------------- | ------------
- | viewportChangePos | (deltaX, deltaY) | Move the viewport relative to the current location
- | viewportChangeSize | (width, height) | Change size of the viewport
- | absX | (x) | Return X relative to the remote display
- | absY | (y) | Return Y relative to the remote display
- | resize | (width, height) | Set width and height
- | flip | (from_queue) | Update the visible canvas with the contents of the rendering canvas
- | clear | () | Clear the display (show logo if set)
- | pending | () | Check if there are waiting items in the render queue
- | flush | () | Resume processing the render queue unless it's empty
- | fillRect | (x, y, width, height, color, from_queue) | Draw a filled in rectangle
- | copyImage | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area
- | imageRect | (x, y, mime, arr) | Draw a rectangle with an image
- | startTile | (x, y, width, height, color) | Begin updating a tile
- | subTile | (tile, x, y, w, h, color) | Update a sub-rectangle within the given tile
- | finishTile | () | Draw the current tile to the display
- | blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
- | blitRgbImage | (x, y, width, height, arr, offset, from_queue) | Blit RGB encoded image to display
- | blitRgbxImage | (x, y, width, height, arr, offset, from_queue) | Blit RGBX encoded image to display
- | drawImage | (img, x, y) | Draw image and track damage
- | changeCursor | (pixels, mask, hotx, hoty, w, h) | Change cursor appearance
- | defaultCursor | () | Restore default cursor appearance
- | disableLocalCursor | () | Disable local (client-side) cursor
- | clippingDisplay | () | Check if the remote display is larger than the client display
- | autoscale | (containerWidth, containerHeight, downscaleOnly) | Scale the display
-
- ### 2.3.3 Callbacks
-
- | name | parameters | description
- | ------- | ---------- | ------------
- | onFlush | () | A display flush has been requested and we are now ready to resume FBU processing
-
-
- ## 2.4 RFB Module
-
- ### 2.4.1 Configuration Attributes
-
- | name | type | mode | default | description
- | ----------------- | ---- | ---- | ---------- | ------------
- | target | DOM | WO | null | Canvas element for rendering (passed to Display, Mouse and Keyboard)
- | encrypt | bool | RW | false | Use TLS/SSL encryption
- | local_cursor | bool | RW | false | Request locally rendered cursor
- | shared | bool | RW | true | Request shared VNC mode
- | view_only | bool | RW | false | Disable client mouse/keyboard
- | focus_on_click | bool | RW | true | Grab focus on canvas on mouse click
- | xvp_password_sep | str | RW | '@' | Separator for XVP password fields
- | disconnectTimeout | int | RW | 3 | Time (in seconds) to wait for disconnection
- | wsProtocols | arr | RW | ['binary'] | Protocols to use in the WebSocket connection
- | repeaterID | str | RW | '' | UltraVNC RepeaterID to connect to
- | viewportDrag | bool | RW | false | Move the viewport on mouse drags
-
- ### 2.4.2 Methods
-
- | name | parameters | description
- | ------------------ | ---------------------------- | ------------
- | connect | (host, port, password, path) | Connect to the given host:port/path. Optional password and path.
- | disconnect | () | Disconnect
- | sendPassword | (passwd) | Send password after onPasswordRequired callback
- | sendCtrlAltDel | () | Send Ctrl-Alt-Del key sequence
- | xvpOp | (ver, op) | Send a XVP operation (2=shutdown, 3=reboot, 4=reset)
- | xvpShutdown | () | Send XVP shutdown.
- | xvpReboot | () | Send XVP reboot.
- | xvpReset | () | Send XVP reset.
- | sendKey | (keysym, code, down) | Send a key press event. If down not specified, send a down and up event.
- | clipboardPasteFrom | (text) | Send a clipboard paste event
- | requestDesktopSize | (width, height) | Send a request to change the remote desktop size.
-
- ### 2.4.3 Callbacks
-
- | name | parameters | description
- | ------------------ | -------------------------- | ------------
- | onUpdateState | (rfb, state, oldstate) | Connection state change (see details below)
- | onNotification | (rfb, msg, level, options) | Notification for the UI (optional options)
- | onDisconnected | (rfb, reason) | Disconnection finished with an optional reason. No reason specified means normal disconnect.
- | onPasswordRequired | (rfb, msg) | VNC password is required (use sendPassword), optionally comes with a message.
- | onClipboard | (rfb, text) | RFB clipboard contents received
- | onBell | (rfb) | RFB Bell message received
- | onFBUReceive | (rfb, fbu) | RFB FBU received but not yet processed (see details below)
- | onFBUComplete | (rfb, fbu) | RFB FBU received and processed (see details below)
- | onFBResize | (rfb, width, height) | Frame buffer (remote desktop) size changed
- | onDesktopName | (rfb, name) | VNC desktop name recieved
- | onXvpInit | (version) | XVP extensions active for this connection.
-
-
- __RFB onUpdateState callback details__
-
- The RFB module has an 'onUpdateState' callback that is invoked after
- the noVNC connection state changes. Here is a list of the states that
- are reported. Note that the RFB module can not transition from the
- disconnected state in any way, a new instance of the object has to be
- created for new connections.
-
- | connection state | description
- | ---------------- | ------------
- | connecting | starting to connect
- | connected | connected normally
- | disconnecting | starting to disconnect
- | disconnected | disconnected - permanent end-state for this RFB object
-
- __RFB onFBUReceive and on FBUComplete callback details__
-
- The onFBUReceive callback is invoked when a frame buffer update
- message has been received from the server but before the RFB class has
- done any additional handling. The onFBUComplete callback is invoked
- with the same information but after the RFB class has handled the
- message.
-
- The 'fbu' parameter is an object with the following structure:
-
- {
- x: FBU_x_position,
- y: FBU_y_position,
- width: FBU_width,
- height: FBU_height,
- encoding: FBU_encoding_number,
- encodingName: FBU_encoding_string
- }
+ [`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
+ - Send Ctrl-Alt-Del key sequence.
+
+ [`RFB.machineShutdown()`](#rfbmachineshutdown)
+ - Request a shutdown of the remote machine.
+
+ [`RFB.machineReboot()`](#rfbmachinereboot)
+ - Request a reboot of the remote machine.
+
+ [`RFB.machineReset()`](#rfbmachinereset)
+ - Request a reset of the remote machine.
+
+ [`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
+ - Send clipboard contents to server.
+
+ [`RFB.autoscale()`](#rfbautoscale)
+ - Set `RFB.viewportScale` so that the framebuffer fits a specified
+ container.
+
+ [`RFB.requestDesktopSize()`](#rfbrequestDesktopSize)
+ - Send a request to change the remote desktop size.
+
+ [`RFB.viewportChangeSize()`](#rfbviewportChangeSize)
+ - Change size of the viewport.
+
+ ### Details
+
+ #### RFB()
+
+ The `RFB()` constructor returns a new `RFB` object and initiates a new
+ connection to a specified VNC server.
+
+ ##### Syntax
+
+ var rfb = new RFB( target, url [, options] );
+
+ ###### Parameters
+
+ **`target`**
+ - A [`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement)
+ that specifies where graphics should be rendered and input events
+ should be monitored.
+
+ **`url`**
+ - A `DOMString` specifying the VNC server to connect to. This must be
+ a valid WebSocket URL.
+
+ **`options`** *Optional*
+ - An `Object` specifying extra details about how the connection
+ should be made.
+
+ Possible options:
+
+ `shared`
+ - A `boolean` indicating if the remote server should be shared or
+ if any other connected clients should be disconnected. Enabled
+ by default.
+
+ `credentials`
+ - An `Object` specifying the credentials to provide to the server
+ when authenticating. The following credentials are possible:
+
+ | name | type | description
+ | ------------ | ----------- | -----------
+ | `"username"` | `DOMString` | The user that authenticates
+ | `"password"` | `DOMString` | Password for the user
+ | `"target"` | `DOMString` | Target machine or session
+
+ `repeaterID`
+ - A `DOMString` specifying the ID to provide to any VNC repeater
+ encountered.
+
+ #### updatestate
+
+ The `updatestate` event is fired after the noVNC connection state
+ changes. The `detail` property is an `Object` containg the property
+ `state` with the new connection state.
+
+ Here is a list of the states that are reported:
+
+ | connection state | description
+ | ----------------- | ------------
+ | `"connecting"` | starting to connect
+ | `"connected"` | connected normally
+ | `"disconnecting"` | starting to disconnect
+ | `"disconnected"` | disconnected
+
+ Note that a `RFB` objects can not transition from the disconnected
+ state in any way, a new instance of the object has to be created for
+ new connections.
+
+ #### notification
+
+ The `notification` event is fired when the `RFB` object wants a message
+ displayed to the user. The `detail` property is an `Object` containing
+ the following properties:
+
+ | Property | Type | Description
+ | --------- | ----------- | -----------
+ | `message` | `DOMString` | The message to display
+ | `level` | `DOMString` | The severity of the message
+
+ The following levels are currently defined:
+
+ - `"normal"`
+ - `"warn"`
+ - `"error"`
+
+ #### disconnect
+
+ The `disconnect` event is fired when the connection has been
+ terminated. The `detail` property is an `Object` the optionally
+ contains the property `reason`. `reason` is a `DOMString` specifying
+ the reason in the event of an unexpected termination. `reason` will be
+ omitted for a clean termination.
+
+ #### credentialsrequired
+
+ The `credentialsrequired` event is fired when the server requests more
+ credentials than were specified to [`RFB()`](#rfb-1). The `detail`
+ property is an `Object` containing the property `types` which is an
+ `Array` of `DOMString` listing the credentials that are required.
+
+ #### clipboard
+
+ The `clipboard` event is fired when the server has sent clipboard data.
+ The `detail` property is an `Object` containing the property `text`
+ which is a `DOMString` with the clipboard data.
+
+ #### bell
+
+ The `bell` event is fired when the server has requested an audible
+ bell.
+
+ #### fbresize
+
+ The `fbresize` event is fired when the framebuffer has changed
+ dimensions. The `detail` property is an `Object` with the properties
+ `width` and `height` specifying the new dimensions.
+
+ #### desktopname
+
+ The `desktopname` event is fired when the name of the remote desktop
+ changes. The `detail` property is an `Object` with the property `name`
+ which is a `DOMString` specifying the new name.
+
+ #### capabilities
+
+ The `capabilities` event is fired whenever an entry is added or removed
+ from `RFB.capabilities`. The `detail` property is an `Object` with the
+ property `capabilities` containing the new value of `RFB.capabilities`.
+
+ #### RFB.disconnect()
+
+ The `RFB.disconnect()` method is used to disconnect from the currently
+ connected server.
+
+ ##### Syntax
+
+ RFB.disconnect( );
+
+ #### RFB.sendCredentials()
+
+ The `RFB.sendCredentials()` method is used to provide the missing
+ credentials after a `credentialsrequired` event has been fired.
+
+ ##### Syntax
+
+ RFB.sendCredentials( credentials );
+
+ ###### Parameters
+
+ **`credentials`**
+ - An `Object` specifying the credentials to provide to the server
+ when authenticating. See [`RFB()`](#rfb-1) for details.
+
+ #### RFB.sendKey()
+
+ The `RFB.sendKey()` method is used to send a key event to the server.
+
+ ##### Syntax
+
+ RFB.sendKey( keysym, code [, down] );
+
+ ###### Parameters
+
+ **`keysym`**
+ - A `long` specifying the RFB keysym to send. Can be `0` if a valid
+ **`code`** is specified.
+
+ **`code`**
+ - A `DOMString` specifying the physical key to send. Valid values are
+ those that can be specified to
+ [`KeyboardEvent.code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code).
+ If the physical key cannot be determined then `null` shall be
+ specified.
+
+ **`down`** *Optional*
+ - A `boolean` specifying if a press or a release event should be
+ sent. If omitted then both a press and release event are sent.
+
+ #### RFB.sendCtrlAltDel()
+
+ The `RFB.sendCtrlAltDel()` method is used to send the key sequence
+ *left Control*, *left Alt*, *Delete*. This is a convenience wrapper
+ around [`RFB.sendKey()`](#rfbsendkey).
+
+ ##### Syntax
+
+ RFB.sendCtrlAltDel( );
+
+ #### RFB.machineShutdown()
+
+ The `RFB.machineShutdown()` method is used to request to shut down the
+ remote machine. The capability `power` must be set for this method to
+ have any effect.
+
+ ##### Syntax
+
+ RFB.machineShutdown( );
+
+ #### RFB.machineReboot()
+
+ The `RFB.machineReboot()` method is used to request a clean reboot of
+ the remote machine. The capability `power` must be set for this method
+ to have any effect.
+
+ ##### Syntax
+
+ RFB.machineReboot( );
+
+ #### RFB.machineReset()
+
+ The `RFB.machineReset()` method is used to request a forced reset of
+ the remote machine. The capability `power` must be set for this method
+ to have any effect.
+
+ ##### Syntax
+
+ RFB.machineReset( );
+
+ #### RFB.clipboardPasteFrom()
+
+ The `RFB.clipboardPasteFrom()` method is used to send clipboard data
+ to the remote server.
+
+ ##### Syntax
+
+ RFB.clipboardPasteFrom( text );
+
+ ###### Parameters
+
+ **`text`**
+ - A `DOMString` specifying the clipboard data to send. Currently only
+ characters from ISO 8859-1 are supported.
+
+ #### RFB.autoscale()
+
+ The `RFB.autoscale()` method is used to automatically adjust
+ `RFB.viewportScale` to fit given dimensions.
+
+ ##### Syntax
+
+ RFB.autoscale( width, height );
+
+ ###### Parameters
+
+ **`width`**
+ - A `long` specifying the maximum width of the canvas in CSS pixels.
+
+ **`height`**
+ - A `long` specifying the maximum height of the canvas in CSS pixels.
+
+ #### RFB.requestDesktopSize()
+
+ The `RFB.requestDesktopSize()` method is used to request a change of
+ the framebuffer. The capability `resize` must be set for this method to
+ have any effect.
+
+ Note that this is merely a request and the server may deny it.
+ The [`fbresize`](#fbresize) event will be fired when the framebuffer
+ actually changes dimensions.
+
+ ##### Syntax
+
+ RFB.requestDesktopSize( width, height );
+
+ ###### Parameters
+
+ **`width`**
+ - A `long` specifying the new requested width in CSS pixels.
+
+ **`height`**
+ - A `long` specifying the new requested height in CSS pixels.
+
+ #### RFB.viewportChangeSize()
+
+ The `RFB.viewportChangeSize()` method is used to change the size of the
+ canvas rather than the underlying framebuffer.
+
+ This method has no effect if `RFB.clipViewport` is set to `false`.
+
+ ##### Syntax
+
+ RFB.viewportChangeSize( width, height );
+
+ ###### Parameters
+
+ **`width`**
+ - A `long` specifying the new width in CSS pixels.
+
+ **`height`**
+ - A `long` specifying the new height in CSS pixels.
<!--
noVNC example: lightweight example using minimal UI and features
Copyright (C) 2012 Joel Martin
- Copyright (C) 2013 Samuel Mannehed for Cendio AB
+ Copyright (C) 2017 Samuel Mannehed for Cendio AB
noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
import RFB from './core/rfb.js';
var rfb;
+ var doneInitialResize;
var resizeTimeout;
var desktopName;
rfb.requestDesktopSize(innerW, innerH - controlbarH);
}
}
- function FBUComplete(rfb, fbu) {
+ function initialResize() {
+ if (doneInitialResize) return;
UIresize();
- rfb.set_onFBUComplete(function() { });
+ doneInitialResize = true;
}
- function updateDesktopName(rfb, name) {
- desktopName = name;
+ function updateDesktopName(e) {
+ desktopName = e.detail.name;
}
- function passwordRequired(rfb, msg) {
- if (typeof msg === 'undefined') {
- msg = 'Password Required: ';
- }
+ function credentials(e) {
var html;
var form = document.createElement('form');
- form.style = 'margin-bottom: 0px';
- form.innerHTML = '<label></label>'
- form.innerHTML += '<input type=password size=10 id="password_input" class="noVNC_status">';
+ form.innerHTML = '<label></label>';
+ form.innerHTML += '<input type=password size=10 id="password_input">';
form.onsubmit = setPassword;
// bypass status() because it sets text content
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
document.getElementById('noVNC_status').innerHTML = '';
document.getElementById('noVNC_status').appendChild(form);
- document.getElementById('noVNC_status').querySelector('label').textContent = msg;
+ document.getElementById('noVNC_status').querySelector('label').textContent = 'Password Required: ';
}
function setPassword() {
- rfb.sendPassword(document.getElementById('password_input').value);
+ rfb.sendCredentials({ password: document.getElementById('password_input').value });
return false;
}
function sendCtrlAltDel() {
rfb.sendCtrlAltDel();
return false;
}
- function xvpShutdown() {
- rfb.xvpShutdown();
+ function machineShutdown() {
+ rfb.machineShutdown();
return false;
}
- function xvpReboot() {
- rfb.xvpReboot();
+ function machineReboot() {
+ rfb.machineReboot();
return false;
}
- function xvpReset() {
- rfb.xvpReset();
+ function machineReset() {
+ rfb.machineReset();
return false;
}
function status(text, level) {
default:
level = "warn";
}
- document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_" + level);
+ document.getElementById('noVNC_status_bar').className = "noVNC_status_" + level;
document.getElementById('noVNC_status').textContent = text;
}
- function updateState(rfb, state, oldstate) {
+ function updateState(e) {
var cad = document.getElementById('sendCtrlAltDelButton');
- switch (state) {
+ switch (e.detail.state) {
case 'connecting':
status("Connecting", "normal");
break;
case 'connected':
- if (rfb && rfb.get_encrypt()) {
+ doneInitialResize = false;
+ if (WebUtil.getConfigVar('encrypt',
+ (window.location.protocol === "https:"))) {
status("Connected (encrypted) to " +
desktopName, "normal");
} else {
status("Disconnected", "normal");
break;
default:
- status(state, "warn");
+ status(e.detail.state, "warn");
break;
}
- if (state === 'connected') {
+ if (e.detail.state === 'connected') {
cad.disabled = false;
} else {
cad.disabled = true;
- xvpInit(0);
+ updatePowerButtons();
}
}
- function disconnected(rfb, reason) {
- if (typeof(reason) !== 'undefined') {
- status(reason, "error");
+ function disconnect(e) {
+ if (typeof(e.detail.reason) !== 'undefined') {
+ status(e.detail.reason, "error");
}
}
- function notification(rfb, msg, level, options) {
- status(msg, level);
+ function notification(e) {
+ status(e.detail.message, e.detail.level);
}
window.onresize = function () {
}, 500);
};
- function xvpInit(ver) {
- var xvpbuttons;
- xvpbuttons = document.getElementById('noVNC_xvp_buttons');
- if (ver >= 1) {
+ function updatePowerButtons() {
+ var powerbuttons;
+ powerbuttons = document.getElementById('noVNC_power_buttons');
+ if (rfb.capabilities.power) {
- powerbuttons.style.display = 'inline';
+ xvpbuttons.className= "noVNC_shown";
} else {
- powerbuttons.style.display = 'none';
+ xvpbuttons.className = "noVNC_hidden";
}
}
- document.getElementById('sendCtrlAltDelButton').style.display = "inline";
document.getElementById('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
- document.getElementById('xvpShutdownButton').onclick = xvpShutdown;
- document.getElementById('xvpRebootButton').onclick = xvpReboot;
- document.getElementById('xvpResetButton').onclick = xvpReset;
+ document.getElementById('machineShutdownButton').onclick = machineShutdown;
+ document.getElementById('machineRebootButton').onclick = machineReboot;
+ document.getElementById('machineResetButton').onclick = machineReset;
WebUtil.init_logging(WebUtil.getConfigVar('logging', 'warn'));
document.title = WebUtil.getConfigVar('title', 'noVNC');
status('Must specify host and port in URL', 'error');
}
- try {
- rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
- 'encrypt': WebUtil.getConfigVar('encrypt',
- (window.location.protocol === "https:")),
- 'repeaterID': WebUtil.getConfigVar('repeaterID', ''),
- 'local_cursor': WebUtil.getConfigVar('cursor', true),
- 'shared': WebUtil.getConfigVar('shared', true),
- 'view_only': WebUtil.getConfigVar('view_only', false),
- 'onNotification': notification,
- 'onUpdateState': updateState,
- 'onDisconnected': disconnected,
- 'onXvpInit': xvpInit,
- 'onPasswordRequired': passwordRequired,
- 'onFBUComplete': FBUComplete,
- 'onDesktopName': updateDesktopName});
- } catch (exc) {
- status('Unable to create RFB client -- ' + exc, 'error');
- return; // don't continue trying to connect
+ var url;
+
+ if (WebUtil.getConfigVar('encrypt',
+ (window.location.protocol === "https:"))) {
+ url = 'wss';
+ } else {
+ url = 'ws';
+ }
+
+ url += '://' + host;
+ if(port) {
+ url += ':' + port;
}
+ url += '/' + path;
- rfb.connect(host, port, password, path);
+ rfb = new RFB(document.getElementById('noVNC_canvas'), url,
+ { repeaterID: WebUtil.getConfigVar('repeaterID', ''),
+ shared: WebUtil.getConfigVar('shared', true),
+ credentials: { password: password } });
+ rfb.viewOnly = WebUtil.getConfigVar('view_only', false);
+ rfb.addEventListener("notification", notification);
+ rfb.addEventListener("updatestate", updateState);
+ rfb.addEventListener("disconnect", disconnect);
+ rfb.addEventListener("capabilities", function () { updatePowerButtons(); initialResize(); });
+ rfb.addEventListener("credentialsrequired", credentials);
+ rfb.addEventListener("desktopname", updateDesktopName);
})();
</script>
</head>
-<body style="margin: 0px;">
- <div id="noVNC_container">
- <div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
- <table border=0 width="100%"><tr>
- <td><div id="noVNC_status" style="position: relative; height: auto;">
- Loading
- </div></td>
- <td width="1%"><div id="noVNC_buttons">
- <input type=button value="Send CtrlAltDel"
- id="sendCtrlAltDelButton">
- <span id="noVNC_power_buttons">
- <input type=button value="Shutdown"
- id="machineShutdownButton">
- <input type=button value="Reboot"
- id="machineRebootButton">
- <input type=button value="Reset"
- id="machineResetButton">
- </span>
- </div></td>
- </tr></table>
- </div>
- <canvas id="noVNC_canvas" width="640px" height="20px">
- Canvas not supported.
- </canvas>
- </div>
-
- </body>
+<body>
+ <div id="noVNC_status_bar">
+ <div id="noVNC_left_dummy_elem"></div>
+ <div id="noVNC_status">Loading</div>
+ <div id="noVNC_buttons">
+ <input type=button value="Send CtrlAltDel"
+ id="sendCtrlAltDelButton" class="noVNC_shown">
- <span id="noVNC_xvp_buttons" class="noVNC_hidden">
++ <span id="noVNC_power_buttons" class="noVNC_hidden">
+ <input type=button value="Shutdown"
- id="xvpShutdownButton">
++ id="machineShutdownButton">
+ <input type=button value="Reboot"
- id="xvpRebootButton">
++ id="machineRebootButton">
+ <input type=button value="Reset"
- id="xvpResetButton">
++ id="machineResetButton">
+ </span>
+ </div>
+ </div>
+ <canvas id="noVNC_canvas" width="0" height="0">
+ Canvas not supported.
+ </canvas>
+
+</body>
</html>