Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
		
							parent
							
								
									eadf0cc8de
								
							
						
					
					
						commit
						11755b911c
					
				
							
								
								
									
										688
									
								
								js/x11/core/examples/vncviewer/rfbclient.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								js/x11/core/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