diff --git a/js/x11/core/handshake.js b/js/x11/core/handshake.js new file mode 100644 index 0000000..bd8ecc1 --- /dev/null +++ b/js/x11/core/handshake.js @@ -0,0 +1,231 @@ +var getAuthString = Require('x11/core/auth'); +var xutil = Require('x11/core/xutil'); + +function readVisuals(bl, visuals, n_visuals, cb) +{ + if (n_visuals == 0) + { + cb(); + return; + } + + var visual = {}; + bl.unpackTo( visual, + [ + 'L vid', + 'C class', + 'C bits_per_rgb', + 'S map_ent', + 'L red_mask', + 'L green_mask', + 'L blue_mask', + 'xxxx' + ], + function() { + var vid = visual.vid; + // delete visual.vid; + visuals[visual.vid] = visual; + if (Object.keys(visuals).length == n_visuals) + cb() + else + readVisuals(bl, visuals, n_visuals, cb); + }); +} + +function readScreens(bl, display, cbDisplayReady) +{ + var numParsedDepths = 0; + var readDepths = function(bl, display, depths, n_depths, cb) + { + if (n_depths == 0) + { + cb(); + return; + } + + bl.unpack( 'CxSxxxx', function(res) { + var dep = res[0]; + var n_visuals = res[1]; + var visuals = {}; + readVisuals(bl, visuals, n_visuals, function() + { + if (dep in depths) { + for (var visual in visuals) { + depths[dep][visual] = visuals[visual]; + } + } else { + depths[dep] = visuals; + } + numParsedDepths++; + if (numParsedDepths == n_depths) + cb(); + else + readDepths(bl, display, depths, n_depths, cb); + }); + }); + } + + // for (i=0; i < display.screen_num; ++i) + { + var scr = {}; + bl.unpackTo( scr, + [ + 'L root', + 'L default_colormap', + 'L white_pixel', + 'L black_pixel', + 'L input_masks', + 'S pixel_width', + 'S pixel_height', + 'S mm_width', + 'S mm_height', + 'S min_installed_maps', + 'S max_installed_maps', + 'L root_visual', + 'C root_depth', + 'C backing_stores', + 'C root_depth', + 'C num_depths' + ], + function () { + var depths = {}; + readDepths(bl, display, depths, scr.num_depths, function() { + + scr.depths = depths; + delete scr.num_depths; + display.screen.push(scr); + + if (display.screen.length == display.screen_num) + { + delete display.screen_num; + cbDisplayReady(null, display); + return; + } else { + readScreens(bl, display, cbDisplayReady); + } + }); + }); + } +} + +function readServerHello(bl, cb) +{ + +bl.unpack('C', function(res) { + + if (res[0] == 0) + { + // conection time error + // unpack error + bl.unpack('Cxxxxxx', function (rlen) { + bl.get(rlen[0], function (reason) { + var err = new Error; + err.message = 'X server connection failed: ' + reason.toString(); + cb(err); + }); + }); + // TODO: do we need to close stream from our side? + // TODO: api to close source stream via attached unpackstream + return; + } + + var display = {}; + bl.unpackTo( + display, + [ + 'x', + 'S major', + 'S minor', + 'S xlen', + 'L release', + 'L resource_base', + 'L resource_mask', + 'L motion_buffer_size', + 'S vlen', + 'S max_request_length', + 'C screen_num', + 'C format_num', + 'C image_byte_order', + 'C bitmap_bit_order', + 'C bitmap_scanline_unit', + 'C bitmap_scanline_pad', + 'C min_keycode', + 'C max_keycode', + 'xxxx' + ], + + function() + { + var pvlen = xutil.padded_length(display.vlen); + + // setup data to generate resource id + // TODO: cleaunup code here + var mask = display.resource_mask; + display.rsrc_shift = 0; + while (!( (mask >> display.rsrc_shift) & 1) ) + display.rsrc_shift++; + display.rsrc_id = 0; + + bl.get(pvlen, function(vendor) + { + display.vendor = vendor.toString().substr(0, display.vlen); // utf8 by default? + + display.format = {}; + for (var i=0; i < display.format_num; ++i) + { + bl.unpack('CCCxxxxx', function(fmt) { + var depth = fmt[0]; + display.format[depth] = {}; + display.format[depth].bits_per_pixel = fmt[1]; + display.format[depth].scanline_pad = fmt[2]; + if (Object.keys(display.format).length == display.format_num) + { + delete display.format_num; + display.screen = []; + readScreens(bl, display, cb); + } + }); + } + }); + } + ); +}); + +} + +function getByteOrder() { + var isLittleEndian = ((new Uint32Array((new Uint8Array([1,2,3,4])).buffer))[0] === 0x04030201); + if (isLittleEndian) { + return 'l'.charCodeAt(0); + } else { + return 'B'.charCodeAt(0); + } +} + +function writeClientHello(stream, displayNum, authHost, authFamily) +{ + getAuthString( displayNum, authHost, authFamily, function( err, cookie ) { + if (err) { + throw err; + } + var byte_order = getByteOrder(); + var protocol_major = 11; // TODO: config? env? + var protocol_minor = 0; + stream.pack( + 'CxSSSSxxpp', + [ + byte_order, + protocol_major, + protocol_minor, + cookie.authName.length, + cookie.authData.length, + cookie.authName, + cookie.authData + ] + ); + stream.flush(); + }); +} + +module.exports.readServerHello = readServerHello; +module.exports.writeClientHello = writeClientHello;