Merge branch 'api' of https://github.com/CendioOssman/noVNC
This commit is contained in:
commit
a201bfc5eb
|
@ -591,17 +591,17 @@ select:active {
|
|||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* XVP Shutdown/Reboot */
|
||||
:root:not(.noVNC_connected) #noVNC_xvp_button {
|
||||
/* Shutdown/Reboot */
|
||||
:root:not(.noVNC_connected) #noVNC_power_button {
|
||||
display: none;
|
||||
}
|
||||
#noVNC_xvp {
|
||||
#noVNC_power {
|
||||
}
|
||||
#noVNC_xvp_buttons {
|
||||
#noVNC_power_buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#noVNC_xvp input[type=button] {
|
||||
#noVNC_power input[type=button] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
252
app/ui.js
252
app/ui.js
|
@ -13,7 +13,7 @@
|
|||
|
||||
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";
|
||||
|
@ -91,7 +91,7 @@ var UI = {
|
|||
UI.addControlbarHandlers();
|
||||
UI.addTouchSpecificHandlers();
|
||||
UI.addExtraKeysHandlers();
|
||||
UI.addXvpHandlers();
|
||||
UI.addMachineHandlers();
|
||||
UI.addConnectionControlHandlers();
|
||||
UI.addClipboardHandlers();
|
||||
UI.addSettingsHandlers();
|
||||
|
@ -167,7 +167,6 @@ var UI = {
|
|||
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);
|
||||
|
@ -200,28 +199,6 @@ var UI = {
|
|||
}
|
||||
},
|
||||
|
||||
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
|
||||
* ==============
|
||||
|
@ -278,8 +255,8 @@ var UI = {
|
|||
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);
|
||||
|
@ -330,15 +307,15 @@ var UI = {
|
|||
.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() {
|
||||
|
@ -377,8 +354,6 @@ var UI = {
|
|||
.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);
|
||||
|
@ -413,7 +388,7 @@ var UI = {
|
|||
* VISUAL
|
||||
* ------v------*/
|
||||
|
||||
updateState: function(rfb, state, oldstate) {
|
||||
updateState: function(event) {
|
||||
var msg;
|
||||
|
||||
document.documentElement.classList.remove("noVNC_connecting");
|
||||
|
@ -421,7 +396,7 @@ var UI = {
|
|||
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");
|
||||
|
@ -429,8 +404,9 @@ var UI = {
|
|||
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;
|
||||
|
@ -462,12 +438,6 @@ var UI = {
|
|||
|
||||
UI.enableDisableViewClip();
|
||||
|
||||
if (cursorURIsSupported() && !isTouchDevice) {
|
||||
UI.enableSetting('cursor');
|
||||
} else {
|
||||
UI.disableSetting('cursor');
|
||||
}
|
||||
|
||||
if (UI.connected) {
|
||||
UI.disableSetting('encrypt');
|
||||
UI.disableSetting('shared');
|
||||
|
@ -487,12 +457,12 @@ var UI = {
|
|||
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')
|
||||
|
@ -562,8 +532,8 @@ var UI = {
|
|||
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) {
|
||||
|
@ -866,7 +836,7 @@ var UI = {
|
|||
|
||||
closeAllPanels: function() {
|
||||
UI.closeSettingsPanel();
|
||||
UI.closeXvpPanel();
|
||||
UI.closePowerPanel();
|
||||
UI.closeClipboardPanel();
|
||||
UI.closeExtraKeys();
|
||||
},
|
||||
|
@ -883,12 +853,6 @@ var UI = {
|
|||
|
||||
// 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');
|
||||
|
@ -924,50 +888,52 @@ var UI = {
|
|||
/* ------^-------
|
||||
* /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------*/
|
||||
|
@ -998,9 +964,9 @@ var UI = {
|
|||
}
|
||||
},
|
||||
|
||||
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");
|
||||
},
|
||||
|
||||
|
@ -1053,19 +1019,34 @@ var UI = {
|
|||
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() {
|
||||
|
@ -1075,9 +1056,6 @@ var UI = {
|
|||
// 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
|
||||
},
|
||||
|
||||
|
@ -1092,9 +1070,9 @@ var UI = {
|
|||
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");
|
||||
|
@ -1125,8 +1103,8 @@ var UI = {
|
|||
* PASSWORD
|
||||
* ------v------*/
|
||||
|
||||
passwordRequired: function(rfb, msg) {
|
||||
|
||||
credentials: function(e) {
|
||||
// FIXME: handle more types
|
||||
document.getElementById('noVNC_password_dlg')
|
||||
.classList.add('noVNC_open');
|
||||
|
||||
|
@ -1134,24 +1112,23 @@ var UI = {
|
|||
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();
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
|
@ -1214,11 +1191,10 @@ var UI = {
|
|||
|
||||
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();
|
||||
|
@ -1250,19 +1226,17 @@ var UI = {
|
|||
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();
|
||||
},
|
||||
|
||||
|
@ -1275,13 +1249,14 @@ var UI = {
|
|||
// 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;
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
|
@ -1300,12 +1275,11 @@ var UI = {
|
|||
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) {
|
||||
|
@ -1314,7 +1288,7 @@ var UI = {
|
|||
}
|
||||
|
||||
if (cur_clip !== new_clip) {
|
||||
display.set_viewport(new_clip);
|
||||
UI.rfb.clipViewport = new_clip;
|
||||
}
|
||||
|
||||
var size = UI.screenSize();
|
||||
|
@ -1322,7 +1296,7 @@ var UI = {
|
|||
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();
|
||||
}
|
||||
|
||||
|
@ -1335,7 +1309,7 @@ var UI = {
|
|||
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 {
|
||||
|
@ -1352,7 +1326,7 @@ var UI = {
|
|||
toggleViewDrag: function() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
var drag = UI.rfb.get_viewportDrag();
|
||||
var drag = UI.rfb.dragViewport;
|
||||
UI.setViewDrag(!drag);
|
||||
},
|
||||
|
||||
|
@ -1360,7 +1334,7 @@ var UI = {
|
|||
setViewDrag: function(drag) {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.set_viewportDrag(drag);
|
||||
UI.rfb.dragViewport = drag;
|
||||
|
||||
UI.updateViewDrag();
|
||||
},
|
||||
|
@ -1372,22 +1346,21 @@ var UI = {
|
|||
|
||||
// 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");
|
||||
|
@ -1655,9 +1628,9 @@ var UI = {
|
|||
* ------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];
|
||||
|
@ -1672,21 +1645,16 @@ var UI = {
|
|||
}
|
||||
},
|
||||
|
||||
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();
|
||||
|
@ -1704,13 +1672,13 @@ var UI = {
|
|||
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
|
||||
|
|
194
core/display.js
194
core/display.js
|
@ -10,12 +10,10 @@
|
|||
/*jslint browser: true, white: false */
|
||||
/*global Util, Base64, changeCursor */
|
||||
|
||||
import { browserSupportsCursorURIs as cursorURIsSupported } from './util/browsers.js';
|
||||
import { set_defaults, make_properties } from './util/properties.js';
|
||||
import * as Log from './util/logging.js';
|
||||
import Base64 from "./base64.js";
|
||||
|
||||
export default function Display(defaults) {
|
||||
export default function Display(target) {
|
||||
this._drawCtx = null;
|
||||
this._c_forceCanvas = false;
|
||||
|
||||
|
@ -32,16 +30,11 @@ export default function Display(defaults) {
|
|||
this._tile_x = 0;
|
||||
this._tile_y = 0;
|
||||
|
||||
set_defaults(this, defaults, {
|
||||
'scale': 1.0,
|
||||
'viewport': false,
|
||||
'render_mode': '',
|
||||
"onFlush": function () {},
|
||||
});
|
||||
|
||||
Log.Debug(">> Display.constructor");
|
||||
|
||||
// The visible canvas
|
||||
this._target = target;
|
||||
|
||||
if (!this._target) {
|
||||
throw new Error("Target must be set");
|
||||
}
|
||||
|
@ -72,23 +65,10 @@ export default function Display(defaults) {
|
|||
this.clear();
|
||||
|
||||
// Check canvas features
|
||||
if ('createImageData' in this._drawCtx) {
|
||||
this._render_mode = 'canvas rendering';
|
||||
} else {
|
||||
if (!('createImageData' in this._drawCtx)) {
|
||||
throw new Error("Canvas does not support createImageData");
|
||||
}
|
||||
|
||||
if (this._prefer_js === null) {
|
||||
Log.Info("Prefering javascript operations");
|
||||
this._prefer_js = true;
|
||||
}
|
||||
|
||||
// Determine browser support for setting the cursor via data URI scheme
|
||||
if (this._cursor_uri || this._cursor_uri === null ||
|
||||
this._cursor_uri === undefined) {
|
||||
this._cursor_uri = cursorURIsSupported();
|
||||
}
|
||||
|
||||
Log.Debug("<< Display.constructor");
|
||||
};
|
||||
|
||||
|
@ -101,13 +81,50 @@ try {
|
|||
}
|
||||
|
||||
Display.prototype = {
|
||||
// Public methods
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
_scale: 1.0,
|
||||
get scale() { return this._scale; },
|
||||
set scale(scale) {
|
||||
this._rescale(scale);
|
||||
},
|
||||
|
||||
_clipViewport: false,
|
||||
get clipViewport() { return this._clipViewport; },
|
||||
set clipViewport(viewport) {
|
||||
this._clipViewport = viewport;
|
||||
// May need to readjust the viewport dimensions
|
||||
var vp = this._viewportLoc;
|
||||
this.viewportChangeSize(vp.w, vp.h);
|
||||
this.viewportChangePos(0, 0);
|
||||
},
|
||||
|
||||
get width() {
|
||||
return this._fb_width;
|
||||
},
|
||||
get height() {
|
||||
return this._fb_height;
|
||||
},
|
||||
|
||||
get isClipped() {
|
||||
var vp = this._viewportLoc;
|
||||
return this._fb_width > vp.w || this._fb_height > vp.h;
|
||||
},
|
||||
|
||||
logo: null,
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onflush: function () {}, // A flush request has finished
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
viewportChangePos: function (deltaX, deltaY) {
|
||||
var vp = this._viewportLoc;
|
||||
deltaX = Math.floor(deltaX);
|
||||
deltaY = Math.floor(deltaY);
|
||||
|
||||
if (!this._viewport) {
|
||||
if (!this._clipViewport) {
|
||||
deltaX = -vp.w; // clamped later of out of bounds
|
||||
deltaY = -vp.h;
|
||||
}
|
||||
|
@ -146,7 +163,7 @@ Display.prototype = {
|
|||
|
||||
viewportChangeSize: function(width, height) {
|
||||
|
||||
if (!this._viewport ||
|
||||
if (!this._clipViewport ||
|
||||
typeof(width) === "undefined" ||
|
||||
typeof(height) === "undefined") {
|
||||
|
||||
|
@ -307,7 +324,7 @@ Display.prototype = {
|
|||
|
||||
flush: function() {
|
||||
if (this._renderQ.length === 0) {
|
||||
this._onFlush();
|
||||
this.onflush();
|
||||
} else {
|
||||
this._flushing = true;
|
||||
}
|
||||
|
@ -382,56 +399,45 @@ Display.prototype = {
|
|||
this._tile = this._drawCtx.createImageData(width, height);
|
||||
}
|
||||
|
||||
if (this._prefer_js) {
|
||||
var red = color[2];
|
||||
var green = color[1];
|
||||
var blue = color[0];
|
||||
var red = color[2];
|
||||
var green = color[1];
|
||||
var blue = color[0];
|
||||
|
||||
var data = this._tile.data;
|
||||
for (var i = 0; i < width * height * 4; i += 4) {
|
||||
data[i] = red;
|
||||
data[i + 1] = green;
|
||||
data[i + 2] = blue;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
this.fillRect(x, y, width, height, color, true);
|
||||
var data = this._tile.data;
|
||||
for (var i = 0; i < width * height * 4; i += 4) {
|
||||
data[i] = red;
|
||||
data[i + 1] = green;
|
||||
data[i + 2] = blue;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
},
|
||||
|
||||
// update sub-rectangle of the current tile
|
||||
subTile: function (x, y, w, h, color) {
|
||||
if (this._prefer_js) {
|
||||
var red = color[2];
|
||||
var green = color[1];
|
||||
var blue = color[0];
|
||||
var xend = x + w;
|
||||
var yend = y + h;
|
||||
var red = color[2];
|
||||
var green = color[1];
|
||||
var blue = color[0];
|
||||
var xend = x + w;
|
||||
var yend = y + h;
|
||||
|
||||
var data = this._tile.data;
|
||||
var width = this._tile.width;
|
||||
for (var j = y; j < yend; j++) {
|
||||
for (var i = x; i < xend; i++) {
|
||||
var p = (i + (j * width)) * 4;
|
||||
data[p] = red;
|
||||
data[p + 1] = green;
|
||||
data[p + 2] = blue;
|
||||
data[p + 3] = 255;
|
||||
}
|
||||
var data = this._tile.data;
|
||||
var width = this._tile.width;
|
||||
for (var j = y; j < yend; j++) {
|
||||
for (var i = x; i < xend; i++) {
|
||||
var p = (i + (j * width)) * 4;
|
||||
data[p] = red;
|
||||
data[p + 1] = green;
|
||||
data[p + 2] = blue;
|
||||
data[p + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color, true);
|
||||
}
|
||||
},
|
||||
|
||||
// draw the current tile to the screen
|
||||
finishTile: function () {
|
||||
if (this._prefer_js) {
|
||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
||||
this._damage(this._tile_x, this._tile_y,
|
||||
this._tile.width, this._tile.height);
|
||||
}
|
||||
// else: No-op -- already done by setSubTile
|
||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
||||
this._damage(this._tile_x, this._tile_y,
|
||||
this._tile.width, this._tile.height);
|
||||
},
|
||||
|
||||
blitImage: function (x, y, width, height, arr, offset, from_queue) {
|
||||
|
@ -500,11 +506,6 @@ Display.prototype = {
|
|||
},
|
||||
|
||||
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
||||
if (this._cursor_uri === false) {
|
||||
Log.Warn("changeCursor called but no cursor data URI support");
|
||||
return;
|
||||
}
|
||||
|
||||
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
|
||||
},
|
||||
|
||||
|
@ -516,32 +517,7 @@ Display.prototype = {
|
|||
this._target.style.cursor = "none";
|
||||
},
|
||||
|
||||
clippingDisplay: function () {
|
||||
var vp = this._viewportLoc;
|
||||
return this._fb_width > vp.w || this._fb_height > vp.h;
|
||||
},
|
||||
|
||||
// Overridden getters/setters
|
||||
set_scale: function (scale) {
|
||||
this._rescale(scale);
|
||||
},
|
||||
|
||||
set_viewport: function (viewport) {
|
||||
this._viewport = viewport;
|
||||
// May need to readjust the viewport dimensions
|
||||
var vp = this._viewportLoc;
|
||||
this.viewportChangeSize(vp.w, vp.h);
|
||||
this.viewportChangePos(0, 0);
|
||||
},
|
||||
|
||||
get_width: function () {
|
||||
return this._fb_width;
|
||||
},
|
||||
get_height: function () {
|
||||
return this._fb_height;
|
||||
},
|
||||
|
||||
autoscale: function (containerWidth, containerHeight, downscaleOnly) {
|
||||
autoscale: function (containerWidth, containerHeight) {
|
||||
var vp = this._viewportLoc;
|
||||
var targetAspectRatio = containerWidth / containerHeight;
|
||||
var fbAspectRatio = vp.w / vp.h;
|
||||
|
@ -553,14 +529,11 @@ Display.prototype = {
|
|||
scaleRatio = containerHeight / vp.h;
|
||||
}
|
||||
|
||||
if (scaleRatio > 1.0 && downscaleOnly) {
|
||||
scaleRatio = 1.0;
|
||||
}
|
||||
|
||||
this._rescale(scaleRatio);
|
||||
},
|
||||
|
||||
// Private Methods
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_rescale: function (factor) {
|
||||
this._scale = factor;
|
||||
var vp = this._viewportLoc;
|
||||
|
@ -685,28 +658,11 @@ Display.prototype = {
|
|||
|
||||
if (this._renderQ.length === 0 && this._flushing) {
|
||||
this._flushing = false;
|
||||
this._onFlush();
|
||||
this.onflush();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
make_properties(Display, [
|
||||
['target', 'wo', 'dom'], // Canvas element for rendering
|
||||
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
|
||||
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data}
|
||||
['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0
|
||||
['viewport', 'rw', 'bool'], // Use viewport clipping
|
||||
['width', 'ro', 'int'], // Display area width
|
||||
['height', 'ro', 'int'], // Display area height
|
||||
|
||||
['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only)
|
||||
|
||||
['prefer_js', 'rw', 'str'], // Prefer Javascript over canvas methods
|
||||
['cursor_uri', 'rw', 'raw'], // Can we render cursor using data URI
|
||||
|
||||
['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished
|
||||
]);
|
||||
|
||||
// Class Methods
|
||||
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
|
||||
if ((w === 0) || (h === 0)) {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
import * as Log from '../util/logging.js';
|
||||
import { stopEvent } from '../util/events.js';
|
||||
import { set_defaults, make_properties } from '../util/properties.js';
|
||||
import * as KeyboardUtil from "./util.js";
|
||||
import KeyTable from "./keysym.js";
|
||||
|
||||
|
@ -18,15 +17,13 @@ import KeyTable from "./keysym.js";
|
|||
// Keyboard event handler
|
||||
//
|
||||
|
||||
export default function Keyboard(defaults) {
|
||||
export default function Keyboard(target) {
|
||||
this._target = target || null;
|
||||
|
||||
this._keyDownList = {}; // List of depressed keys
|
||||
// (even if they are happy)
|
||||
this._pendingKey = null; // Key waiting for keypress
|
||||
|
||||
set_defaults(this, defaults, {
|
||||
'target': null,
|
||||
});
|
||||
|
||||
// keep these here so we can refer to them later
|
||||
this._eventHandlers = {
|
||||
'keyup': this._handleKeyUp.bind(this),
|
||||
|
@ -56,14 +53,14 @@ function isEdge() {
|
|||
}
|
||||
|
||||
Keyboard.prototype = {
|
||||
// private methods
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onkeyevent: function () {}, // Handler for key press/release
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_sendKeyEvent: function (keysym, code, down) {
|
||||
if (!this._onKeyEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Debug("onKeyEvent " + (down ? "down" : "up") +
|
||||
Log.Debug("onkeyevent " + (down ? "down" : "up") +
|
||||
", keysym: " + keysym, ", code: " + code);
|
||||
|
||||
// Windows sends CtrlLeft+AltRight when you press
|
||||
|
@ -77,19 +74,19 @@ Keyboard.prototype = {
|
|||
('ControlLeft' in this._keyDownList) &&
|
||||
('AltRight' in this._keyDownList)) {
|
||||
fakeAltGraph = true;
|
||||
this._onKeyEvent(this._keyDownList['AltRight'],
|
||||
this.onkeyevent(this._keyDownList['AltRight'],
|
||||
'AltRight', false);
|
||||
this._onKeyEvent(this._keyDownList['ControlLeft'],
|
||||
this.onkeyevent(this._keyDownList['ControlLeft'],
|
||||
'ControlLeft', false);
|
||||
}
|
||||
}
|
||||
|
||||
this._onKeyEvent(keysym, code, down);
|
||||
this.onkeyevent(keysym, code, down);
|
||||
|
||||
if (fakeAltGraph) {
|
||||
this._onKeyEvent(this._keyDownList['ControlLeft'],
|
||||
this.onkeyevent(this._keyDownList['ControlLeft'],
|
||||
'ControlLeft', true);
|
||||
this._onKeyEvent(this._keyDownList['AltRight'],
|
||||
this.onkeyevent(this._keyDownList['AltRight'],
|
||||
'AltRight', true);
|
||||
}
|
||||
},
|
||||
|
@ -305,7 +302,7 @@ Keyboard.prototype = {
|
|||
Log.Debug("<< Keyboard.allKeysUp");
|
||||
},
|
||||
|
||||
// Public methods
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
grab: function () {
|
||||
//Log.Debug(">> Keyboard.grab");
|
||||
|
@ -336,9 +333,3 @@ Keyboard.prototype = {
|
|||
//Log.Debug(">> Keyboard.ungrab");
|
||||
},
|
||||
};
|
||||
|
||||
make_properties(Keyboard, [
|
||||
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
|
||||
['onKeyEvent', 'rw', 'func'] // Handler for key press/release
|
||||
]);
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
import * as Log from '../util/logging.js';
|
||||
import { isTouchDevice } from '../util/browsers.js';
|
||||
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
||||
import { set_defaults, make_properties } from '../util/properties.js';
|
||||
|
||||
var WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
|
||||
var WHEEL_STEP_TIMEOUT = 50; // ms
|
||||
var WHEEL_LINE_HEIGHT = 19;
|
||||
|
||||
export default function Mouse(defaults) {
|
||||
export default function Mouse(target) {
|
||||
this._target = target || document;
|
||||
|
||||
this._doubleClickTimer = null;
|
||||
this._lastTouchPos = null;
|
||||
|
@ -28,12 +28,6 @@ export default function Mouse(defaults) {
|
|||
this._accumulatedWheelDeltaX = 0;
|
||||
this._accumulatedWheelDeltaY = 0;
|
||||
|
||||
// Configuration attributes
|
||||
set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'touchButton': 1
|
||||
});
|
||||
|
||||
this._eventHandlers = {
|
||||
'mousedown': this._handleMouseDown.bind(this),
|
||||
'mouseup': this._handleMouseUp.bind(this),
|
||||
|
@ -44,7 +38,16 @@ export default function Mouse(defaults) {
|
|||
};
|
||||
|
||||
Mouse.prototype = {
|
||||
// private methods
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
touchButton: 1, // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onmousebutton: function () {}, // Handler for mouse button click/release
|
||||
onmousemove: function () {}, // Handler for mouse movement
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_resetDoubleClickTimer: function () {
|
||||
this._doubleClickTimer = null;
|
||||
|
@ -83,7 +86,7 @@ Mouse.prototype = {
|
|||
}
|
||||
this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
|
||||
}
|
||||
bmask = this._touchButton;
|
||||
bmask = this.touchButton;
|
||||
// If bmask is set
|
||||
} else if (e.which) {
|
||||
/* everything except IE */
|
||||
|
@ -95,11 +98,10 @@ Mouse.prototype = {
|
|||
(e.button & 0x4) / 2; // Middle
|
||||
}
|
||||
|
||||
if (this._onMouseButton) {
|
||||
Log.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
this._onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Log.Debug("onmousebutton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
this.onmousebutton(pos.x, pos.y, down, bmask);
|
||||
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
|
@ -122,11 +124,11 @@ Mouse.prototype = {
|
|||
_generateWheelStepX: function () {
|
||||
|
||||
if (this._accumulatedWheelDeltaX < 0) {
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 1, 1 << 5);
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 0, 1 << 5);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 5);
|
||||
} else if (this._accumulatedWheelDeltaX > 0) {
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 1, 1 << 6);
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 0, 1 << 6);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 6);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 6);
|
||||
}
|
||||
|
||||
this._accumulatedWheelDeltaX = 0;
|
||||
|
@ -135,11 +137,11 @@ Mouse.prototype = {
|
|||
_generateWheelStepY: function () {
|
||||
|
||||
if (this._accumulatedWheelDeltaY < 0) {
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 1, 1 << 3);
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 0, 1 << 3);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 3);
|
||||
} else if (this._accumulatedWheelDeltaY > 0) {
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 1, 1 << 4);
|
||||
this._onMouseButton(this._pos.x, this._pos.y, 0, 1 << 4);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 4);
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 4);
|
||||
}
|
||||
|
||||
this._accumulatedWheelDeltaY = 0;
|
||||
|
@ -153,8 +155,6 @@ Mouse.prototype = {
|
|||
},
|
||||
|
||||
_handleMouseWheel: function (e) {
|
||||
if (!this._onMouseButton) { return; }
|
||||
|
||||
this._resetWheelStepTimers();
|
||||
|
||||
this._updateMousePosition(e);
|
||||
|
@ -199,9 +199,7 @@ Mouse.prototype = {
|
|||
|
||||
_handleMouseMove: function (e) {
|
||||
this._updateMousePosition(e);
|
||||
if (this._onMouseMove) {
|
||||
this._onMouseMove(this._pos.x, this._pos.y);
|
||||
}
|
||||
this.onmousemove(this._pos.x, this._pos.y);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
|
@ -240,7 +238,8 @@ Mouse.prototype = {
|
|||
this._pos = {x:x, y:y};
|
||||
},
|
||||
|
||||
// Public methods
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
grab: function () {
|
||||
var c = this._target;
|
||||
|
||||
|
@ -282,11 +281,3 @@ Mouse.prototype = {
|
|||
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||
}
|
||||
};
|
||||
|
||||
make_properties(Mouse, [
|
||||
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
||||
|
||||
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
|
||||
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
||||
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
]);
|
||||
|
|
493
core/rfb.js
493
core/rfb.js
|
@ -13,7 +13,8 @@
|
|||
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";
|
||||
|
@ -24,50 +25,78 @@ import KeyTable from "./input/keysym.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._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._fb_name = "";
|
||||
|
||||
this._capabilities = { power: false, resize: false };
|
||||
|
||||
this._supportsFence = false;
|
||||
|
||||
this._supportsContinuousUpdates = false;
|
||||
this._enabledContinuousUpdates = false;
|
||||
|
||||
// Frame buffer update state
|
||||
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
|
||||
|
||||
// Timers
|
||||
this._disconnTimer = null; // disconnection timer
|
||||
|
||||
// Decoder states and stats
|
||||
this._encHandlers = {};
|
||||
this._encStats = {};
|
||||
|
||||
this._FBU = {
|
||||
rects: 0,
|
||||
subrects: 0, // RRE
|
||||
subrects: 0, // RRE and HEXTILE
|
||||
lines: 0, // RAW
|
||||
tiles: 0, // HEXTILE
|
||||
bytes: 0,
|
||||
|
@ -78,12 +107,11 @@ export default function RFB(defaults) {
|
|||
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)
|
||||
|
@ -103,10 +131,6 @@ export default function RFB(defaults) {
|
|||
pixels: 0
|
||||
};
|
||||
|
||||
this._supportsSetDesktopSize = false;
|
||||
this._screen_id = 0;
|
||||
this._screen_flags = 0;
|
||||
|
||||
// Mouse state
|
||||
this._mouse_buttonMask = 0;
|
||||
this._mouse_arr = [];
|
||||
|
@ -114,37 +138,6 @@ export default function RFB(defaults) {
|
|||
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),
|
||||
|
@ -174,19 +167,20 @@ export default function RFB(defaults) {
|
|||
// 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));
|
||||
|
@ -236,33 +230,51 @@ export default function RFB(defaults) {
|
|||
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,
|
||||
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
this._rfb_init_state = '';
|
||||
this._updateConnectionState('connecting');
|
||||
return true;
|
||||
},
|
||||
|
||||
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');
|
||||
|
@ -270,13 +282,13 @@ RFB.prototype = {
|
|||
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);
|
||||
|
@ -285,38 +297,29 @@ RFB.prototype = {
|
|||
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;
|
||||
machineShutdown: function () {
|
||||
this._xvpOp(1, 2);
|
||||
},
|
||||
|
||||
xvpShutdown: function () {
|
||||
return this.xvpOp(1, 2);
|
||||
machineReboot: function () {
|
||||
this._xvpOp(1, 3);
|
||||
},
|
||||
|
||||
xvpReboot: function () {
|
||||
return 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];
|
||||
|
@ -330,63 +333,55 @@ RFB.prototype = {
|
|||
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);
|
||||
|
@ -412,29 +407,6 @@ RFB.prototype = {
|
|||
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;
|
||||
|
||||
|
@ -454,11 +426,11 @@ RFB.prototype = {
|
|||
},
|
||||
|
||||
_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();
|
||||
}
|
||||
|
@ -540,7 +512,8 @@ RFB.prototype = {
|
|||
// 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);
|
||||
|
@ -556,14 +529,16 @@ RFB.prototype = {
|
|||
|
||||
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':
|
||||
|
@ -576,7 +551,7 @@ RFB.prototype = {
|
|||
this._disconnTimer = setTimeout(function () {
|
||||
this._rfb_disconnect_reason = _("Disconnect timeout");
|
||||
this._updateConnectionState('disconnected');
|
||||
}.bind(this), this._disconnectTimeout * 1000);
|
||||
}.bind(this), DISCONNECT_TIMEOUT * 1000);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -618,11 +593,10 @@ RFB.prototype = {
|
|||
* 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':
|
||||
|
@ -634,11 +608,16 @@ RFB.prototype = {
|
|||
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 () {
|
||||
|
@ -681,7 +660,7 @@ RFB.prototype = {
|
|||
this._mouse_buttonMask &= ~bmask;
|
||||
}
|
||||
|
||||
if (this._viewportDrag) {
|
||||
if (this.dragViewport) {
|
||||
if (down && !this._viewportDragging) {
|
||||
this._viewportDragging = true;
|
||||
this._viewportDragPos = {'x': x, 'y': y};
|
||||
|
@ -693,14 +672,14 @@ RFB.prototype = {
|
|||
|
||||
// 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);
|
||||
|
@ -727,7 +706,7 @@ RFB.prototype = {
|
|||
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);
|
||||
|
@ -768,7 +747,7 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
if (is_repeater) {
|
||||
var repeaterID = this._repeaterID;
|
||||
var repeaterID = "ID:" + this._repeaterID;
|
||||
while (repeaterID.length < 250) {
|
||||
repeaterID += "\0";
|
||||
}
|
||||
|
@ -845,21 +824,20 @@ RFB.prototype = {
|
|||
|
||||
// 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();
|
||||
},
|
||||
|
@ -867,14 +845,16 @@ RFB.prototype = {
|
|||
_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;
|
||||
|
@ -1105,12 +1085,14 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -1160,7 +1142,8 @@ RFB.prototype = {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -1219,9 +1202,11 @@ RFB.prototype = {
|
|||
|
||||
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;
|
||||
},
|
||||
|
@ -1283,7 +1268,7 @@ RFB.prototype = {
|
|||
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",
|
||||
|
@ -1317,7 +1302,8 @@ RFB.prototype = {
|
|||
|
||||
case 2: // Bell
|
||||
Log.Debug("Bell");
|
||||
this._onBell(this);
|
||||
var event = new CustomEvent("bell", { detail: {} });
|
||||
this.dispatchEvent(event);
|
||||
return true;
|
||||
|
||||
case 3: // ServerCutText
|
||||
|
@ -1398,12 +1384,6 @@ RFB.prototype = {
|
|||
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 " +
|
||||
|
@ -1458,8 +1438,6 @@ RFB.prototype = {
|
|||
|
||||
this._display.flip();
|
||||
|
||||
this._onFBUComplete(this);
|
||||
|
||||
return true; // We finished this FBU
|
||||
},
|
||||
|
||||
|
@ -1477,77 +1455,24 @@ RFB.prototype = {
|
|||
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();
|
||||
}
|
||||
},
|
||||
|
||||
_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);
|
||||
},
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 = {
|
||||
|
@ -1830,7 +1755,21 @@ 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) {
|
||||
|
@ -2361,6 +2300,8 @@ RFB.encodingHandlers = {
|
|||
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);
|
||||
|
|
|
@ -43,11 +43,3 @@ export function browserSupportsCursorURIs () {
|
|||
|
||||
return _cursor_uris_supported;
|
||||
};
|
||||
|
||||
export function _forceCursorURIs(enabled) {
|
||||
if (enabled === undefined || enabled) {
|
||||
_cursor_uris_supported = true;
|
||||
} else {
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright 2017 Pierre Ossman for Cendio AB
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
var EventTargetMixin = {
|
||||
_listeners: null,
|
||||
|
||||
addEventListener: function(type, callback) {
|
||||
if (!this._listeners) {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
if (!this._listeners.has(type)) {
|
||||
this._listeners.set(type, new Set());
|
||||
}
|
||||
this._listeners.get(type).add(callback);
|
||||
},
|
||||
|
||||
removeEventListener: function(type, callback) {
|
||||
if (!this._listeners || !this._listeners.has(type)) {
|
||||
return;
|
||||
}
|
||||
this._listeners.get(type).delete(callback);
|
||||
},
|
||||
|
||||
dispatchEvent: function(event) {
|
||||
if (!this._listeners || !this._listeners.has(event.type)) {
|
||||
return true;
|
||||
}
|
||||
this._listeners.get(event.type).forEach(function (callback) {
|
||||
callback.call(this, event);
|
||||
}, this);
|
||||
return !event.defaultPrevented;
|
||||
},
|
||||
};
|
||||
|
||||
export default EventTargetMixin;
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright 2017 Pierre Ossman for noVNC
|
||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
/* Polyfills to provide new APIs in old browsers */
|
||||
|
||||
/* Object.assign() (taken from MDN) */
|
||||
if (typeof Object.assign != 'function') {
|
||||
// Must be writable: true, enumerable: false, configurable: true
|
||||
Object.defineProperty(Object, "assign", {
|
||||
value: function assign(target, varArgs) { // .length of function is 2
|
||||
'use strict';
|
||||
if (target == null) { // TypeError if undefined or null
|
||||
throw new TypeError('Cannot convert undefined or null to object');
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) { // Skip over if undefined or null
|
||||
for (var nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
/* CustomEvent constructor (taken from MDN) */
|
||||
(function () {
|
||||
function CustomEvent ( event, params ) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||
var evt = document.createEvent( 'CustomEvent' );
|
||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
if (typeof window.CustomEvent !== "function") {
|
||||
window.CustomEvent = CustomEvent;
|
||||
}
|
||||
})();
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Getter/Setter Creation Utilities
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
function make_property (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
return this['_' + name];
|
||||
}
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
export function make_properties (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
};
|
||||
|
||||
export function set_defaults (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
if (!setter) {
|
||||
Log.Warn('Invalid property ' + keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys[i] in conf) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
# 1. Internal Modules
|
||||
|
||||
The noVNC client is composed of several internal modules that handle
|
||||
rendering, input, networking, etc. Each of the modules is designed to
|
||||
be cross-browser and independent from each other.
|
||||
|
||||
Note however that the API of these modules is not guaranteed to be
|
||||
stable, and this documentation is not maintained as well as the
|
||||
official external API.
|
||||
|
||||
|
||||
## 1.1 Module List
|
||||
|
||||
* __Mouse__ (core/input/mouse.js): Mouse input event handler with
|
||||
limited touch support.
|
||||
|
||||
* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
|
||||
non-US keyboard support. Translates keyDown and keyUp events to X11
|
||||
keysym values.
|
||||
|
||||
* __Display__ (core/display.js): Efficient 2D rendering abstraction
|
||||
layered on the HTML5 canvas element.
|
||||
|
||||
* __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.
|
||||
|
||||
|
||||
## 1.2 Callbacks
|
||||
|
||||
For the Mouse, Keyboard and Display objects the callback functions are
|
||||
assigned to configuration attributes, just as for the RFB object. The
|
||||
WebSock module has a method named 'on' that takes two parameters: the
|
||||
callback event name, and the callback function.
|
||||
|
||||
## 2. Modules
|
||||
|
||||
## 2.1 Mouse Module
|
||||
|
||||
### 2.1.1 Configuration Attributes
|
||||
|
||||
| name | type | mode | default | description
|
||||
| ----------- | ---- | ---- | -------- | ------------
|
||||
| touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
|
||||
|
||||
### 2.1.2 Methods
|
||||
|
||||
| name | parameters | description
|
||||
| ------ | ---------- | ------------
|
||||
| grab | () | Begin capturing mouse events
|
||||
| ungrab | () | Stop capturing mouse events
|
||||
|
||||
### 2.1.2 Callbacks
|
||||
|
||||
| name | parameters | description
|
||||
| ------------- | ------------------- | ------------
|
||||
| onmousebutton | (x, y, down, bmask) | Handler for mouse button click/release
|
||||
| onmousemove | (x, y) | Handler for mouse movement
|
||||
|
||||
|
||||
## 2.2 Keyboard Module
|
||||
|
||||
### 2.2.1 Configuration Attributes
|
||||
|
||||
None
|
||||
|
||||
### 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
|
||||
| ------------ | ----- | ---- | ------- | ------------
|
||||
| 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
|
||||
| clipViewport | bool | RW | false | Use viewport clipping
|
||||
| width | int | RO | | Display area width
|
||||
| height | int | RO | | Display area height
|
||||
| isClipped | bool | RO | | Is the remote display is larger than the client display
|
||||
|
||||
### 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
|
||||
| autoscale | (containerWidth, containerHeight) | 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
|
561
docs/API.md
561
docs/API.md
|
@ -1,255 +1,424 @@
|
|||
# 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
|
||||
[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
|
||||
- Send Ctrl-Alt-Del key sequence.
|
||||
|
||||
| name | type | mode | default | description
|
||||
| ------- | ---- | ---- | -------- | ------------
|
||||
| target | DOM | WO | document | DOM element that captures keyboard input
|
||||
[`RFB.machineShutdown()`](#rfbmachineshutdown)
|
||||
- Request a shutdown of the remote machine.
|
||||
|
||||
### 2.2.2 Methods
|
||||
[`RFB.machineReboot()`](#rfbmachinereboot)
|
||||
- Request a reboot of the remote machine.
|
||||
|
||||
| name | parameters | description
|
||||
| ------ | ---------- | ------------
|
||||
| grab | () | Begin capturing keyboard events
|
||||
| ungrab | () | Stop capturing keyboard events
|
||||
[`RFB.machineReset()`](#rfbmachinereset)
|
||||
- Request a reset of the remote machine.
|
||||
|
||||
### 2.2.3 Callbacks
|
||||
[`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
|
||||
- Send clipboard contents to server.
|
||||
|
||||
| name | parameters | description
|
||||
| ---------- | -------------------- | ------------
|
||||
| onKeyPress | (keysym, code, down) | Handler for key press/release
|
||||
[`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.
|
||||
|
||||
## 2.3 Display Module
|
||||
[`RFB.viewportChangeSize()`](#rfbviewportChangeSize)
|
||||
- Change size of the viewport.
|
||||
|
||||
### 2.3.1 Configuration Attributes
|
||||
### Details
|
||||
|
||||
| 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
|
||||
#### RFB()
|
||||
|
||||
### 2.3.2 Methods
|
||||
The `RFB()` constructor returns a new `RFB` object and initiates a new
|
||||
connection to a specified VNC server.
|
||||
|
||||
| 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
|
||||
##### Syntax
|
||||
|
||||
### 2.3.3 Callbacks
|
||||
var rfb = new RFB( target, url [, options] );
|
||||
|
||||
| name | parameters | description
|
||||
| ------- | ---------- | ------------
|
||||
| onFlush | () | A display flush has been requested and we are now ready to resume FBU processing
|
||||
###### 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.
|
||||
|
||||
## 2.4 RFB Module
|
||||
**`url`**
|
||||
- A `DOMString` specifying the VNC server to connect to. This must be
|
||||
a valid WebSocket URL.
|
||||
|
||||
### 2.4.1 Configuration Attributes
|
||||
**`options`** *Optional*
|
||||
- An `Object` specifying extra details about how the connection
|
||||
should be made.
|
||||
|
||||
| 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
|
||||
Possible options:
|
||||
|
||||
### 2.4.2 Methods
|
||||
`shared`
|
||||
- A `boolean` indicating if the remote server should be shared or
|
||||
if any other connected clients should be disconnected. Enabled
|
||||
by default.
|
||||
|
||||
| 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.
|
||||
`credentials`
|
||||
- An `Object` specifying the credentials to provide to the server
|
||||
when authenticating. The following credentials are possible:
|
||||
|
||||
### 2.4.3 Callbacks
|
||||
| name | type | description
|
||||
| ------------ | ----------- | -----------
|
||||
| `"username"` | `DOMString` | The user that authenticates
|
||||
| `"password"` | `DOMString` | Password for the user
|
||||
| `"target"` | `DOMString` | Target machine or session
|
||||
|
||||
| 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.
|
||||
`repeaterID`
|
||||
- A `DOMString` specifying the ID to provide to any VNC repeater
|
||||
encountered.
|
||||
|
||||
#### updatestate
|
||||
|
||||
__RFB onUpdateState callback details__
|
||||
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.
|
||||
|
||||
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.
|
||||
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 - permanent end-state for this RFB object
|
||||
| connection state | description
|
||||
| ----------------- | ------------
|
||||
| `"connecting"` | starting to connect
|
||||
| `"connected"` | connected normally
|
||||
| `"disconnecting"` | starting to disconnect
|
||||
| `"disconnected"` | disconnected
|
||||
|
||||
__RFB onFBUReceive and on FBUComplete callback details__
|
||||
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.
|
||||
|
||||
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.
|
||||
#### notification
|
||||
|
||||
The 'fbu' parameter is an object with the following structure:
|
||||
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:
|
||||
|
||||
{
|
||||
x: FBU_x_position,
|
||||
y: FBU_y_position,
|
||||
width: FBU_width,
|
||||
height: FBU_height,
|
||||
encoding: FBU_encoding_number,
|
||||
encodingName: FBU_encoding_string
|
||||
}
|
||||
| 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.
|
||||
|
|
|
@ -77,10 +77,10 @@ export default function RecordingPlayer (frames, encoding, disconnected, notific
|
|||
RecordingPlayer.prototype = {
|
||||
run: function (realtime, trafficManagement) {
|
||||
// initialize a new RFB
|
||||
this._rfb = new RFB({'target': document.getElementById('VNC_canvas'),
|
||||
'view_only': true,
|
||||
'onDisconnected': this._handleDisconnect.bind(this),
|
||||
'onNotification': this._notification});
|
||||
this._rfb = new RFB(document.getElementById('VNC_canvas'), 'wss://test');
|
||||
this._rfb.viewOnly = true;
|
||||
this._rfb.ondisconnected = this._handleDisconnect.bind(this);
|
||||
this._rfb.onnotification = this._notification;
|
||||
this._enablePlaybackMode();
|
||||
|
||||
// reset the frame index and timer
|
||||
|
@ -92,9 +92,6 @@ RecordingPlayer.prototype = {
|
|||
|
||||
this._running = true;
|
||||
|
||||
// launch the tests
|
||||
this._rfb.connect('test', 0, 'bogus');
|
||||
|
||||
this._queueNextPacket();
|
||||
},
|
||||
|
||||
|
@ -104,14 +101,8 @@ RecordingPlayer.prototype = {
|
|||
this._rfb._sock.close = function () {};
|
||||
this._rfb._sock.flush = function () {};
|
||||
this._rfb._checkEvents = function () {};
|
||||
this._rfb.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 : "";
|
||||
this._rfb._connect = function () {
|
||||
this._sock.init('binary', 'ws');
|
||||
this._rfb_connection_state = 'connecting';
|
||||
this._rfb_init_state = 'ProtocolVersion';
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -154,12 +145,12 @@ RecordingPlayer.prototype = {
|
|||
// Avoid having excessive queue buildup in non-realtime mode
|
||||
if (this._trafficManagement && this._rfb._flushing) {
|
||||
let player = this;
|
||||
let orig = this._rfb._display.get_onFlush();
|
||||
this._rfb._display.set_onFlush(function () {
|
||||
player._rfb._display.set_onFlush(orig);
|
||||
let orig = this._rfb._display.onflush;
|
||||
this._rfb._display.onflush = function () {
|
||||
player._rfb._display.onflush = orig;
|
||||
player._rfb._onFlush();
|
||||
player._doPacket();
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -184,12 +175,12 @@ RecordingPlayer.prototype = {
|
|||
_finish() {
|
||||
if (this._rfb._display.pending()) {
|
||||
var player = this;
|
||||
this._rfb._display.set_onFlush(function () {
|
||||
this._rfb._display.onflush = function () {
|
||||
if (player._rfb._flushing) {
|
||||
player._rfb._onFlush();
|
||||
}
|
||||
player._finish();
|
||||
});
|
||||
};
|
||||
this._rfb._display.flush();
|
||||
} else {
|
||||
this._running = false;
|
||||
|
|
|
@ -3,7 +3,6 @@ var expect = chai.expect;
|
|||
|
||||
import Base64 from '../core/base64.js';
|
||||
import Display from '../core/display.js';
|
||||
import { _forceCursorURIs, browserSupportsCursorURIs } from '../core/util/browsers.js';
|
||||
|
||||
import sinon from '../vendor/sinon.js';
|
||||
|
||||
|
@ -37,30 +36,11 @@ describe('Display/Canvas Helper', function () {
|
|||
return Base64.decode(data);
|
||||
}
|
||||
|
||||
describe('checking for cursor uri support', function () {
|
||||
it('should disable cursor URIs if there is no support', function () {
|
||||
_forceCursorURIs(false);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
|
||||
it('should enable cursor URIs if there is support', function () {
|
||||
_forceCursorURIs(true);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.true;
|
||||
});
|
||||
|
||||
it('respect the cursor_uri option if there is support', function () {
|
||||
_forceCursorURIs(false);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('viewport handling', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display = new Display(document.createElement('canvas'));
|
||||
display.clipViewport = true;
|
||||
display.resize(5, 5);
|
||||
display.viewportChangeSize(3, 3);
|
||||
display.viewportChangePos(1, 1);
|
||||
|
@ -112,18 +92,16 @@ describe('Display/Canvas Helper', function () {
|
|||
});
|
||||
|
||||
it('should report clipping when framebuffer > viewport', function () {
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.true;
|
||||
expect(display.isClipped).to.be.true;
|
||||
});
|
||||
|
||||
it('should report not clipping when framebuffer = viewport', function () {
|
||||
display.viewportChangeSize(5, 5);
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.false;
|
||||
expect(display.isClipped).to.be.false;
|
||||
});
|
||||
|
||||
it('should show the entire framebuffer when disabling the viewport', function() {
|
||||
display.set_viewport(false);
|
||||
display.clipViewport = false;
|
||||
expect(display.absX(0)).to.equal(0);
|
||||
expect(display.absY(0)).to.equal(0);
|
||||
expect(display._target.width).to.equal(5);
|
||||
|
@ -131,7 +109,7 @@ describe('Display/Canvas Helper', function () {
|
|||
});
|
||||
|
||||
it('should ignore viewport changes when the viewport is disabled', function() {
|
||||
display.set_viewport(false);
|
||||
display.clipViewport = false;
|
||||
display.viewportChangeSize(2, 2);
|
||||
display.viewportChangePos(1, 1);
|
||||
expect(display.absX(0)).to.equal(0);
|
||||
|
@ -141,8 +119,8 @@ describe('Display/Canvas Helper', function () {
|
|||
});
|
||||
|
||||
it('should show the entire framebuffer just after enabling the viewport', function() {
|
||||
display.set_viewport(false);
|
||||
display.set_viewport(true);
|
||||
display.clipViewport = false;
|
||||
display.clipViewport = true;
|
||||
expect(display.absX(0)).to.equal(0);
|
||||
expect(display.absY(0)).to.equal(0);
|
||||
expect(display._target.width).to.equal(5);
|
||||
|
@ -153,7 +131,8 @@ describe('Display/Canvas Helper', function () {
|
|||
describe('resizing', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
|
||||
display = new Display(document.createElement('canvas'));
|
||||
display.clipViewport = false;
|
||||
display.resize(4, 4);
|
||||
});
|
||||
|
||||
|
@ -178,7 +157,7 @@ describe('Display/Canvas Helper', function () {
|
|||
|
||||
describe('viewport', function () {
|
||||
beforeEach(function () {
|
||||
display.set_viewport(true);
|
||||
display.clipViewport = true;
|
||||
display.viewportChangeSize(3, 3);
|
||||
display.viewportChangePos(1, 1);
|
||||
});
|
||||
|
@ -214,11 +193,12 @@ describe('Display/Canvas Helper', function () {
|
|||
var canvas;
|
||||
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
canvas = document.createElement('canvas');
|
||||
display = new Display(canvas);
|
||||
display.clipViewport = true;
|
||||
display.resize(4, 4);
|
||||
display.viewportChangeSize(3, 3);
|
||||
display.viewportChangePos(1, 1);
|
||||
canvas = display.get_target();
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
|
@ -227,21 +207,21 @@ describe('Display/Canvas Helper', function () {
|
|||
});
|
||||
|
||||
it('should not change the bitmap size of the canvas', function () {
|
||||
display.set_scale(2.0);
|
||||
display.scale = 2.0;
|
||||
expect(canvas.width).to.equal(3);
|
||||
expect(canvas.height).to.equal(3);
|
||||
});
|
||||
|
||||
it('should change the effective rendered size of the canvas', function () {
|
||||
display.set_scale(2.0);
|
||||
display.scale = 2.0;
|
||||
expect(canvas.clientWidth).to.equal(6);
|
||||
expect(canvas.clientHeight).to.equal(6);
|
||||
});
|
||||
|
||||
it('should not change when resizing', function () {
|
||||
display.set_scale(2.0);
|
||||
display.scale = 2.0;
|
||||
display.resize(5, 5);
|
||||
expect(display.get_scale()).to.equal(2.0);
|
||||
expect(display.scale).to.equal(2.0);
|
||||
expect(canvas.width).to.equal(3);
|
||||
expect(canvas.height).to.equal(3);
|
||||
expect(canvas.clientWidth).to.equal(6);
|
||||
|
@ -254,9 +234,10 @@ describe('Display/Canvas Helper', function () {
|
|||
var canvas;
|
||||
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
canvas = document.createElement('canvas');
|
||||
display = new Display(canvas);
|
||||
display.clipViewport = true;
|
||||
display.resize(4, 3);
|
||||
canvas = display.get_target();
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
|
@ -291,144 +272,125 @@ describe('Display/Canvas Helper', function () {
|
|||
expect(canvas.width).to.equal(4);
|
||||
expect(canvas.height).to.equal(3);
|
||||
});
|
||||
|
||||
it('should not upscale when downscaleOnly is true', function () {
|
||||
display.autoscale(2, 2, true);
|
||||
expect(display.absX(9)).to.equal(18);
|
||||
expect(display.absY(18)).to.equal(36);
|
||||
expect(canvas.clientWidth).to.equal(2);
|
||||
expect(canvas.clientHeight).to.equal(2);
|
||||
|
||||
display.autoscale(16, 9, true);
|
||||
expect(display.absX(9)).to.equal(9);
|
||||
expect(display.absY(18)).to.equal(18);
|
||||
expect(canvas.clientWidth).to.equal(4);
|
||||
expect(canvas.clientHeight).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawing', function () {
|
||||
|
||||
// TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
|
||||
// basic cases
|
||||
function drawing_tests (pref_js) {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
|
||||
display.resize(4, 4);
|
||||
});
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display(document.createElement('canvas'));
|
||||
display.resize(4, 4);
|
||||
});
|
||||
|
||||
it('should clear the screen on #clear without a logo set', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
|
||||
display._logo = null;
|
||||
display.clear();
|
||||
display.resize(4, 4);
|
||||
var empty = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
|
||||
expect(display).to.have.displayed(new Uint8Array(empty));
|
||||
});
|
||||
it('should clear the screen on #clear without a logo set', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
|
||||
display._logo = null;
|
||||
display.clear();
|
||||
display.resize(4, 4);
|
||||
var empty = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
|
||||
expect(display).to.have.displayed(new Uint8Array(empty));
|
||||
});
|
||||
|
||||
it('should draw the logo on #clear with a logo set', function (done) {
|
||||
display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) };
|
||||
display.clear();
|
||||
display.set_onFlush(function () {
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
expect(display._fb_width).to.equal(4);
|
||||
expect(display._fb_height).to.equal(4);
|
||||
done();
|
||||
});
|
||||
display.flush();
|
||||
});
|
||||
|
||||
it('should not draw directly on the target canvas', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
|
||||
display.flip();
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
var expected = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) {
|
||||
expected[i] = 0xff;
|
||||
expected[i+1] = expected[i+2] = 0;
|
||||
expected[i+3] = 0xff;
|
||||
}
|
||||
expect(display).to.have.displayed(new Uint8Array(expected));
|
||||
});
|
||||
|
||||
it('should support filling a rectangle with particular color via #fillRect', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.flip();
|
||||
it('should draw the logo on #clear with a logo set', function (done) {
|
||||
display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) };
|
||||
display.clear();
|
||||
display.onflush = function () {
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
expect(display._fb_width).to.equal(4);
|
||||
expect(display._fb_height).to.equal(4);
|
||||
done();
|
||||
};
|
||||
display.flush();
|
||||
});
|
||||
|
||||
it('should support copying an portion of the canvas via #copyImage', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
|
||||
display.copyImage(0, 0, 2, 2, 2, 2);
|
||||
display.flip();
|
||||
it('should not draw directly on the target canvas', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
|
||||
display.flip();
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
var expected = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) {
|
||||
expected[i] = 0xff;
|
||||
expected[i+1] = expected[i+2] = 0;
|
||||
expected[i+3] = 0xff;
|
||||
}
|
||||
expect(display).to.have.displayed(new Uint8Array(expected));
|
||||
});
|
||||
|
||||
it('should support filling a rectangle with particular color via #fillRect', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support copying an portion of the canvas via #copyImage', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
|
||||
display.copyImage(0, 0, 2, 2, 2, 2);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing images via #imageRect', function (done) {
|
||||
display.imageRect(0, 0, "image/png", make_image_png(checked_data));
|
||||
display.flip();
|
||||
display.onflush = function () {
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
done();
|
||||
};
|
||||
display.flush();
|
||||
});
|
||||
|
||||
it('should support drawing images via #imageRect', function (done) {
|
||||
display.imageRect(0, 0, "image/png", make_image_png(checked_data));
|
||||
display.flip();
|
||||
display.set_onFlush(function () {
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
done();
|
||||
});
|
||||
display.flush();
|
||||
});
|
||||
it('should support drawing tile data with a background color and sub tiles', function () {
|
||||
display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.finishTile();
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing tile data with a background color and sub tiles', function () {
|
||||
display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.finishTile();
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
it('should support drawing BGRX blit images with true color via #blitImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 4] = checked_data[i * 4 + 2];
|
||||
data[i * 4 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 4 + 2] = checked_data[i * 4];
|
||||
data[i * 4 + 3] = checked_data[i * 4 + 3];
|
||||
}
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing BGRX blit images with true color via #blitImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 4] = checked_data[i * 4 + 2];
|
||||
data[i * 4 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 4 + 2] = checked_data[i * 4];
|
||||
data[i * 4 + 3] = checked_data[i * 4 + 3];
|
||||
}
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 3] = checked_data[i * 4];
|
||||
data[i * 3 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 3 + 2] = checked_data[i * 4 + 2];
|
||||
}
|
||||
display.blitRgbImage(0, 0, 4, 4, data, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 3] = checked_data[i * 4];
|
||||
data[i * 3 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 3 + 2] = checked_data[i * 4 + 2];
|
||||
}
|
||||
display.blitRgbImage(0, 0, 4, 4, data, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing an image object via #drawImage', function () {
|
||||
var img = make_image_canvas(checked_data);
|
||||
display.drawImage(img, 0, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
}
|
||||
|
||||
describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
|
||||
describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
|
||||
it('should support drawing an image object via #drawImage', function () {
|
||||
var img = make_image_canvas(checked_data);
|
||||
display.drawImage(img, 0, 0);
|
||||
display.flip();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the render queue processor', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false });
|
||||
display = new Display(document.createElement('canvas'));
|
||||
display.resize(4, 4);
|
||||
sinon.spy(display, '_scan_renderQ');
|
||||
});
|
||||
|
@ -468,11 +430,11 @@ describe('Display/Canvas Helper', function () {
|
|||
});
|
||||
|
||||
it('should call callback when queue is flushed', function () {
|
||||
display.set_onFlush(sinon.spy());
|
||||
display.onflush = sinon.spy();
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
expect(display.get_onFlush()).to.not.have.been.called;
|
||||
expect(display.onflush).to.not.have.been.called;
|
||||
display.flush();
|
||||
expect(display.get_onFlush()).to.have.been.calledOnce;
|
||||
expect(display.onflush).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should draw a blit image on type "blit"', function () {
|
||||
|
|
|
@ -31,105 +31,105 @@ describe('Key Event Handling', function() {
|
|||
describe('Decode Keyboard Events', function() {
|
||||
it('should decode keydown events', function(done) {
|
||||
if (isIE() || isEdge()) this.skip();
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||
});
|
||||
it('should decode keyup events', function(done) {
|
||||
if (isIE() || isEdge()) this.skip();
|
||||
var calls = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
if (calls++ === 1) {
|
||||
expect(down).to.be.equal(false);
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
||||
});
|
||||
|
||||
describe('Legacy keypress Events', function() {
|
||||
it('should wait for keypress when needed', function() {
|
||||
var callback = sinon.spy();
|
||||
var kbd = new Keyboard({onKeyEvent: callback});
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = sinon.spy();
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
|
||||
expect(callback).to.not.have.been.called;
|
||||
expect(kbd.onkeyevent).to.not.have.been.called;
|
||||
});
|
||||
it('should decode keypress events', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
|
||||
kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61}));
|
||||
});
|
||||
it('should ignore keypress with different code', function() {
|
||||
var callback = sinon.spy();
|
||||
var kbd = new Keyboard({onKeyEvent: callback});
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = sinon.spy();
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
|
||||
kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61}));
|
||||
expect(callback).to.not.have.been.called;
|
||||
expect(kbd.onkeyevent).to.not.have.been.called;
|
||||
});
|
||||
it('should handle keypress with missing code', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
|
||||
kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61}));
|
||||
});
|
||||
it('should guess key if no keypress and numeric key', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x32);
|
||||
expect(code).to.be.equal('Digit2');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32}));
|
||||
});
|
||||
it('should guess key if no keypress and alpha key', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false}));
|
||||
});
|
||||
it('should guess key if no keypress and alpha key (with shift)', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x41);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true}));
|
||||
});
|
||||
it('should not guess key if no keypress and unknown key', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09}));
|
||||
});
|
||||
});
|
||||
|
@ -139,7 +139,7 @@ describe('Key Event Handling', function() {
|
|||
if (isIE() || isEdge()) this.skip();
|
||||
});
|
||||
it('should suppress anything with a valid key', function() {
|
||||
var kbd = new Keyboard({});
|
||||
var kbd = new Keyboard(document, {});
|
||||
var evt = keyevent('keydown', {code: 'KeyA', key: 'a'});
|
||||
kbd._handleKeyDown(evt);
|
||||
expect(evt.preventDefault).to.have.been.called;
|
||||
|
@ -148,13 +148,13 @@ describe('Key Event Handling', function() {
|
|||
expect(evt.preventDefault).to.have.been.called;
|
||||
});
|
||||
it('should not suppress keys without key', function() {
|
||||
var kbd = new Keyboard({});
|
||||
var kbd = new Keyboard(document, {});
|
||||
var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
|
||||
kbd._handleKeyDown(evt);
|
||||
expect(evt.preventDefault).to.not.have.been.called;
|
||||
});
|
||||
it('should suppress the following keypress event', function() {
|
||||
var kbd = new Keyboard({});
|
||||
var kbd = new Keyboard(document, {});
|
||||
var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
|
||||
kbd._handleKeyDown(evt);
|
||||
var evt = keyevent('keypress', {code: 'KeyA', charCode: 0x41});
|
||||
|
@ -168,8 +168,8 @@ describe('Key Event Handling', function() {
|
|||
it('should fake keyup events for virtual keyboards', function(done) {
|
||||
if (isIE() || isEdge()) this.skip();
|
||||
var count = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch (count++) {
|
||||
case 0:
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
|
@ -182,7 +182,7 @@ describe('Key Event Handling', function() {
|
|||
expect(down).to.be.equal(false);
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'}));
|
||||
});
|
||||
|
||||
|
@ -215,8 +215,8 @@ describe('Key Event Handling', function() {
|
|||
it('should fake keyup events on iOS', function(done) {
|
||||
if (isIE() || isEdge()) this.skip();
|
||||
var count = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch (count++) {
|
||||
case 0:
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
|
@ -229,7 +229,7 @@ describe('Key Event Handling', function() {
|
|||
expect(down).to.be.equal(false);
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||
});
|
||||
});
|
||||
|
@ -240,67 +240,67 @@ describe('Key Event Handling', function() {
|
|||
if (isIE() || isEdge()) this.skip();
|
||||
});
|
||||
it('should send release using the same keysym as the press', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
if (!down) {
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
|
||||
});
|
||||
it('should send the same keysym for multiple presses', function() {
|
||||
var count = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('KeyA');
|
||||
expect(down).to.be.equal(true);
|
||||
count++;
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
|
||||
expect(count).to.be.equal(2);
|
||||
});
|
||||
it('should do nothing on keyup events if no keys are down', function() {
|
||||
var callback = sinon.spy();
|
||||
var kbd = new Keyboard({onKeyEvent: callback});
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = sinon.spy();
|
||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
||||
expect(callback).to.not.have.been.called;
|
||||
expect(kbd.onkeyevent).to.not.have.been.called;
|
||||
});
|
||||
|
||||
describe('Legacy Events', function() {
|
||||
it('should track keys using keyCode if no code', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('Platform65');
|
||||
if (!down) {
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'}));
|
||||
kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'}));
|
||||
});
|
||||
it('should ignore compositing code', function() {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('Unidentified');
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'}));
|
||||
});
|
||||
it('should track keys using keyIdentifier if no code', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
expect(code).to.be.equal('Platform65');
|
||||
if (!down) {
|
||||
done();
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'}));
|
||||
kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'}));
|
||||
});
|
||||
|
@ -335,8 +335,8 @@ describe('Key Event Handling', function() {
|
|||
|
||||
it('should change Alt to AltGraph', function() {
|
||||
var count = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch (count++) {
|
||||
case 0:
|
||||
expect(keysym).to.be.equal(0xFF7E);
|
||||
|
@ -347,27 +347,27 @@ describe('Key Event Handling', function() {
|
|||
expect(code).to.be.equal('AltRight');
|
||||
break;
|
||||
}
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
|
||||
expect(count).to.be.equal(2);
|
||||
});
|
||||
it('should change left Super to Alt', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0xFFE9);
|
||||
expect(code).to.be.equal('MetaLeft');
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
|
||||
});
|
||||
it('should change right Super to left Super', function(done) {
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
expect(keysym).to.be.equal(0xFFEB);
|
||||
expect(code).to.be.equal('MetaRight');
|
||||
done();
|
||||
}});
|
||||
};
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
|
||||
});
|
||||
});
|
||||
|
@ -400,8 +400,8 @@ describe('Key Event Handling', function() {
|
|||
|
||||
it('should generate fake undo/redo events on press when AltGraph is down', function() {
|
||||
var times_called = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch(times_called++) {
|
||||
case 0:
|
||||
expect(keysym).to.be.equal(0xFFE3);
|
||||
|
@ -439,7 +439,7 @@ describe('Key Event Handling', function() {
|
|||
expect(down).to.be.equal(true);
|
||||
break;
|
||||
}
|
||||
}});
|
||||
};
|
||||
// First the modifier combo
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
|
||||
|
@ -449,8 +449,8 @@ describe('Key Event Handling', function() {
|
|||
});
|
||||
it('should no do anything on key release', function() {
|
||||
var times_called = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch(times_called++) {
|
||||
case 7:
|
||||
expect(keysym).to.be.equal(0x61);
|
||||
|
@ -458,7 +458,7 @@ describe('Key Event Handling', function() {
|
|||
expect(down).to.be.equal(false);
|
||||
break;
|
||||
}
|
||||
}});
|
||||
};
|
||||
// First the modifier combo
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
|
||||
|
@ -469,8 +469,8 @@ describe('Key Event Handling', function() {
|
|||
});
|
||||
it('should not consider a char modifier to be down on the modifier key itself', function() {
|
||||
var times_called = 0;
|
||||
var kbd = new Keyboard({
|
||||
onKeyEvent: function(keysym, code, down) {
|
||||
var kbd = new Keyboard(document);
|
||||
kbd.onkeyevent = function(keysym, code, down) {
|
||||
switch(times_called++) {
|
||||
case 0:
|
||||
expect(keysym).to.be.equal(0xFFE3);
|
||||
|
@ -488,7 +488,7 @@ describe('Key Event Handling', function() {
|
|||
expect(down).to.be.equal(true);
|
||||
break;
|
||||
}
|
||||
}});
|
||||
};
|
||||
// First the modifier combo
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
||||
kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
|
||||
|
|
|
@ -33,59 +33,51 @@ describe('Mouse Event Handling', function() {
|
|||
|
||||
describe('Decode Mouse Events', function() {
|
||||
it('should decode mousedown events', function(done) {
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
expect(bmask).to.be.equal(0x01);
|
||||
expect(down).to.be.equal(1);
|
||||
done();
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
expect(bmask).to.be.equal(0x01);
|
||||
expect(down).to.be.equal(1);
|
||||
done();
|
||||
};
|
||||
mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
|
||||
});
|
||||
it('should decode mouseup events', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
expect(bmask).to.be.equal(0x01);
|
||||
if (calls++ === 1) {
|
||||
expect(down).to.not.be.equal(1);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
expect(bmask).to.be.equal(0x01);
|
||||
if (calls++ === 1) {
|
||||
expect(down).to.not.be.equal(1);
|
||||
done();
|
||||
}
|
||||
};
|
||||
mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
|
||||
mouse._handleMouseUp(mouseevent('mouseup', { button: '0x01' }));
|
||||
});
|
||||
it('should decode mousemove events', function(done) {
|
||||
var mouse = new Mouse({
|
||||
onMouseMove: function(x, y) {
|
||||
// Note that target relative coordinates are sent
|
||||
expect(x).to.be.equal(40);
|
||||
expect(y).to.be.equal(10);
|
||||
done();
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousemove = function(x, y) {
|
||||
// Note that target relative coordinates are sent
|
||||
expect(x).to.be.equal(40);
|
||||
expect(y).to.be.equal(10);
|
||||
done();
|
||||
};
|
||||
mouse._handleMouseMove(mouseevent('mousemove',
|
||||
{ clientX: 50, clientY: 20 }));
|
||||
});
|
||||
it('should decode mousewheel events', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
calls++;
|
||||
expect(bmask).to.be.equal(1<<6);
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
} else if (calls === 2) {
|
||||
expect(down).to.not.be.equal(1);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
calls++;
|
||||
expect(bmask).to.be.equal(1<<6);
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
} else if (calls === 2) {
|
||||
expect(down).to.not.be.equal(1);
|
||||
done();
|
||||
}
|
||||
};
|
||||
mouse._handleMouseWheel(mouseevent('mousewheel',
|
||||
{ deltaX: 50, deltaY: 0,
|
||||
deltaMode: 0}));
|
||||
|
@ -99,22 +91,20 @@ describe('Mouse Event Handling', function() {
|
|||
|
||||
it('should use same pos for 2nd tap if close enough', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
done();
|
||||
}
|
||||
};
|
||||
// touch events are sent in an array of events
|
||||
// with one item for each touch point
|
||||
mouse._handleMouseDown(touchevent(
|
||||
|
@ -132,22 +122,20 @@ describe('Mouse Event Handling', function() {
|
|||
|
||||
it('should not modify 2nd tap pos if far apart', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
};
|
||||
mouse._handleMouseDown(touchevent(
|
||||
'touchstart', { touches: [{ clientX: 78, clientY: 46 }]}));
|
||||
this.clock.tick(10);
|
||||
|
@ -163,22 +151,20 @@ describe('Mouse Event Handling', function() {
|
|||
|
||||
it('should not modify 2nd tap pos if not soon enough', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
};
|
||||
mouse._handleMouseDown(touchevent(
|
||||
'touchstart', { touches: [{ clientX: 78, clientY: 46 }]}));
|
||||
this.clock.tick(10);
|
||||
|
@ -194,22 +180,20 @@ describe('Mouse Event Handling', function() {
|
|||
|
||||
it('should not modify 2nd tap pos if not touch', function(done) {
|
||||
var calls = 0;
|
||||
var mouse = new Mouse({
|
||||
onMouseButton: function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
},
|
||||
target: target
|
||||
});
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = function(x, y, down, bmask) {
|
||||
calls++;
|
||||
if (calls === 1) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.be.equal(68);
|
||||
expect(y).to.be.equal(36);
|
||||
} else if (calls === 3) {
|
||||
expect(down).to.be.equal(1);
|
||||
expect(x).to.not.be.equal(68);
|
||||
expect(y).to.not.be.equal(36);
|
||||
done();
|
||||
}
|
||||
};
|
||||
mouse._handleMouseDown(mouseevent(
|
||||
'mousedown', { button: '0x01', clientX: 78, clientY: 46 }));
|
||||
this.clock.tick(10);
|
||||
|
@ -231,8 +215,8 @@ describe('Mouse Event Handling', function() {
|
|||
afterEach(function () { this.clock.restore(); });
|
||||
|
||||
it('should accumulate wheel events if small enough', function () {
|
||||
var callback = sinon.spy();
|
||||
var mouse = new Mouse({ onMouseButton: callback, target: target });
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = sinon.spy();
|
||||
|
||||
mouse._handleMouseWheel(mouseevent(
|
||||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
|
@ -250,7 +234,7 @@ describe('Mouse Event Handling', function() {
|
|||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
deltaX: 4, deltaY: 0, deltaMode: 0 }));
|
||||
|
||||
expect(callback).to.have.callCount(2); // mouse down and up
|
||||
expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
|
||||
|
||||
this.clock.tick(10);
|
||||
mouse._handleMouseWheel(mouseevent(
|
||||
|
@ -260,12 +244,12 @@ describe('Mouse Event Handling', function() {
|
|||
expect(mouse._accumulatedWheelDeltaX).to.be.equal(4);
|
||||
expect(mouse._accumulatedWheelDeltaY).to.be.equal(9);
|
||||
|
||||
expect(callback).to.have.callCount(2); // still
|
||||
expect(mouse.onmousebutton).to.have.callCount(2); // still
|
||||
});
|
||||
|
||||
it('should not accumulate large wheel events', function () {
|
||||
var callback = sinon.spy();
|
||||
var mouse = new Mouse({ onMouseButton: callback, target: target });
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = sinon.spy();
|
||||
|
||||
mouse._handleMouseWheel(mouseevent(
|
||||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
|
@ -279,24 +263,24 @@ describe('Mouse Event Handling', function() {
|
|||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
deltaX: 400, deltaY: 400, deltaMode: 0 }));
|
||||
|
||||
expect(callback).to.have.callCount(8); // mouse down and up
|
||||
expect(mouse.onmousebutton).to.have.callCount(8); // mouse down and up
|
||||
});
|
||||
|
||||
it('should send even small wheel events after a timeout', function () {
|
||||
var callback = sinon.spy();
|
||||
var mouse = new Mouse({ onMouseButton: callback, target: target });
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = sinon.spy();
|
||||
|
||||
mouse._handleMouseWheel(mouseevent(
|
||||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
deltaX: 1, deltaY: 0, deltaMode: 0 }));
|
||||
this.clock.tick(51); // timeout on 50 ms
|
||||
|
||||
expect(callback).to.have.callCount(2); // mouse down and up
|
||||
expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
|
||||
});
|
||||
|
||||
it('should account for non-zero deltaMode', function () {
|
||||
var callback = sinon.spy();
|
||||
var mouse = new Mouse({ onMouseButton: callback, target: target });
|
||||
var mouse = new Mouse(target);
|
||||
mouse.onmousebutton = sinon.spy();
|
||||
|
||||
mouse._handleMouseWheel(mouseevent(
|
||||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
|
@ -308,7 +292,7 @@ describe('Mouse Event Handling', function() {
|
|||
'mousewheel', { clientX: 18, clientY: 40,
|
||||
deltaX: 1, deltaY: 0, deltaMode: 2 }));
|
||||
|
||||
expect(callback).to.have.callCount(4); // mouse down and up
|
||||
expect(mouse.onmousebutton).to.have.callCount(4); // mouse down and up
|
||||
});
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
17
vnc.html
17
vnc.html
|
@ -152,18 +152,18 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- XVP Shutdown/Reboot -->
|
||||
<!-- Shutdown/Reboot -->
|
||||
<input type="image" alt="Shutdown/Reboot" src="app/images/power.svg"
|
||||
id="noVNC_xvp_button" class="noVNC_button"
|
||||
id="noVNC_power_button" class="noVNC_button"
|
||||
title="Shutdown/Reboot..." />
|
||||
<div class="noVNC_vcenter">
|
||||
<div id="noVNC_xvp" class="noVNC_panel">
|
||||
<div id="noVNC_power" class="noVNC_panel">
|
||||
<div class="noVNC_heading">
|
||||
<img src="app/images/power.svg"> Power
|
||||
</div>
|
||||
<input type="button" id="noVNC_xvp_shutdown_button" value="Shutdown" />
|
||||
<input type="button" id="noVNC_xvp_reboot_button" value="Reboot" />
|
||||
<input type="button" id="noVNC_xvp_reset_button" value="Reset" />
|
||||
<input type="button" id="noVNC_shutdown_button" value="Shutdown" />
|
||||
<input type="button" id="noVNC_reboot_button" value="Reboot" />
|
||||
<input type="button" id="noVNC_reset_button" value="Reset" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -213,7 +213,6 @@
|
|||
<select id="noVNC_setting_resize" name="vncResize">
|
||||
<option value="off">None</option>
|
||||
<option value="scale">Local Scaling</option>
|
||||
<option value="downscale">Local Downscaling</option>
|
||||
<option value="remote">Remote Resizing</option>
|
||||
</select>
|
||||
</li>
|
||||
|
@ -221,10 +220,6 @@
|
|||
<li>
|
||||
<div class="noVNC_expander">Advanced</div>
|
||||
<div><ul>
|
||||
<li>
|
||||
<label><input id="noVNC_setting_cursor" type="checkbox" /> Local Cursor</label>
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<label for="noVNC_setting_repeaterID">Repeater ID:</label>
|
||||
<input id="noVNC_setting_repeaterID" type="input" value="" />
|
||||
|
|
120
vnc_lite.html
120
vnc_lite.html
|
@ -80,6 +80,7 @@
|
|||
import RFB from './core/rfb.js';
|
||||
|
||||
var rfb;
|
||||
var doneInitialResize;
|
||||
var resizeTimeout;
|
||||
var desktopName;
|
||||
|
||||
|
@ -92,17 +93,15 @@
|
|||
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');
|
||||
|
@ -114,26 +113,26 @@
|
|||
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) {
|
||||
|
@ -148,14 +147,16 @@
|
|||
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 {
|
||||
|
@ -170,25 +171,25 @@
|
|||
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 () {
|
||||
|
@ -201,10 +202,10 @@
|
|||
}, 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) {
|
||||
xvpbuttons.className= "noVNC_shown";
|
||||
} else {
|
||||
xvpbuttons.className = "noVNC_hidden";
|
||||
|
@ -212,9 +213,9 @@
|
|||
}
|
||||
|
||||
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');
|
||||
|
@ -252,27 +253,32 @@
|
|||
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';
|
||||
}
|
||||
|
||||
rfb.connect(host, port, password, path);
|
||||
url += '://' + host;
|
||||
if(port) {
|
||||
url += ':' + port;
|
||||
}
|
||||
url += '/' + 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>
|
||||
|
@ -284,13 +290,13 @@
|
|||
<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>
|
||||
|
|
Loading…
Reference in New Issue