diff --git a/app/ui.js b/app/ui.js index e5f18d20..1531cc11 100644 --- a/app/ui.js +++ b/app/ui.js @@ -30,8 +30,8 @@ window.updateSetting = (name, value) => { } } -import "core-js/stable"; -import "regenerator-runtime/runtime"; +//import "core-js/stable"; +//import "regenerator-runtime/runtime"; import * as Log from '../core/util/logging.js'; import _, { l10n } from './localization.js'; import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox, isWindows, isIOS, supportsPointerLock } @@ -245,7 +245,8 @@ const UI = { UI.initSetting('toggle_control_panel', false); UI.initSetting('enable_perf_stats', false); UI.initSetting('virtual_keyboard_visible', false); - UI.initSetting('enable_ime', false) + UI.initSetting('enable_ime', false); + UI.initSetting('enable_webrtc', true); UI.toggleKeyboardControls(); if (WebUtil.isInsideKasmVDI()) { @@ -542,6 +543,8 @@ const UI = { UI.addSettingChangeHandler('virtual_keyboard_visible', UI.toggleKeyboardControls); UI.addSettingChangeHandler('enable_ime'); UI.addSettingChangeHandler('enable_ime', UI.toggleIMEMode); + UI.addSettingChangeHandler('enable_webrtc'); + UI.addSettingChangeHandler('enable_webrtc', UI.toggleWebRTC); }, addFullscreenHandlers() { @@ -1411,6 +1414,7 @@ const UI = { UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless'); UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime'); UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless; + UI.rfb.enableWebRTC = UI.getSetting('enable_webrtc'); //Only explicitly request permission to clipboard on browsers that support binary clipboard access if (supportsBinaryClipboard()) { @@ -1661,6 +1665,18 @@ const UI = { UI.toggleIMEMode(); } break; + case 'enable_webrtc': + if (!UI.getSetting('enable_webrtc')) { + UI.forceSetting('enable_webrtc', true, false); + UI.toggleWebRTC(); + } + break; + case 'disable_webrtc': + if (UI.getSetting('enable_webrtc')) { + UI.forceSetting('enable_webrtc', false, false); + UI.toggleWebRTC(); + } + break; } } }, @@ -2087,6 +2103,16 @@ const UI = { } }, + toggleWebRTC() { + if (UI.rfb) { + if (UI.getSetting('enable_webrtc')) { + UI.rfb.enableWebRTC = true; + } else { + UI.rfb.enableWebRTC = false; + } + } + }, + showKeyboardControls() { document.getElementById('noVNC_keyboard_control').classList.add("is-visible"); }, diff --git a/core/decoders/tight.js b/core/decoders/tight.js index 873ac9a6..6e2799e3 100644 --- a/core/decoders/tight.js +++ b/core/decoders/tight.js @@ -37,7 +37,7 @@ export default class TightDecoder { for (let i = 0; i < 4; i++) { if ((this._ctl >> i) & 1) { this._zlibs[i].reset(); - Log.Info("Reset zlib stream " + i); + Log.Debug("Reset zlib stream " + i); } } diff --git a/core/decoders/udp.js b/core/decoders/udp.js index a4516e91..13650f79 100644 --- a/core/decoders/udp.js +++ b/core/decoders/udp.js @@ -92,7 +92,7 @@ export default class UDPDecoder { for (let i = 0; i < 4; i++) { if ((zlibs_flags >> i) & 1) { this._zlibs[i].reset(); - Log.Info("Reset zlib stream " + i); + Log.Debug("Reset zlib stream " + i); } } diff --git a/core/rfb.js b/core/rfb.js index 4dccf3eb..fa770600 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -138,6 +138,16 @@ export default class RFB extends EventTargetMixin { this._maxVideoResolutionY = 540; this._clipboardBinary = true; this._useUdp = true; + this.TransitConnectionStates = { + Tcp: Symbol("tcp"), + Udp: Symbol("udp"), + Upgrading: Symbol("upgrading"), + Downgrading: Symbol("downgrading"), + Failure: Symbol("failure") + } + this._transitConnectionState = this.TransitConnectionStates.Tcp; + this._udpConnectFailures = 0; //Failures in upgrading connection to udp + this._udpTransitFailures = 0; //Failures in transit after successful upgrade this._trackFrameStats = false; @@ -698,6 +708,20 @@ export default class RFB extends EventTargetMixin { get statsFps() { return this._display.fps; } + get enableWebRTC() { return this._useUdp; } + set enableWebRTC(value) { + this._useUdp = value; + if (!value) { + if (this._rfbConnectionState === 'connected' && this._transitConnectionState == this.TransitConnectionStates.Udp) { + this._sendUdpDowngrade(); + } + } else { + if (this._rfbConnectionState === 'connected' && (this._transitConnectionState == this.TransitConnectionStates.Tcp)) { + this._sendUdpUpgrade(); + } + } + } + // ===== PUBLIC METHODS ===== /* @@ -900,6 +924,11 @@ export default class RFB extends EventTargetMixin { // ===== PRIVATE METHODS ===== + _changeTransitConnectionState(value) { + Log.Debug("Transit state change from " + this._transitConnectionState.toString() + ' to ' + value.toString()); + this._transitConnectionState = value; + } + _connect() { Log.Debug(">> RFB.connect"); @@ -1006,6 +1035,8 @@ export default class RFB extends EventTargetMixin { this._udpChannel.onerror = function(e) { Log.Error("data channel error " + e.message); + this._udpTransitFailures+=1; + this._sendUdpDowngrade(); } let sock = this._sock; @@ -3017,6 +3048,11 @@ export default class RFB extends EventTargetMixin { } _sendUdpUpgrade() { + if (this._transitConnectionState == this.TransitConnectionStates.Upgrading) { + return; + } + this._changeTransitConnectionState(this.TransitConnectionStates.Upgrading); + let peer = this._udpPeer; let sock = this._sock; @@ -3037,10 +3073,13 @@ export default class RFB extends EventTargetMixin { sock.flush(); }).catch(function(reason) { Log.Error("Failed to create offer " + reason); + this._changeTransitConnectionState(this.TransitConnectionStates.Tcp); + this._udpConnectFailures++; }); } _sendUdpDowngrade() { + this._changeTransitConnectionState(this.TransitConnectionStates.Downgrading); const buff = sock._sQ; const offset = sock._sQlen; @@ -3062,16 +3101,22 @@ export default class RFB extends EventTargetMixin { let peer = this._udpPeer; var response = JSON.parse(payload); + Log.Debug("UDP Upgrade recieved from server: " + payload); peer.setRemoteDescription(new RTCSessionDescription(response.answer)).then(function() { var candidate = new RTCIceCandidate(response.candidate); peer.addIceCandidate(candidate).then(function() { Log.Debug("success in addicecandidate"); - }).catch(function(err) { + this._changeTransitConnectionState(this.TransitConnectionStates.Udp); + }.bind(this)).catch(function(err) { Log.Error("Failure in addIceCandidate", err); - }); - }).catch(function(e) { + this._changeTransitConnectionState(this.TransitConnectionStates.Failure) + this._udpConnectFailures++; + }.bind(this)); + }.bind(this)).catch(function(e) { Log.Error("Failure in setRemoteDescription", e); - }); + this._changeTransitConnectionState(this.TransitConnectionStates.Failure) + this._udpConnectFailures++; + }.bind(this)); } _framebufferUpdate() { @@ -3426,6 +3471,23 @@ export default class RFB extends EventTargetMixin { } try { + if (this._transitConnectionState == this.TransitConnectionStates.Udp || this._transitConnectionState == this.TransitConnectionStates.Failure) { + if (this._transitConnectionState == this.TransitConnectionStates.Udp) { + Log.Warn("Implicit UDP Transit Failure, TCP rects recieved while in UDP mode.") + this._udpTransitFailures++; + } + this._changeTransitConnectionState(this.TransitConnectionStates.Tcp); + if (this._useUdp) { + if (this._udpConnectFailures < 3 && this._udpTransitFailures < 3) { + setTimeout(function() { + Log.Warn("Attempting to connect via UDP again after failure.") + this.enableWebRTC = true; + }.bind(this), 3000); + } else { + Log.Warn("UDP connection failures exceeded limit, remaining on TCP transit.") + } + } + } return decoder.decodeRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, this._sock, this._display, diff --git a/vnc.html b/vnc.html index 6531283d..1ee56b00 100644 --- a/vnc.html +++ b/vnc.html @@ -246,6 +246,12 @@ Translate keyboard shurtcuts +
  • + +