From 67ed5e1643a35d858434fc04d6f96e7aa338a68e Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Fri, 1 May 2020 16:14:15 +0200 Subject: Add ability to set compression level Fixes github issue #1382. --- app/ui.js | 17 +++++++++ core/rfb.js | 23 +++++++++++- docs/API.md | 8 +++++ docs/EMBEDDING.md | 2 ++ tests/test.rfb.js | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ vnc.html | 4 +++ 6 files changed, 155 insertions(+), 1 deletion(-) diff --git a/app/ui.js b/app/ui.js index 32b6e98..61ddffe 100644 --- a/app/ui.js +++ b/app/ui.js @@ -162,6 +162,7 @@ const UI = { UI.initSetting('view_clip', false); UI.initSetting('resize', 'off'); UI.initSetting('quality', 6); + UI.initSetting('compression', 2); UI.initSetting('shared', true); UI.initSetting('view_only', false); UI.initSetting('show_dot', false); @@ -350,6 +351,8 @@ const UI = { UI.addSettingChangeHandler('resize', UI.updateViewClip); UI.addSettingChangeHandler('quality'); UI.addSettingChangeHandler('quality', UI.updateQuality); + UI.addSettingChangeHandler('compression'); + UI.addSettingChangeHandler('compression', UI.updateCompression); UI.addSettingChangeHandler('view_clip'); UI.addSettingChangeHandler('view_clip', UI.updateViewClip); UI.addSettingChangeHandler('shared'); @@ -841,6 +844,7 @@ const UI = { UI.updateSetting('view_clip'); UI.updateSetting('resize'); UI.updateSetting('quality'); + UI.updateSetting('compression'); UI.updateSetting('shared'); UI.updateSetting('view_only'); UI.updateSetting('path'); @@ -1043,6 +1047,7 @@ const UI = { UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); + UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); UI.rfb.showDotCursor = UI.getSetting('show_dot'); UI.updateViewOnly(); // requires UI.rfb @@ -1349,6 +1354,18 @@ const UI = { /* ------^------- * /QUALITY * ============== + * COMPRESSION + * ------v------*/ + + updateCompression() { + if (!UI.rfb) return; + + UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); + }, + +/* ------^------- + * /COMPRESSION + * ============== * KEYBOARD * ------v------*/ diff --git a/core/rfb.js b/core/rfb.js index 0593c19..4a8483f 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -278,6 +278,7 @@ export default class RFB extends EventTargetMixin { } this._qualityLevel = 6; + this._compressionLevel = 2; } // ===== PROPERTIES ===== @@ -360,6 +361,26 @@ export default class RFB extends EventTargetMixin { } } + get compressionLevel() { + return this._compressionLevel; + } + set compressionLevel(compressionLevel) { + if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) { + Log.Error("compressionLevel must be an integer between 0 and 9"); + return; + } + + if (this._compressionLevel === compressionLevel) { + return; + } + + this._compressionLevel = compressionLevel; + + if (this._rfb_connection_state === 'connected') { + this._sendEncodings(); + } + } + // ===== PUBLIC METHODS ===== disconnect() { @@ -1411,7 +1432,7 @@ export default class RFB extends EventTargetMixin { // Psuedo-encoding settings encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel); - encs.push(encodings.pseudoEncodingCompressLevel0 + 2); + encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel); encs.push(encodings.pseudoEncodingDesktopSize); encs.push(encodings.pseudoEncodingLastRect); diff --git a/docs/API.md b/docs/API.md index db43ec6..e6b0e66 100644 --- a/docs/API.md +++ b/docs/API.md @@ -69,6 +69,14 @@ protocol stream. Value `0` implies low quality and `9` implies high quality. Default value is `6`. +`compressionLevel` + - Is an `int` in range `[0-9]` controlling the desired compression + level. Value `0` means no compression. Level 1 uses a minimum of CPU + resources and achieves weak compression ratios, while level 9 offers + best compression but is slow in terms of CPU consumption on the server + side. Use high levels with very slow network connections. This only + applies to connections using the Tight encoding. Default value is `2`. + `capabilities` *Read only* - Is an `Object` indicating which optional extensions are available on the server. Some methods may only be called if the corresponding diff --git a/docs/EMBEDDING.md b/docs/EMBEDDING.md index 3f85f4b..6a5dcd8 100644 --- a/docs/EMBEDDING.md +++ b/docs/EMBEDDING.md @@ -63,6 +63,8 @@ query string. Currently the following options are available: * `quality` - The session JPEG quality level. Can be `0` to `9`. +* `compression` - The session compression level. Can be `0` to `9`. + * `show_dot` - If a dot cursor should be shown when the remote server provides no local cursor, or provides a fully-transparent (invisible) cursor. diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 4e27393..a8975c2 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2967,6 +2967,108 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); }); }); + + describe('Compression level setting', function () { + const defaultCompression = 2; + + let client; + + beforeEach(function () { + client = make_rfb(); + sinon.spy(RFB.messages, "clientEncodings"); + }); + + afterEach(function () { + RFB.messages.clientEncodings.restore(); + }); + + it(`should equal ${defaultCompression} by default`, function () { + expect(client._compressionLevel).to.equal(defaultCompression); + }); + + it('should ignore non-integers when set', function () { + client.compressionLevel = '1'; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = 1.5; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = null; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = undefined; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = {}; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should ignore integers out of range [0, 9]', function () { + client.compressionLevel = -1; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = 10; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should send clientEncodings with new compression value', function () { + let newCompression; + + newCompression = 5; + client.compressionLevel = newCompression; + expect(client.compressionLevel).to.equal(newCompression); + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); + }); + + it('should not send clientEncodings if compression is the same', function () { + let newCompression; + + newCompression = 9; + client.compressionLevel = newCompression; + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); + + RFB.messages.clientEncodings.resetHistory(); + + client.compressionLevel = newCompression; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + }); + + it('should not send clientEncodings if not in connected state', function () { + let newCompression; + + client._rfb_connection_state = ''; + newCompression = 7; + client.compressionLevel = newCompression; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client._rfb_connection_state = 'connnecting'; + newCompression = 6; + client.compressionLevel = newCompression; + expect(RFB.messages.clientEncodings).to.not.have.been.called; + + RFB.messages.clientEncodings.resetHistory(); + + client._rfb_connection_state = 'connected'; + newCompression = 5; + client.compressionLevel = newCompression; + expect(RFB.messages.clientEncodings).to.have.been.calledOnce; + expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); + }); + }); }); describe('RFB messages', function () { diff --git a/vnc.html b/vnc.html index 571ca20..a1bbb21 100644 --- a/vnc.html +++ b/vnc.html @@ -211,6 +211,10 @@ +
  • + + +

  • -- cgit v1.2.1