Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
f9e46c2706
commit
c0593d1b21
688
js/x11/examples/vncviewer/rfbclient.js
Normal file
688
js/x11/examples/vncviewer/rfbclient.js
Normal file
|
@ -0,0 +1,688 @@
|
|||
"use strict";
|
||||
|
||||
//var clog = clog;
|
||||
//var clog = function() {};
|
||||
var clog = console.log;
|
||||
|
||||
var util = require('util'); // util.inherits
|
||||
var net = require('net');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var PackStream = require('./unpackstream');
|
||||
var hexy = require('./hexy').hexy;
|
||||
|
||||
// constants
|
||||
var rfb = require('./constants');
|
||||
for (var key in rfb)
|
||||
{
|
||||
module.exports[key] = rfb[key];
|
||||
}
|
||||
|
||||
|
||||
function RfbClient(stream, params)
|
||||
{
|
||||
EventEmitter.call(this);
|
||||
this.params = params;
|
||||
var cli = this;
|
||||
cli.stream = stream;
|
||||
cli.pack_stream = new PackStream();
|
||||
cli.pack_stream.on('data', function( data ) {
|
||||
//clog(hexy(data, {prefix: 'from client '}));
|
||||
cli.stream.write(data);
|
||||
});
|
||||
stream.on('data', function( data ) {
|
||||
//var dump = data.length > 20 ? data.slice(0,20) : data;
|
||||
//clog(hexy(dump, {prefix: 'from server '}));
|
||||
cli.pack_stream.write(data);
|
||||
});
|
||||
|
||||
// TODO: check if I need that at all
|
||||
cli.pack_stream.serverBigEndian = !true;
|
||||
cli.pack_stream.clientBigEndian = !true;
|
||||
cli.readServerVersion();
|
||||
}
|
||||
util.inherits(RfbClient, EventEmitter);
|
||||
|
||||
PackStream.prototype.readString = function(strcb)
|
||||
{
|
||||
var stream = this;
|
||||
stream.unpack('L', function(res) {
|
||||
//clog(res[0]);
|
||||
stream.get(res[0], function(strBuff) {
|
||||
strcb(strBuff.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.terminate = function()
|
||||
{
|
||||
debugger;
|
||||
this.stream.end();
|
||||
}
|
||||
|
||||
RfbClient.prototype.readError = function()
|
||||
{
|
||||
var cli = this;
|
||||
this.pack_stream.readString(function(str) {
|
||||
console.error(str);
|
||||
cli.emit('error', str);
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readServerVersion = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
stream.get(12, function(rfbver) {
|
||||
cli.serverVersion = rfbver.toString('ascii');
|
||||
console.log(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', rfbver]);
|
||||
stream.pack('a', [ rfb.versionstring.V3_008 ]).flush();
|
||||
if (cli.serverVersion == rfb.versionstring.V3_003)
|
||||
{
|
||||
stream.unpack('L', function(secType) {
|
||||
var type = secType[0];
|
||||
console.error('3.003 security type: ' + type);
|
||||
if (type == 0)
|
||||
{
|
||||
cli.readError();
|
||||
} else {
|
||||
cli.securityType = type;
|
||||
// 3.003 version does not send result for None security
|
||||
if (type == rfb.security.None)
|
||||
cli.clientInit();
|
||||
else
|
||||
cli.processSecurity();
|
||||
}
|
||||
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// read security types
|
||||
stream.unpack('C', function(res) {
|
||||
var numSecTypes = res[0];
|
||||
if (numSecTypes == 0) {
|
||||
console.error(['zero num sec types', res]);
|
||||
cli.readError();
|
||||
} else {
|
||||
|
||||
stream.get(numSecTypes, function(secTypes) {
|
||||
// TODO: check what is in options
|
||||
//
|
||||
// send sec type we are going to use
|
||||
//cli.securityType = rfb.security.None;
|
||||
cli.securityType = rfb.security.VNC;
|
||||
stream.pack('C', [cli.securityType]).flush();
|
||||
cli.processSecurity();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readSecurityResult = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
stream.unpack('L', function(securityResult) {
|
||||
if (securityResult[0] == 0)
|
||||
{
|
||||
cli.clientInit();
|
||||
} else {
|
||||
stream.readString(function(message) {
|
||||
console.error(message);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.processSecurity = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
switch(cli.securityType) {
|
||||
case rfb.security.None:
|
||||
// do nothing
|
||||
cli.readSecurityResult();
|
||||
break;
|
||||
case rfb.security.VNC:
|
||||
stream.get(16, function(challenge) {
|
||||
var response = require('./d3des').response(challenge, cli.params.password);
|
||||
stream.pack('a', [response]).flush();
|
||||
cli.readSecurityResult();
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error('unknown security type: ' + cli.securityType);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
RfbClient.prototype.clientInit = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
var initMessage = cli.disconnectOthers ? rfb.connectionFlag.Exclusive : rfb.connectionFlag.Shared;
|
||||
stream.pack('C', [ initMessage ]).flush();
|
||||
|
||||
stream.unpackTo(
|
||||
cli,
|
||||
[
|
||||
"S width",
|
||||
"S height",
|
||||
"C bpp", // 16-bytes pixel format
|
||||
"C depth",
|
||||
"C isBigEndian",
|
||||
"C isTrueColor",
|
||||
"S redMax",
|
||||
"S greenMax",
|
||||
"S blueMax",
|
||||
"C redShift",
|
||||
"C greenShift",
|
||||
"C blueShift",
|
||||
"xxx",
|
||||
"L titleLength"
|
||||
],
|
||||
|
||||
function() {
|
||||
|
||||
|
||||
// TODO: remove next 3 lines
|
||||
stream.serverBigEndian = false; //cli.isBigEndian;
|
||||
stream.clientBigEndian = false; //cli.isBigEndian;
|
||||
//stream.bigEndian = false; //cli.isBigEndian;
|
||||
|
||||
stream.get(cli.titleLength, function(titleBuf) {
|
||||
cli.title = titleBuf.toString();
|
||||
delete cli.titleLength;
|
||||
cli.setPixelFormat();
|
||||
});
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
RfbClient.prototype.setPixelFormat = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
stream.pack('CxxxCCCCSSSCCCxxx',
|
||||
[0, cli.bpp, cli.depth, cli.isBigEndian, cli.isTrueColor, cli.redMax, cli.greenMax, cli.blueMax,
|
||||
cli.redShift, cli.greenShift, cli.blueShift]
|
||||
);
|
||||
stream.flush();
|
||||
cli.setEncodings();
|
||||
}
|
||||
|
||||
function repeat(str, num)
|
||||
{
|
||||
var res = '';
|
||||
for (var i=0; i < num; ++i)
|
||||
res += str;
|
||||
return res;
|
||||
}
|
||||
|
||||
RfbClient.prototype.setEncodings = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
// build encodings list
|
||||
// TODO: API
|
||||
var encodings = [rfb.encodings.raw, rfb.encodings.copyRect, rfb.encodings.pseudoDesktopSize, rfb.encodings.hextile];
|
||||
|
||||
stream.pack('CxS', [rfb.clientMsgTypes.setEncodings, encodings.length]);
|
||||
stream.pack(repeat('l', encodings.length), encodings);
|
||||
stream.flush();
|
||||
|
||||
cli.requestUpdate(false, 0, 0, cli.width, cli.height);
|
||||
cli.expectNewMessage();
|
||||
console.log('handshake performed');
|
||||
this.emit('connect');
|
||||
console.log('emitted CONNECT');
|
||||
}
|
||||
|
||||
RfbClient.prototype.expectNewMessage = function()
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
stream.get(1, function(buff) {
|
||||
console.log('server message:' + buff[0]);
|
||||
switch(buff[0]) {
|
||||
case rfb.serverMsgTypes.fbUpdate: cli.readFbUpdate(); break;
|
||||
case rfb.serverMsgTypes.setColorMap: cli.readColorMap(); break;
|
||||
case rfb.serverMsgTypes.bell: cli.readBell(); break;
|
||||
case rfb.serverMsgTypes.cutText: cli.readClipboardUpdate(); break;
|
||||
default:
|
||||
clog('unsopported server message: ' + buff[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var decodeHandlers = {
|
||||
};
|
||||
|
||||
RfbClient.prototype.readFbUpdate = function()
|
||||
{
|
||||
clog('fb update');
|
||||
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
stream.unpack('xS', function(res) {
|
||||
var numRects = res[0];
|
||||
// decode each rectangle
|
||||
var numRectsLeft = numRects;
|
||||
clog('number of rectngles in fb updte message: ' + numRects);
|
||||
function unpackRect() {
|
||||
if (numRectsLeft == 0)
|
||||
{
|
||||
cli.expectNewMessage();
|
||||
cli.requestUpdate(true, 0, 0, cli.width, cli.height);
|
||||
return;
|
||||
}
|
||||
numRectsLeft--;
|
||||
|
||||
var rect = {};
|
||||
stream.unpackTo(rect,
|
||||
['S x', 'S y', 'S width', 'S height', 'l encoding'],
|
||||
function() {
|
||||
|
||||
// TODO: rewrite using decodeHandlers
|
||||
switch(rect.encoding) {
|
||||
case rfb.encodings.raw:
|
||||
cli.readRawRect(rect, unpackRect);
|
||||
break;
|
||||
case rfb.encodings.copyRect:
|
||||
cli.readCopyRect(rect, unpackRect);
|
||||
break;
|
||||
case rfb.encodings.pseudoDesktopSize:
|
||||
clog(['Resize', rect]);
|
||||
cli.width = rect.width;
|
||||
cli.height = rect.height;
|
||||
cli.emit('resize', rect);
|
||||
unpackRect();
|
||||
break;
|
||||
case rfb.encodings.hextile:
|
||||
cli.readHextile(rect, unpackRect);
|
||||
break;
|
||||
default:
|
||||
clog('unknown encoding!!! ' + rect.encoding);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
unpackRect();
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readHextile = function(rect, cb)
|
||||
{
|
||||
rect.emitter = new EventEmitter();
|
||||
rect.on = function(eventname, cb) {
|
||||
rect.emitter.on(eventname, cb);
|
||||
}
|
||||
rect.emit = function(eventname, param) {
|
||||
rect.emitter.emit(eventname, param);
|
||||
}
|
||||
|
||||
rect.widthTiles = (rect.width >>> 4);
|
||||
rect.heightTiles = (rect.height >>> 4);
|
||||
clog(['tiles: ', rect.widthTiles, rect.heightTiles]);
|
||||
rect.rightRectWidth = rect.width & 0x0f;
|
||||
rect.bottomRectHeight = rect.height & 0x0f;
|
||||
rect.tilex = 0;
|
||||
rect.tiley = 0;
|
||||
rect.tiles = [];
|
||||
console.log('===== emitting rect');
|
||||
this.emit('rect', rect);
|
||||
this.readHextileTile(rect, cb);
|
||||
}
|
||||
|
||||
RfbClient.prototype.readHextileTile = function(rect, cb)
|
||||
{
|
||||
var tile = {};
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
tile.x = rect.tilex;
|
||||
tile.y = rect.tiley;
|
||||
tile.width = 16;
|
||||
if (tile.x == rect.widthTiles && rect.rightRectWidth > 0)
|
||||
tile.width = rect.rightRectWidt;
|
||||
tile.height = 16;
|
||||
if (tile.y == rect.heightTiles && rect.bottomRectHeight > 0)
|
||||
tile.height = rect.bottomRectHeight;
|
||||
|
||||
// calculate next tilex & tiley and move up 'stack' if we at the last tile
|
||||
function nextTile()
|
||||
{
|
||||
clog('nextTile');
|
||||
rect.emit('tile', tile);
|
||||
tile = {};
|
||||
if (rect.tilex < rect.widthTiles)
|
||||
{
|
||||
rect.tilex++;
|
||||
//clog([rect.tilex, rect.tiley]);
|
||||
return cli.readHextileTile(rect, cb);
|
||||
} else {
|
||||
clog('===================== new row! ' + rect.tiley);
|
||||
rect.tilex = 0;
|
||||
if (rect.tiley < rect.heightTiles)
|
||||
{
|
||||
rect.tiley++;
|
||||
//clog([rect.tilex, rect.tiley]);
|
||||
return cli.readHextileTile(rect, cb);
|
||||
} else {
|
||||
clog('====================')
|
||||
clog(rect);
|
||||
return cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bytesPerPixel = cli.bpp >> 3;
|
||||
console.log('bytesPerPixel: ' + bytesPerPixel);
|
||||
var tilebuflen = bytesPerPixel*tile.width*tile.height;
|
||||
stream.unpack('C', function(subEnc) {
|
||||
clog('tile flags: ' + subEnc[0]);
|
||||
tile.subEncoding = subEnc[0];
|
||||
var hextile = rfb.subEncodings.hextile;
|
||||
if (tile.subEncoding & hextile.raw) {
|
||||
stream.get(tilebuflen, function(rawbuff)
|
||||
{
|
||||
clog('raw tile');
|
||||
tile.buffer = rawbuff;
|
||||
nextTile();
|
||||
});
|
||||
return;
|
||||
}
|
||||
tile.buffer = new Buffer(tilebuflen);
|
||||
|
||||
function solidBackground() {
|
||||
clog('solidBackground');
|
||||
// the whole tile is just single colored width x height
|
||||
for (var i=0; i < tilebuflen; i+= bytesPerPixel)
|
||||
tile.backgroundColor.copy(tile.buffer, i);
|
||||
}
|
||||
|
||||
function readBackground() {
|
||||
clog('readBackground');
|
||||
if (tile.subEncoding & hextile.backgroundSpecified) {
|
||||
clog('hextile.backgroundSpecified');
|
||||
stream.get(bytesPerPixel, function(pixelValue)
|
||||
{
|
||||
clog(['tile.backgroundColor', pixelValue, tile.subEncoding]);
|
||||
tile.backgroundColor = pixelValue;
|
||||
rect.backgroundColor = pixelValue;
|
||||
readForeground();
|
||||
});
|
||||
} else {
|
||||
tile.backgroundColor = rect.backgroundColor;
|
||||
readForeground();
|
||||
}
|
||||
}
|
||||
|
||||
function readForeground() {
|
||||
clog('readForeground');
|
||||
// we should have background color set here
|
||||
solidBackground();
|
||||
if (rect.subEncoding & hextile.foregroundSpecified) {
|
||||
clog('foreground specified');
|
||||
stream.get(bytesPerPixel, function(pixelValue)
|
||||
{
|
||||
tile.foreroundColor = pixelValue;
|
||||
rect.foreroundColor = pixelValue;
|
||||
console.log(rect);
|
||||
readSubrects();
|
||||
});
|
||||
} else {
|
||||
clog('foreground NOT specified');
|
||||
clog(rect);
|
||||
tile.foregroundColor = rect.foregroundColor;
|
||||
readSubrects();
|
||||
}
|
||||
}
|
||||
|
||||
function readSubrects() {
|
||||
clog('readSubrects');
|
||||
if (tile.subEncoding & hextile.anySubrects) {
|
||||
clog('have subrects');
|
||||
// read number of subrectangles
|
||||
stream.get('C', function(subrectsNum) {
|
||||
tile.subrectsNum = subrectsNum[0];
|
||||
clog('number of subrects = ' + tile.subrectsNum);
|
||||
readSubrect();
|
||||
});
|
||||
} else {
|
||||
nextTile();
|
||||
}
|
||||
}
|
||||
|
||||
function drawRect(x, y, w, h)
|
||||
{
|
||||
console.log(tile);
|
||||
console.log(['drawRect', x, y, w, h, tile.foregroundColor]);
|
||||
// TODO: optimise
|
||||
for(var px = x; px < x+w; ++px)
|
||||
{
|
||||
for(var py = x; py < y+h; ++py)
|
||||
{
|
||||
var offset = bytesPerPixel*(tile.width*py + px);
|
||||
tile.foregroundColor.copy(tile.buffer, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readSubrect() {
|
||||
clog('readSubrect');
|
||||
if (tile.subEncoding & hextile.subrectsColored) {
|
||||
// we have color + rect data
|
||||
stream.get(bytesPerPixel, function(pixelValue)
|
||||
{
|
||||
clog(['coloredSubrect: ', pixelValue]);
|
||||
tile.foreroundColor = pixelValue;
|
||||
rect.foreroundColor = pixelValue;
|
||||
readSubrectRect();
|
||||
});
|
||||
} else // we have just rect data
|
||||
readSubrectRect();
|
||||
}
|
||||
|
||||
function readSubrectRect() {
|
||||
clog('readSubrectRect');
|
||||
// read subrect x y w h encoded in two bytes
|
||||
stream.get(2, function(subrectRaw) {
|
||||
var x = (subrectRaw[0] & 0xf0) >> 4;
|
||||
var y = (subrectRaw[0] & 0x0f);
|
||||
var width = (subrectRaw[1] & 0xf0) >> 4 + 1;
|
||||
var height = (subrectRaw[1] & 0x0f) + 1;
|
||||
clog(['readSubrectRect', x, y, width, height, tile.subrectsNum]);
|
||||
drawRect(x, y, width, height);
|
||||
tile.subrectsNum--;
|
||||
|
||||
if (tile.subrectsNum === 0)
|
||||
{
|
||||
nextTile();
|
||||
} else
|
||||
readSubrect();
|
||||
});
|
||||
}
|
||||
|
||||
readBackground();
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readCopyRect = function(rect, cb)
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
stream.unpack('SS', function(src) {
|
||||
rect.src = { x: src[0], y: src[1] };
|
||||
clog(['copy rect', src, rect]);
|
||||
cli.emit('rect', rect);
|
||||
cb(rect);
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readRawRect = function(rect, cb)
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
var bytesPerPixel = cli.bpp >> 3;
|
||||
stream.get(bytesPerPixel*rect.width*rect.height, function(rawbuff)
|
||||
{
|
||||
//clog('arrived ' + rawbuff.length + ' bytes of fb update');
|
||||
rect.buffer = rawbuff;
|
||||
cli.emit('rect', rect);
|
||||
cb(rect);
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.readColorMap = function()
|
||||
{
|
||||
clog('color map');
|
||||
}
|
||||
|
||||
RfbClient.prototype.readBell = function()
|
||||
{
|
||||
clog('bell');
|
||||
this.expectNewMessage();
|
||||
}
|
||||
|
||||
RfbClient.prototype.readClipboardUpdate = function()
|
||||
{
|
||||
clog('clipboard update');
|
||||
var stream = this.pack_stream;
|
||||
var cli = this;
|
||||
|
||||
stream.unpack('xxxL', function(res) {
|
||||
clog(res[0] + ' bytes string in the buffer');
|
||||
stream.get(res[0], function(buf) {
|
||||
clog(buf.toString());
|
||||
cli.expectNewMessage();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
RfbClient.prototype.pointerEvent = function(x, y, buttons)
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
|
||||
stream.pack('CCSS', [rfb.clientMsgTypes.pointerEvent, buttons, x, y]);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
RfbClient.prototype.keyEvent = function(keysym, isDown)
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
|
||||
stream.pack('CCxxL', [rfb.clientMsgTypes.keyEvent, isDown, keysym]);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
RfbClient.prototype.requestUpdate = function(incremental, x, y, width, height)
|
||||
{
|
||||
var stream = this.pack_stream;
|
||||
stream.pack('CCSSSS', [rfb.clientMsgTypes.fbUpdate, incremental, x, y, width, height]);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
// TODO: add client cutText event!
|
||||
|
||||
|
||||
|
||||
var fs = require('fs');
|
||||
function createRfbStream(name)
|
||||
{
|
||||
var stream = new EventEmitter();
|
||||
var fileStream = fs.createReadStream(name);
|
||||
var pack = new PackStream();
|
||||
fileStream.on('data', function( data ) {
|
||||
//fileStream.pause();
|
||||
//setTimeout(function() {
|
||||
pack.write(data);
|
||||
//clog('received from filestream:' + data.length);
|
||||
//fileStream.resume();
|
||||
//}, 10);
|
||||
});
|
||||
|
||||
var start = +new Date();
|
||||
function readData()
|
||||
{
|
||||
pack.unpack('L', function(size) {
|
||||
pack.get(size[0], function(databuf) {
|
||||
pack.unpack('L', function(timestamp) {
|
||||
var padding = 3 - ((size - 1) & 0x03);
|
||||
pack.get(padding, function() {
|
||||
if (!stream.ending) {
|
||||
stream.emit('data', databuf);
|
||||
var now = +new Date - start;
|
||||
var timediff = timestamp[0] - now;
|
||||
stream.timeout = setTimeout(readData, timediff);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pack.get(12, function(fileVersion) {
|
||||
readData();
|
||||
});
|
||||
|
||||
stream.end = function() {
|
||||
stream.ending = true;
|
||||
if (stream.timeout)
|
||||
clearTimeout(stream.timeout);
|
||||
};
|
||||
|
||||
stream.write = function(buf) {
|
||||
// ignore
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
function createConnection(params)
|
||||
{
|
||||
var stream;
|
||||
if (params.rfbfile)
|
||||
{
|
||||
console.log('reading from ' + params.rfbfile);
|
||||
stream = createRfbStream(params.rfbfile);
|
||||
}
|
||||
else {
|
||||
console.log('connecting to ' + params.host + ':' + params.port);
|
||||
stream = net.createConnection(params.port, params.host);
|
||||
}
|
||||
|
||||
if (params.rfbFileOut)
|
||||
{
|
||||
var start = +new Date();
|
||||
var wstream = fs.createWriteStream(params.rfbFileOut);
|
||||
wstream.write('FBS 001.001\n');
|
||||
stream.on('data', function(data) {
|
||||
var sizeBuf = new Buffer(4);
|
||||
var timeBuf = new Buffer(4);
|
||||
var size = data.length;
|
||||
sizeBuf.writeInt32BE(size, 0);
|
||||
wstream.write(sizeBuf);
|
||||
wstream.write(data);
|
||||
timeBuf.writeInt32BE(+new Date() - start, 0);
|
||||
wstream.write(timeBuf);
|
||||
var padding = 3 - ((size - 1) & 0x03);
|
||||
var pbuf = new Buffer(padding);
|
||||
wstream.write(pbuf);
|
||||
}).on('end', function() {
|
||||
wstream.end();
|
||||
});
|
||||
}
|
||||
|
||||
return new RfbClient(stream, params);
|
||||
}
|
||||
exports.createConnection = createConnection;
|
Loading…
Reference in New Issue
Block a user