noVNC/app/ui_screen.js

286 lines
11 KiB
JavaScript

import RFB from "../core/rfb.js";
import * as WebUtil from "./webutil.js";
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox, isWindows, isIOS, supportsPointerLock }
from '../core/util/browser.js';
import { MouseButtonMapper, XVNC_BUTTONS } from "../core/mousebuttonmapper.js";
import * as Log from '../core/util/logging.js';
const UI = {
connected: false,
screenID: null,
//Initial Loading of the UI
prime() {
this.start();
},
//Render default UI
start() {
window.addEventListener("beforeunload", (e) => {
if (UI.rfb) {
UI.disconnect();
}
});
UI.addDefaultHandlers();
UI.updateVisualState('disconnected');
},
addDefaultHandlers() {
document.getElementById('noVNC_connect_button').addEventListener('click', UI.connect);;
},
getSetting(name, isBool, default_value) {
let val = WebUtil.readSetting(name);
if ((val === 'undefined' || val === null) && default_value !== 'undefined' && default_value !== null) {
val = default_value;
}
if (typeof val !== 'undefined' && val !== null && isBool) {
if (val.toString().toLowerCase() in {'0': 1, 'no': 1, 'false': 1}) {
val = false;
} else {
val = true;
}
}
return val;
},
connect() {
UI.rfb = new RFB(document.getElementById('noVNC_container'),
document.getElementById('noVNC_keyboardinput'),
"", //URL
{
shared: UI.getSetting('shared', true),
repeaterID: UI.getSetting('repeaterID', false),
credentials: { password: null }
},
false // Not a primary display
);
UI.rfb.addEventListener("connect", UI.connectFinished);
//UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
UI.rfb.clipViewport = UI.getSetting('view_clip');
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
UI.rfb.dynamicQualityMin = parseInt(UI.getSetting('dynamic_quality_min'));
UI.rfb.dynamicQualityMax = parseInt(UI.getSetting('dynamic_quality_max'));
UI.rfb.jpegVideoQuality = parseInt(UI.getSetting('jpeg_video_quality'));
UI.rfb.webpVideoQuality = parseInt(UI.getSetting('webp_video_quality'));
UI.rfb.videoArea = parseInt(UI.getSetting('video_area'));
UI.rfb.videoTime = parseInt(UI.getSetting('video_time'));
UI.rfb.videoOutTime = parseInt(UI.getSetting('video_out_time'));
UI.rfb.videoScaling = parseInt(UI.getSetting('video_scaling'));
UI.rfb.treatLossless = parseInt(UI.getSetting('treat_lossless'));
UI.rfb.maxVideoResolutionX = parseInt(UI.getSetting('max_video_resolution_x'));
UI.rfb.maxVideoResolutionY = parseInt(UI.getSetting('max_video_resolution_y'));
UI.rfb.frameRate = parseInt(UI.getSetting('framerate'));
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
UI.rfb.showDotCursor = UI.getSetting('show_dot', true);
UI.rfb.idleDisconnect = UI.getSetting('idle_disconnect');
UI.rfb.pointerRelative = UI.getSetting('pointer_relative');
UI.rfb.videoQuality = parseInt(UI.getSetting('video_quality'));
UI.rfb.antiAliasing = UI.getSetting('anti_aliasing');
UI.rfb.clipboardUp = UI.getSetting('clipboard_up', true, true);
UI.rfb.clipboardDown = UI.getSetting('clipboard_down', true, true);
UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless', true, true);
UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime', true, false);
UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless;
UI.rfb.enableWebRTC = UI.getSetting('enable_webrtc', true, false);
UI.rfb.enableHiDpi = UI.getSetting('enable_hidpi', true, false);
UI.rfb.mouseButtonMapper = UI.initMouseButtonMapper();
if (UI.rfb.videoQuality === 5) {
UI.rfb.enableQOI = true;
}
this._supportsBroadcastChannel = (typeof BroadcastChannel !== "undefined");
if (this._supportsBroadcastChannel) {
this._controlChannel = new BroadcastChannel("registrationChannel");
this._controlChannel.addEventListener('message', (event) => {
switch (event.data.eventType) {
case 'identify':
UI.identify(event.data)
break;
}
});
}
//attach this secondary display to the primary display
UI.screenID = UI.rfb.attachSecondaryDisplay();
document.querySelector('title').textContent = 'Display ' + UI.screenID
if (supportsBinaryClipboard()) {
// explicitly request permission to the clipboard
navigator.permissions.query({ name: "clipboard-read" }).then((result) => { Log.Debug('binary clipboard enabled') });
}
},
updateVisualState(state) {
document.documentElement.classList.remove("noVNC_connecting");
document.documentElement.classList.remove("noVNC_connected");
document.documentElement.classList.remove("noVNC_disconnecting");
document.documentElement.classList.remove("noVNC_reconnecting");
document.documentElement.classList.remove("noVNC_disconnected");
const transitionElem = document.getElementById("noVNC_transition_text");
if (WebUtil.isInsideKasmVDI())
{
parent.postMessage({ action: 'connection_state', value: state}, '*' );
}
let connect_el = document.getElementById('noVNC_connect_dlg');
switch (state) {
case 'init':
break;
case 'connecting':
transitionElem.textContent = _("Connecting...");
document.documentElement.classList.add("noVNC_connecting");
break;
case 'connected':
document.documentElement.classList.add("noVNC_connected");
if (!connect_el.classList.contains("noVNC_hidden")) {
connect_el.classList.add('noVNC_hidden');
}
break;
case 'disconnecting':
transitionElem.textContent = _("Disconnecting...");
document.documentElement.classList.add("noVNC_disconnecting");
break;
case 'disconnected':
document.documentElement.classList.add("noVNC_disconnected");
if (connect_el.classList.contains("noVNC_hidden")) {
connect_el.classList.remove('noVNC_hidden');
}
break;
case 'reconnecting':
transitionElem.textContent = _("Reconnecting...");
document.documentElement.classList.add("noVNC_reconnecting");
break;
default:
Log.Error("Invalid visual state: " + state);
UI.showStatus(_("Internal error"), 'error');
return;
}
},
identify(data) {
const screen = data.screens.find(el => el.id === UI.screenID)
document.getElementById('noVNC_identify_monitor').innerHTML = screen.num
document.getElementById('noVNC_identify_monitor').classList.add("show")
document.querySelector('title').textContent = 'Display ' + screen.num + ' - ' + UI.screenID
setTimeout(() => {
document.getElementById('noVNC_identify_monitor').classList.remove("show")
}, 3500)
},
showStatus(text, statusType, time, kasm = false) {
// If inside the full Kasm CDI framework, don't show messages unless explicitly told to
if (WebUtil.isInsideKasmVDI() && !kasm) {
return;
}
const statusElem = document.getElementById('noVNC_status');
if (typeof statusType === 'undefined') {
statusType = 'normal';
}
// Don't overwrite more severe visible statuses and never
// errors. Only shows the first error.
if (statusElem.classList.contains("noVNC_open")) {
if (statusElem.classList.contains("noVNC_status_error")) {
return;
}
if (statusElem.classList.contains("noVNC_status_warn") &&
statusType === 'normal') {
return;
}
}
clearTimeout(UI.statusTimeout);
switch (statusType) {
case 'error':
statusElem.classList.remove("noVNC_status_warn");
statusElem.classList.remove("noVNC_status_normal");
statusElem.classList.add("noVNC_status_error");
break;
case 'warning':
case 'warn':
statusElem.classList.remove("noVNC_status_error");
statusElem.classList.remove("noVNC_status_normal");
statusElem.classList.add("noVNC_status_warn");
break;
case 'normal':
case 'info':
default:
statusElem.classList.remove("noVNC_status_error");
statusElem.classList.remove("noVNC_status_warn");
statusElem.classList.add("noVNC_status_normal");
break;
}
statusElem.textContent = text;
statusElem.classList.add("noVNC_open");
// If no time was specified, show the status for 1.5 seconds
if (typeof time === 'undefined') {
time = 1500;
}
// Error messages do not timeout
if (statusType !== 'error') {
UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
}
},
hideStatus() {
clearTimeout(UI.statusTimeout);
document.getElementById('noVNC_status').classList.remove("noVNC_open");
},
disconnect() {
if (UI.rfb) {
UI.rfb.disconnect();
}
},
connectFinished(e) {
UI.connected = true;
UI.inhibitReconnect = false;
UI.showStatus("Secondary Screen Connected");
UI.updateVisualState('connected');
// Do this last because it can only be used on rendered elements
UI.rfb.focus();
},
initMouseButtonMapper() {
const mouseButtonMapper = new MouseButtonMapper();
const settings = WebUtil.readSetting("mouseButtonMapper");
if (settings) {
mouseButtonMapper.load(settings);
return mouseButtonMapper;
}
mouseButtonMapper.set(0, XVNC_BUTTONS.LEFT_BUTTON);
mouseButtonMapper.set(1, XVNC_BUTTONS.MIDDLE_BUTTON);
mouseButtonMapper.set(2, XVNC_BUTTONS.RIGHT_BUTTON);
mouseButtonMapper.set(3, XVNC_BUTTONS.BACK_BUTTON);
mouseButtonMapper.set(4, XVNC_BUTTONS.FORWARD_BUTTON);
WebUtil.writeSetting("mouseButtonMapper", mouseButtonMapper.dump());
return mouseButtonMapper;
},
}
UI.prime();
export default UI;