summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Clouter <alex@digriz.org.uk>2014-10-12 00:27:44 +0100
committerSolly Ross <sross@redhat.com>2016-01-07 10:58:51 -0500
commit2230ce6831675ff9a71a39f9e3eebb2b2ead2b0b (patch)
tree7b0423a83d3f552b51c4cdb19ceffd56a9758157
parent74ac4ef8c1b201ef6e6a9cba0acf5a392b2efefc (diff)
downloadnovnc-feature/ikvm-support.tar.gz
added aten-ikvm supportfeature/ikvm-support
-rw-r--r--README.md2
-rw-r--r--include/keysym.js77
-rw-r--r--include/rfb.js300
-rw-r--r--tests/test.rfb.js155
4 files changed, 532 insertions, 2 deletions
diff --git a/README.md b/README.md
index 8db4bfb..b21156b 100644
--- a/README.md
+++ b/README.md
@@ -124,7 +124,7 @@ use a WebSockets to TCP socket proxy. There is a python proxy included
* UI and Icons : Chris Gordon
* Original Logo : Michael Sersen
* tight encoding : Michael Tinglof (Mercuri.ca)
- * pixel format conversion : [Alexander Clouter](http://www.digriz.org.uk/)
+ * pixel format conversion and ATEN iKVM support : [Alexander Clouter](http://www.digriz.org.uk/)
* Included libraries:
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
diff --git a/include/keysym.js b/include/keysym.js
index 58b107c..2cc2688 100644
--- a/include/keysym.js
+++ b/include/keysym.js
@@ -376,3 +376,80 @@ XK_udiaeresis = 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIA
XK_yacute = 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
XK_thorn = 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
+
+var XK2HID = {};
+
+// F{1..12}
+for (var i = XK_F1; i <= XK_F12; i++) {
+ XK2HID[i] = 0x3a + (i - XK_F1);
+}
+// A-Za-z
+for (var i = XK_A; i <= XK_Z; i++) {
+ XK2HID[i] = 0x04 + (i - XK_A);
+ XK2HID[i + (XK_a - XK_A)] = 0x04 + (i - XK_A);
+}
+// 1-9
+for (var i = XK_1; i <= XK_9; i++) {
+ XK2HID[i] = 0x1e + (i - XK_1);
+}
+
+XK2HID[XK_0] = 0x27;
+XK2HID[XK_Return] = 0x28;
+XK2HID[XK_Escape] = 0x29;
+XK2HID[XK_BackSpace] = 0x2a;
+XK2HID[XK_Tab] = 0x2b;
+XK2HID[XK_space] = 0x2c;
+XK2HID[XK_minus] = 0x2d;
+XK2HID[XK_equal] = 0x2e;
+XK2HID[XK_bracketleft] = 0x2f;
+XK2HID[XK_bracketright] = 0x30;
+XK2HID[XK_backslash] = 0x31;
+XK2HID[XK_semicolon] = 0x33;
+XK2HID[XK_apostrophe] = 0x34;
+XK2HID[XK_grave] = 0x35;
+XK2HID[XK_comma] = 0x36;
+XK2HID[XK_period] = 0x37;
+XK2HID[XK_slash] = 0x38;
+
+XK2HID[XK_Print] = 0x46;
+XK2HID[XK_Scroll_Lock] = 0x47;
+XK2HID[XK_Pause] = 0x48;
+XK2HID[XK_Insert] = 0x49;
+XK2HID[XK_Home] = 0x4a;
+XK2HID[XK_Page_Up] = 0x4b;
+XK2HID[XK_Delete] = 0x4c;
+XK2HID[XK_End] = 0x4d;
+XK2HID[XK_Page_Down] = 0x4e;
+XK2HID[XK_Right] = 0x4f;
+XK2HID[XK_Left] = 0x50;
+XK2HID[XK_Down] = 0x51;
+XK2HID[XK_Up] = 0x52;
+
+XK2HID[XK_Control_L] = 0xe0;
+XK2HID[XK_Control_R] = XK2HID[XK_Control_L];
+XK2HID[XK_Shift_L] = 0xe1;
+XK2HID[XK_Shift_R] = XK2HID[XK_Shift_L];
+XK2HID[XK_Alt_L] = 0xe2;
+XK2HID[XK_Alt_R] = XK2HID[XK_Alt_L];
+XK2HID[XK_Super_L] = 0xe3;
+XK2HID[XK_Super_R] = XK2HID[XK_Super_L];
+
+XK2HID[XK_Caps_Lock] = 0x39;
+
+// locale hardcoded hack
+XK2HID[XK_less] = XK2HID[XK_comma];
+XK2HID[XK_greater] = XK2HID[XK_period];
+XK2HID[XK_exclam] = XK2HID[XK_1];
+XK2HID[XK_at] = XK2HID[XK_2];
+XK2HID[XK_numbersign] = XK2HID[XK_3];
+XK2HID[XK_dollar] = XK2HID[XK_4];
+XK2HID[XK_percent] = XK2HID[XK_5];
+XK2HID[XK_asciicircum] = XK2HID[XK_6];
+XK2HID[XK_ampersand] = XK2HID[XK_7];
+XK2HID[XK_asterisk] = XK2HID[XK_8];
+XK2HID[XK_parenleft] = XK2HID[XK_9];
+XK2HID[XK_parenright] = XK2HID[XK_0];
+XK2HID[XK_underscore] = XK2HID[XK_minus];
+XK2HID[XK_bar] = XK2HID[XK_backslash];
+XK2HID[XK_quotedbl] = XK2HID[XK_apostrophe];
+XK2HID[XK_asciitilde] = XK2HID[XK_grave];
diff --git a/include/rfb.js b/include/rfb.js
index 8b1d92e..52e37bb 100644
--- a/include/rfb.js
+++ b/include/rfb.js
@@ -33,6 +33,7 @@ var RFB;
this._rfb_auth_scheme = '';
this._rfb_tightvnc = false;
+ this._rfb_atenikvm = false;
this._rfb_xvp_ver = 0;
// In preference order
@@ -43,6 +44,7 @@ var RFB;
['HEXTILE', 0x05 ],
['RRE', 0x02 ],
['RAW', 0x00 ],
+ ['ATEN', 0x59 ],
['DesktopSize', -223 ],
['Cursor', -239 ],
@@ -75,6 +77,8 @@ var RFB;
subrects: 0, // RRE
lines: 0, // RAW
tiles: 0, // HEXTILE
+ aten_len: -1, // ATEN
+ aten_type: -1, // ATEN
bytes: 0,
x: 0,
y: 0,
@@ -129,6 +133,7 @@ var RFB;
'local_cursor': false, // Request locally rendered cursor
'shared': true, // Request shared mode
'view_only': false, // Disable client mouse/keyboard
+ 'aten_password_sep': ':', // Separator for ATEN iKVM password fields
'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
@@ -363,9 +368,12 @@ var RFB;
this._FBU.lines = 0; // RAW
this._FBU.tiles = 0; // HEXTILE
this._FBU.zlibs = []; // TIGHT zlib encoders
+ this._FBU.aten_len = -1; // ATEN
+ this._FBU.aten_type = -1; // ATEN
this._mouse_buttonMask = 0;
this._mouse_arr = [];
this._rfb_tightvnc = false;
+ this._rfb_atenikvm = false;
// Clear the per connection encoding stats
var i;
@@ -713,6 +721,36 @@ var RFB;
},
// authentication
+ _negotiate_aten_auth: function () {
+ var aten_sep = this._aten_password_sep;
+ var aten_auth = this._rfb_password.split(aten_sep);
+ if (aten_auth.length < 2) {
+ this._updateState('password', 'ATEN iKVM credentials required (user' + aten_sep +
+ 'password) -- got only ' + this._rfb_password);
+ this._onPasswordRequired(this);
+ return false;
+ }
+
+ this._rfb_atenikvm = true;
+
+ if (this._rfb_tightvnc) {
+ this._rfb_tightvnc = false;
+ } else {
+ this._sock.rQskipBytes(4);
+ }
+
+ this._sock.rQskipBytes(16);
+
+ var username = aten_auth[0];
+ username += new Array(24 - username.length+1).join("\x00");
+ var password = aten_auth.slice(1).join(aten_sep);
+ password += new Array(24 - password.length+1).join("\x00");
+
+ this._sock.send_string(username + password);
+ this._updateState("SecurityResult");
+ return true;
+ },
+
_negotiate_xvp_auth: function () {
var xvp_sep = this._xvp_password_sep;
var xvp_auth = this._rfb_password.split(xvp_sep);
@@ -779,9 +817,12 @@ var RFB;
},
_negotiate_tight_auth: function () {
+ var numTunnels; // NB(directxman12): this is only in scope within the following block,
+ // or if equal to zero (necessary for ATEN iKVM support)
if (!this._rfb_tightvnc) { // first pass, do the tunnel negotiation
if (this._sock.rQwait("num tunnels", 4)) { return false; }
- var numTunnels = this._sock.rQshift32();
+ numTunnels = this._sock.rQshift32();
+ if (this._rfb_version === 3.8 && (numTunnels & 0xffff0ff0) >>> 0 === 0xaff90fb0) { return this._negotiate_aten_auth(); }
if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; }
this._rfb_tightvnc = true;
@@ -797,6 +838,12 @@ var RFB;
var subAuthCount = this._sock.rQshift32();
if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; }
+ // Newer X10 Supermicro motherboards get here
+ if (this._rfb_version === 3.8 && numTunnels === 0 && subAuthCount === 0) {
+ Util.Warn("Newer ATEN iKVM detected, you may get an 'unsupported encoding 87'");
+ return this._negotiate_aten_auth();
+ }
+
var clientSupportedTypes = {
'STDVNOAUTH__': 1,
'STDVVNCAUTH_': 2
@@ -883,6 +930,7 @@ var RFB;
_negotiate_server_init: function () {
if (this._sock.rQwait("server initialization", 24)) { return false; }
+ if (this._rfb_atenikvm && this._sock.rQwait("ATEN server initialization", 36)) { return false; }
/* Screen size */
this._fb_width = this._sock.rQshift16();
@@ -911,6 +959,14 @@ var RFB;
if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length));
+ if (this._rfb_atenikvm) {
+ this._sock.rQskipBytes(8); // unknown
+ this._sock.rQskip8(); // IKVMVideoEnable
+ this._sock.rQskip8(); // IKVMKMEnable
+ this._sock.rQskip8(); // IKVMKickEnable
+ this._sock.rQskip8(); // VUSBEnable
+ }
+
if (this._rfb_tightvnc) {
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
// In TightVNC mode, ServerInit message is extended
@@ -957,6 +1013,57 @@ var RFB;
this._convertColor = true;
}
+ // ATEN 'wisdom' from chicken-aten-ikvm:lens/lens.rb
+ // tested against the following Supermicro motherboards
+ // (use 'dmidecode -s baseboard-product-name' for model):
+ // - X7SPA-F
+ // - X8DTL
+ // - X8SIE-F
+ // - X9SCL/X9SCM
+ // - X9SCM-F
+ // - X9DRD-iF
+ // - X9SRE/X9SRE-3F/X9SRi/X9SRi-3F
+ // - X9DRL-3F/X9DRL-6F
+ // - X10SLD
+ //
+ // Not supported (uses encoding 87):
+ // - X10SL7-F
+ // - X10SLD-F
+ // - X10SLM-F
+ // - X10SLE
+ //
+ // Simply does not work:
+ // Hermon (WPMC450) [hangs at login]:
+ // - X7SB3-F
+ // - X8DTU-F
+ // - X8STi-3F
+ // Peppercon (Raritan/Kira100) [connection refused]:
+ // - X7SBi
+ //
+ // Thanks to Brian Rak and Erik Smit for testing
+ if (this._rfb_atenikvm) {
+ // we do not know the resolution till the first fbupdate so go large
+ // although, not necessary, saves a pointless full screen refresh
+ this._fb_width = RFB.ATEN_INIT_WIDTH;
+ this._fb_height = RFB.ATEN_INIT_HEIGHT;
+
+ // lies about what it supports
+ Util.Warn("ATEN iKVM lies and only does 15 bit depth with RGB555");
+ this._convertColor = true;
+ this._pixelFormat.bpp = 16;
+ this._pixelFormat.depth = 15;
+ this._pixelFormat.red_max = (1 << 5) - 1;
+ this._pixelFormat.green_max = (1 << 5) - 1;
+ this._pixelFormat.blue_max = (1 << 5) - 1;
+ this._pixelFormat.red_shift = 10;
+ this._pixelFormat.green_shift = 5;
+ this._pixelFormat.blue_shift = 0;
+
+ // changes to protocol format
+ RFB.messages.keyEvent = RFB.messages.atenKeyEvent;
+ RFB.messages.pointerEvent = RFB.messages.atenPointerEvent;
+ }
+
if (this._convertColor)
this._display.set_true_color(this._pixelFormat.true_color);
this._display.resize(this._fb_width, this._fb_height);
@@ -1118,6 +1225,42 @@ var RFB;
msg_type = this._sock.rQshift8();
}
+ if (this._rfb_atenikvm) {
+ switch (msg_type) {
+ case 4: // Front Ground Event
+ Util.Debug("ATEN iKVM Front Ground Event");
+ this._sock.rQskipBytes(20);
+ return true;
+
+ case 22: // Keep Alive Event
+ Util.Debug("ATEN iKVM Keep Alive Event");
+ this._sock.rQskipBytes(1);
+ return true;
+
+ case 51: // Video Get Info
+ Util.Debug("ATEN iKVM Video Get Info");
+ this._sock.rQskipBytes(4);
+ return true;
+
+ case 55: // Mouse Get Info
+ Util.Debug("ATEN iKVM Mouse Get Info");
+ this._sock.rQskipBytes(2);
+ return true;
+
+ case 57: // Session Message
+ Util.Debug("ATEN iKVM Session Message");
+ this._sock.rQskipBytes(4); // u32
+ this._sock.rQskipBytes(4); // u32
+ this._sock.rQskipBytes(256);
+ return true;
+
+ case 60: // Get Viewer Lang
+ Util.Debug("ATEN iKVM Get Viewer Lang");
+ this._sock.rQskipBytes(8);
+ return true;
+ }
+ }
+
switch (msg_type) {
case 0: // FramebufferUpdate
var ret = this._framebufferUpdate();
@@ -1191,6 +1334,11 @@ var RFB;
this._FBU.encoding);
return false;
}
+
+ // ATEN uses 0x00 even when it is meant to be 0x59
+ if (this._rfb_atenikvm && this._FBU.encoding === 0x00) {
+ this._FBU.encoding = 0x59;
+ }
}
this._timing.last_fbu = (new Date()).getTime();
@@ -1342,6 +1490,7 @@ var RFB;
['local_cursor', 'rw', 'bool'], // Request locally rendered cursor
['shared', 'rw', 'bool'], // Request shared mode
['view_only', 'rw', 'bool'], // Disable client mouse/keyboard
+ ['aten_password_sep', 'rw', 'str'], // Separator for ATEN iKVM password fields
['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
@@ -1378,6 +1527,11 @@ var RFB;
RFB.prototype.get_keyboard = function () { return this._keyboard; };
RFB.prototype.get_mouse = function () { return this._mouse; };
+ // ATEN iKVM doesn't send the resolution until the first refresh, so start big
+ // to avoid a repaint
+ RFB.ATEN_INIT_WIDTH = 10000;
+ RFB.ATEN_INIT_HEIGHT = 10000;
+
// Class Methods
RFB.messages = {
keyEvent: function (sock, keysym, down) {
@@ -1398,6 +1552,70 @@ var RFB;
sock._sQlen += 8;
},
+ atenKeyEvent: function (sock, keysym, down) {
+ var ks = XK2HID[keysym];
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 4;
+ buff[offset + 1] = 0;
+ buff[offset + 2] = down;
+
+ buff[offset + 3] = 0;
+ buff[offset + 4] = 0;
+
+ buff[offset + 5] = (ks >> 24);
+ buff[offset + 6] = (ks >> 16);
+ buff[offset + 7] = (ks >> 8);
+ buff[offset + 8] = ks;
+
+ buff[offset + 9] = 0;
+ buff[offset + 10] = 0;
+ buff[offset + 11] = 0;
+ buff[offset + 12] = 0;
+
+ buff[offset + 13] = 0;
+ buff[offset + 14] = 0;
+ buff[offset + 15] = 0;
+ buff[offset + 16] = 0;
+
+ buff[offset + 17] = 0;
+
+ sock._sQlen += 18;
+ },
+
+ atenPointerEvent: function (sock, x, y, mask) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 5;
+ buff[offset + 1] = 0;
+ buff[offset + 2] = mask;
+
+ buff[offset + 3] = x >> 8;
+ buff[offset + 4] = x;
+
+ buff[offset + 5] = y >> 8;
+ buff[offset + 6] = y;
+
+ buff[offset + 7] = 0;
+ buff[offset + 8] = 0;
+ buff[offset + 9] = 0;
+ buff[offset + 10] = 0;
+
+ buff[offset + 11] = 0;
+ buff[offset + 12] = 0;
+ buff[offset + 13] = 0;
+ buff[offset + 14] = 0;
+
+ buff[offset + 15] = 0;
+ buff[offset + 16] = 0;
+
+ buff[offset + 17] = 0;
+
+ sock._sQlen += 18;
+ },
+
pointerEvent: function (sock, x, y, mask) {
var buff = sock._sQ;
var offset = sock._sQlen;
@@ -2214,6 +2432,86 @@ var RFB;
compress_lo: function () {
Util.Error("Server sent compress level pseudo-encoding");
+ },
+
+ ATEN: function () {
+ if (this._FBU.aten_len === -1) {
+ this._FBU.bytes = 8;
+ if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
+ this._FBU.bytes = 0;
+ this._sock.rQskipBytes(4);
+ this._FBU.aten_len = this._sock.rQshift32();
+
+ if (this._FBU.width === 64896 && this._FBU.height === 65056) {
+ Util.Info("ATEN iKVM screen is probably off");
+ if (this._FBU.aten_len !== 10 && this._FBU.aten_len !== 0) {
+ Util.Debug(">> ATEN iKVM screen off (aten_len="+this._FBU.aten_len+")");
+ this._fail('expected aten_len to be 10 when screen is off');
+ }
+ this._FBU.aten_len = 0;
+ return true;
+ }
+ if (this._fb_width !== this._FBU.width && this._fb_height !== this._FBU.height) {
+ Util.Debug(">> ATEN resize desktop");
+ this._fb_width = this._FBU.width;
+ this._fb_height = this._FBU.height;
+ this._onFBResize(this, this._fb_width, this._fb_height);
+ this._display.resize(this._fb_width, this._fb_height);
+ Util.Debug("<< ATEN resize desktop");
+ }
+ }
+
+ if (this._FBU.aten_type === -1) {
+ this._FBU.bytes = 10;
+ if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
+ this._FBU.bytes = 0;
+ this._FBU.aten_type = this._sock.rQshift8();
+ this._sock.rQskip8();
+
+ this._sock.rQskipBytes(4); // number of subrects
+ if (this._FBU.aten_len !== this._sock.rQshift32()) {
+ return this._fail('ATEN RAW len mis-match');
+ }
+ this._FBU.aten_len -= 10;
+ }
+
+ while (this._FBU.aten_len > 0) {
+ switch (this._FBU.aten_type) {
+ case 0: // Subrects
+ this._FBU.bytes = 6 + (16 * 16 * this._pixelFormat.Bpp); // at least a subrect
+ if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
+ var a = this._sock.rQshift16();
+ var b = this._sock.rQshift16();
+ var y = this._sock.rQshift8();
+ var x = this._sock.rQshift8();
+ this._convert_color(this._destBuff, this._sock.rQshiftBytes(this._FBU.bytes - 6));
+ this._display.blitImage(x * 16, y * 16, 16, 16, this._destBuff, 0, true, false);
+ this._FBU.aten_len -= this._FBU.bytes;
+ this._FBU.bytes = 0;
+ break;
+ case 1: // RAW
+ var olines = (this._FBU.lines === 0) ? this._FBU.height : this._FBU.lines;
+ this._encHandlers.RAW();
+ this._FBU.aten_len -= (olines - this._FBU.lines) * this._FBU.width * this._pixelFormat.Bpp;
+ if (this._FBU.bytes > 0) return false;
+ break;
+ default:
+ return this._fail('unknown ATEN type: '+this._FBU.aten_type);
+ }
+ }
+
+ if (this._FBU.aten_len < 0) {
+ this._fail('aten_len dropped below zero');
+ }
+
+ if (this._FBU.aten_type === 0) {
+ this._FBU.rects--;
+ }
+
+ this._FBU.aten_len = -1;
+ this._FBU.aten_type = -1;
+
+ return true;
}
};
})();
diff --git a/tests/test.rfb.js b/tests/test.rfb.js
index 0fa93f0..c481806 100644
--- a/tests/test.rfb.js
+++ b/tests/test.rfb.js
@@ -873,6 +873,51 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._rfb_state).to.equal('failed');
});
});
+
+ describe('ATEN iKVM Authentication Handler', function () {
+ var client;
+
+ beforeEach(function () {
+ client = make_rfb();
+ client.connect('host', 8675);
+ client._sock._websocket._open();
+ client._rfb_state = 'Security';
+ client._rfb_version = 3.8;
+ send_security(16, client);
+ client._sock._websocket._get_sent_data(); // skip the security reply
+ client._rfb_password = 'test1:test2';
+ });
+
+ var auth = [
+ 116, 101, 115, 116, 49, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 116, 101, 115, 116, 50, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0];
+
+ it('via old style method', function () {
+ client._sock._websocket._receive_data(new Uint8Array([
+ 0xaf, 0xf9, 0x0f, 0xb0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0]));
+ expect(client._rfb_tightvnc).to.be.false;
+ expect(client._rfb_atenikvm).to.be.true;
+ expect(client._sock).to.have.sent(auth);
+ });
+
+ it('via new style method', function () {
+ client._sock._websocket._receive_data(new Uint8Array([
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0]));
+ expect(client._rfb_tightvnc).to.be.false;
+ expect(client._rfb_atenikvm).to.be.true;
+ expect(client._sock).to.have.sent(auth);
+ });
+ });
});
describe('SecurityResult', function () {
@@ -1020,6 +1065,25 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._rfb_state).to.equal('normal');
});
+ it('should handle an ATEN iKVM server initialization', function () {
+ RFB.ATEN_INIT_WIDTH = 1;
+ RFB.ATEN_INIT_HEIGHT = 1;
+ client._rfb_atenikvm = true;
+ send_server_init({ true_color: 1, bpp: 32 }, client);
+ client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
+ expect(client._pixelFormat.bpp).to.equal(16);
+ expect(client._pixelFormat.depth).to.equal(15);
+ expect(client._pixelFormat.red_max).to.equal(31);
+ expect(client._pixelFormat.green_max).to.equal(31);
+ expect(client._pixelFormat.blue_max).to.equal(31);
+ expect(client._pixelFormat.red_shift).to.equal(10);
+ expect(client._pixelFormat.green_shift).to.equal(5);
+ expect(client._pixelFormat.blue_shift).to.equal(0);
+ expect(client._pixelFormat.Bpp).to.equal(2);
+ expect(client._pixelFormat.Bdepth).to.equal(2);
+ expect(client._rfb_state).to.equal('normal');
+ });
+
it('should call the resize callback and resize the display', function () {
client.set_onFBResize(sinon.spy());
sinon.spy(client._display, 'resize');
@@ -1562,6 +1626,97 @@ describe('Remote Frame Buffer Protocol Client', function() {
// TODO(directxman12): test this
});
+ describe('the ATEN encoding handler', function () {
+ var client;
+ beforeEach(function () {
+ client = make_rfb();
+ client.connect('host', 8675);
+ client._sock._websocket._open();
+ client._rfb_state = 'normal';
+ client._fb_name = 'some device';
+ client._rfb_atenikvm = true;
+ // start large, then go small
+ client._fb_width = 10000;
+ client._fb_height = 10000;
+ client._display._fb_width = 10000;
+ client._display._fb_height = 10000;
+ client._display._viewportLoc.w = 10000;
+ client._display._viewportLoc.h = 10000;
+ client._convertColor = true;
+ client._pixelFormat.Bpp = 2;
+ client._pixelFormat.big_endian = false;
+ client._pixelFormat.red_shift = 10;
+ client._pixelFormat.red_max = 31;
+ client._pixelFormat.green_shift = 5;
+ client._pixelFormat.green_max = 31;
+ client._pixelFormat.blue_shift = 0;
+ client._pixelFormat.blue_max = 31;
+ client._destBuff = new Uint8Array(client._fb_width * client._fb_height * 4);
+ });
+
+ var aten_target_data_arr = [0xa8, 0xe8, 0xf8, 0xff];
+ var aten_target_data;
+
+ before(function () {
+ for (var i = 0; i < 10; i++) {
+ aten_target_data_arr = aten_target_data_arr.concat(aten_target_data_arr);
+ }
+ aten_target_data = new Uint8Array(aten_target_data_arr);
+ });
+
+ it('should handle subtype subrect encoding', function () {
+ var info = [{ x: 0, y: 0, width: 32, height: 32, encoding: 0x59 }];
+ var rect = [];
+
+ rect.push32(0); // padding
+ rect.push32(2082); // 10 + 32x32x2Bpp + 6*(num of subrects)
+
+ rect.push8(0); // type
+ rect.push8(0); // padding
+
+ rect.push32(4); // num of subrects (32/16)*(32/16)
+ rect.push32(2082); // length (again)
+
+ for (var y = 0; y < 2; y++) {
+ for (var x = 0; x < 2; x++) {
+ rect.push16(0); // a
+ rect.push16(0); // b
+ rect.push8(y);
+ rect.push8(x);
+
+ for (var i = 0; i < 16*16; i++) {
+ rect.push16(0xbf57);
+ }
+ }
+ }
+
+ send_fbu_msg(info, [rect], client);
+ expect(client._display).to.have.displayed(aten_target_data);
+ });
+
+ it('should handle subtype RAW encoding', function () {
+ // do not use encoding=0x59 here, as rfb.js should override it
+ var info = [{ x: 0, y: 0, width: 32, height: 32, encoding: 0x00 }];
+ var rect = [];
+
+ rect.push32(0); // padding
+ rect.push32(2058); // 10 + 32x32x2Bpp
+
+ rect.push8(1); // type
+ rect.push8(0); // padding
+
+ rect.push32(0); // padding
+ rect.push32(2058); // length (again)
+
+ for (var i = 0; i < 32*32; i++) {
+ rect.push16(0xbf57);
+ }
+
+ send_fbu_msg(info, [rect], client);
+ expect(client._display).to.have.displayed(aten_target_data);
+ });
+ });
+
it('should handle the DesktopSize pseduo-encoding', function () {
client.set_onFBResize(sinon.spy());
sinon.spy(client._display, 'resize');