Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
10d19a4595
commit
c4ed984325
688
js/x11/core/xcore.js
Normal file
688
js/x11/core/xcore.js
Normal file
|
@ -0,0 +1,688 @@
|
|||
var util = Require('util'); // util.inherits
|
||||
var net = Require('net');
|
||||
|
||||
var handshake = Require('x11/core/handshake');
|
||||
//var xevents = require('./xevents');
|
||||
|
||||
var EventEmitter = Require('events').EventEmitter;
|
||||
var PackStream = Require('x11/core/unpackstream');
|
||||
var hexy = Require('x11/core/hexy').hexy;
|
||||
|
||||
var Buffer = Require('buffer').Buffer;
|
||||
// add 'unpack' method for buffer
|
||||
Require('x11/core/unpackbuffer').addUnpack(Buffer);
|
||||
|
||||
var os = require('os');
|
||||
|
||||
var xerrors = Require('x11/core/xerrors');
|
||||
var coreRequests = Require('x11/core/corereqs');
|
||||
var stdatoms = Require('x11/core/stdatoms');
|
||||
var em = Require('x11/core/eventmask').eventMask;
|
||||
|
||||
function stash ()
|
||||
{
|
||||
Require('x11/core/ext/apple-wm');
|
||||
Require('x11/core/ext/big-requests');
|
||||
Require('x11/core/ext/composite');
|
||||
Require('x11/core/ext/damage');
|
||||
Require('x11/core/ext/dpms');
|
||||
Require('x11/core/ext/fixes');
|
||||
Require('x11/core/ext/glxconstants');
|
||||
Require('x11/core/ext/glx');
|
||||
Require('x11/core/ext/glxrender');
|
||||
Require('x11/core/ext/randr');
|
||||
Require('x11/core/ext/render');
|
||||
Require('x11/core/ext/screen-saver');
|
||||
Require('x11/core/ext/shape');
|
||||
Require('x11/core/ext/xc-misc');
|
||||
Require('x11/core/ext/xtest');
|
||||
}
|
||||
if (process.argv.indexOf('-buildx11')!=-1) stash();
|
||||
|
||||
function XClient(displayNum, screenNum, options)
|
||||
{
|
||||
EventEmitter.call(this);
|
||||
this.options = options ? options : {};
|
||||
|
||||
// TODO: this is probably not used
|
||||
this.core_requests = {};
|
||||
this.ext_requests = {};
|
||||
|
||||
this.displayNum = displayNum;
|
||||
this.screenNum = screenNum;
|
||||
}
|
||||
util.inherits(XClient, EventEmitter);
|
||||
|
||||
XClient.prototype.init = function(stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
|
||||
this.authHost = stream.remoteAddress;
|
||||
// Node v0.10.x does not have stream.remoteFamily, so dig in to find it
|
||||
this.authFamily = stream._getpeername ? stream._getpeername().family : stream.remoteFamily;
|
||||
if (!this.authHost || this.authHost === '127.0.0.1' || this.authHost === '::1') {
|
||||
this.authHost = os.hostname();
|
||||
this.authFamily = null;
|
||||
}
|
||||
|
||||
var pack_stream = new PackStream();
|
||||
|
||||
// data received from stream is dispached to
|
||||
// read requests set by calls to .unpack and .unpackTo
|
||||
//stream.pipe(pack_stream);
|
||||
|
||||
// pack_stream write requests are buffered and
|
||||
// flushed to stream as result of call to .flush
|
||||
// TODO: listen for drain event and flush automatically
|
||||
//pack_stream.pipe(stream);
|
||||
var client = this;
|
||||
pack_stream.on('data', function( data ) {
|
||||
//console.error(hexy(data, {prefix: 'from packer '}));
|
||||
//for (var i=0; i < data.length; ++i)
|
||||
// console.log('<<< ' + data[i]);
|
||||
stream.write(data);
|
||||
});
|
||||
stream.on('data', function( data ) {
|
||||
//console.error(hexy(data, {prefix: 'to unpacker '}));
|
||||
//for (var i=0; i < data.length; ++i)
|
||||
// console.log('>>> ' + data[i]);
|
||||
pack_stream.write(data);
|
||||
});
|
||||
stream.on('end', function() {
|
||||
client.emit('end');
|
||||
});
|
||||
|
||||
this.pack_stream = pack_stream;
|
||||
|
||||
this.rsrc_id = 0; // generated for each new resource
|
||||
var cli = this;
|
||||
if (cli.options.debug) {
|
||||
this.seq_num_ = 0;
|
||||
this.seq2stack = {}; // debug: map seq_num to stack at the moment request was issued
|
||||
Object.defineProperty(cli, "seq_num", {
|
||||
set : function(v) {
|
||||
cli.seq_num_ = v;
|
||||
var err = new Error();
|
||||
Error.captureStackTrace(err, arguments.callee);
|
||||
err.timestamp = Date.now();
|
||||
cli.seq2stack[client.seq_num] = err;
|
||||
},
|
||||
get: function() {
|
||||
return cli.seq_num_;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.seq_num = 0;
|
||||
}
|
||||
|
||||
|
||||
// in/out packets indexed by sequence ID
|
||||
this.replies = {};
|
||||
this.atoms = stdatoms;
|
||||
this.atom_names = (function() {
|
||||
var names = {};
|
||||
Object.keys(stdatoms).forEach(function(key) {
|
||||
names[stdatoms[key]] = key;
|
||||
});
|
||||
|
||||
return names;
|
||||
})();
|
||||
|
||||
this.eventMask = em;
|
||||
|
||||
this.event_consumers = {}; // maps window id to eventemitter TODO: bad name
|
||||
this.eventParsers = {};
|
||||
this.errorParsers = {};
|
||||
this._extensions = {};
|
||||
|
||||
this.importRequestsFromTemplates(this, coreRequests);
|
||||
|
||||
this.startHandshake();
|
||||
this._closing = false;
|
||||
this._unusedIds = [];
|
||||
}
|
||||
|
||||
// TODO: close() = set 'closing' flag, watch it in replies and writeQueue, terminate if empty
|
||||
XClient.prototype.terminate = function()
|
||||
{
|
||||
this.stream.end();
|
||||
}
|
||||
|
||||
// GetAtomName used as cheapest non-modifying request with reply
|
||||
// 3 - id for shortest standard atom, "ARC"
|
||||
XClient.prototype.ping = function(cb) {
|
||||
var start = Date.now();
|
||||
this.GetAtomName(3, function(err, str) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, Date.now() - start);
|
||||
});
|
||||
}
|
||||
|
||||
XClient.prototype.close = function(cb) {
|
||||
var cli = this;
|
||||
cli.ping(function(err) {
|
||||
if (err) return cb(err);
|
||||
cli.terminate();
|
||||
if (cb) cb();
|
||||
});
|
||||
cli._closing = true;
|
||||
}
|
||||
|
||||
XClient.prototype.importRequestsFromTemplates = function(target, reqs)
|
||||
{
|
||||
var client = this;
|
||||
this.pending_atoms = {};
|
||||
for (var r in reqs)
|
||||
{
|
||||
// r is request name
|
||||
target[r] = (function(reqName) {
|
||||
|
||||
var reqFunc = function req_proxy() {
|
||||
|
||||
if (client._closing)
|
||||
throw new Error('client is in closing state');
|
||||
|
||||
// simple overflow handling (this means that currently there is no way to have more than 65535 requests in the queue
|
||||
// TODO: edge cases testing
|
||||
if (client.seq_num == 65535)
|
||||
client.seq_num = 0;
|
||||
else
|
||||
client.seq_num++;
|
||||
|
||||
// is it fast?
|
||||
var args = Array.prototype.slice.call(req_proxy.arguments);
|
||||
|
||||
var callback = args.length > 0 ? args[args.length - 1] : null;
|
||||
if (callback && callback.constructor.name != 'Function')
|
||||
callback = null;
|
||||
|
||||
// TODO: see how much we can calculate in advance (not in each request)
|
||||
var reqReplTemplate = reqs[reqName];
|
||||
var reqTemplate = reqReplTemplate[0];
|
||||
var templateType = typeof reqTemplate;
|
||||
|
||||
if (templateType == 'object')
|
||||
templateType = reqTemplate.constructor.name;
|
||||
|
||||
if (templateType == 'function')
|
||||
{
|
||||
if (reqName === 'InternAtom') {
|
||||
var value = req_proxy.arguments[1];
|
||||
if (client.atoms[value]) {
|
||||
-- client.seq_num;
|
||||
return setImmediate(function() {
|
||||
callback(undefined, client.atoms[value]);
|
||||
});
|
||||
} else {
|
||||
client.pending_atoms[client.seq_num] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// call template with input arguments (not including callback which is last argument TODO currently with callback. won't hurt)
|
||||
//reqPack = reqTemplate.call(args);
|
||||
var reqPack = reqTemplate.apply(this, req_proxy.arguments);
|
||||
var format = reqPack[0];
|
||||
var requestArguments = reqPack[1];
|
||||
|
||||
if (callback)
|
||||
this.replies[this.seq_num] = [reqReplTemplate[1], callback];
|
||||
|
||||
client.pack_stream.pack(format, requestArguments);
|
||||
var b = client.pack_stream.write_queue[0];
|
||||
client.pack_stream.flush();
|
||||
|
||||
} else if (templateType == 'Array'){
|
||||
if (reqName === 'GetAtomName') {
|
||||
var atom = req_proxy.arguments[0];
|
||||
if (client.atom_names[atom]) {
|
||||
-- client.seq_num;
|
||||
return setImmediate(function() {
|
||||
callback(undefined, client.atom_names[atom]);
|
||||
});
|
||||
} else {
|
||||
client.pending_atoms[client.seq_num] = atom;
|
||||
}
|
||||
}
|
||||
|
||||
var format = reqTemplate[0];
|
||||
var requestArguments = [];
|
||||
|
||||
for (var a = 0; a < reqTemplate[1].length; ++a)
|
||||
requestArguments.push(reqTemplate[1][a]);
|
||||
for (var a in args)
|
||||
requestArguments.push(args[a]);
|
||||
|
||||
if (callback)
|
||||
this.replies[this.seq_num] = [reqReplTemplate[1], callback];
|
||||
|
||||
client.pack_stream.pack(format, requestArguments);
|
||||
client.pack_stream.flush();
|
||||
} else {
|
||||
throw 'unknown request format - ' + templateType;
|
||||
}
|
||||
}
|
||||
return reqFunc;
|
||||
})(r);
|
||||
}
|
||||
}
|
||||
|
||||
XClient.prototype.AllocID = function()
|
||||
{
|
||||
if (this._unusedIds.length > 0) {
|
||||
return this._unusedIds.pop();
|
||||
}
|
||||
// TODO: handle overflow (XCMiscGetXIDRange from XC_MISC ext)
|
||||
this.display.rsrc_id++;
|
||||
return (this.display.rsrc_id << this.display.rsrc_shift) + this.display.resource_base;
|
||||
};
|
||||
|
||||
XClient.prototype.ReleaseID = function(id) {
|
||||
this._unusedIds.push(id);
|
||||
};
|
||||
|
||||
// TODO: move core events unpackers to corereqs.js
|
||||
XClient.prototype.unpackEvent = function(type, seq, extra, code, raw, headerBuf)
|
||||
{
|
||||
var event = {}; // TODO: constructor & base functions
|
||||
// Remove the most significant bit. See Chapter 1, Event Format section in X11 protocol
|
||||
// specification
|
||||
type = type & 0x7F;
|
||||
event.type = type;
|
||||
event.seq = seq;
|
||||
|
||||
var extUnpacker = this.eventParsers[type];
|
||||
if (extUnpacker)
|
||||
{
|
||||
return extUnpacker(type, seq, extra, code, raw);
|
||||
}
|
||||
|
||||
if (type == 2 || type == 3 || type == 4 || type == 5 || type == 6) { // motion event
|
||||
var values = raw.unpack('LLLssssSC');
|
||||
//event.raw = values;
|
||||
// TODO: use unpackTo???
|
||||
event.name = [,,'KeyPress', 'KeyRelease', 'ButtonPress', 'ButtonRelease', 'MotionNotify'][type]
|
||||
event.time = extra;
|
||||
event.keycode = code;
|
||||
event.root = values[0];
|
||||
event.wid = values[1];
|
||||
event.child = values[2];
|
||||
event.rootx = values[3];
|
||||
event.rooty = values[4];
|
||||
event.x = values[5];
|
||||
event.y = values[6];
|
||||
event.buttons = values[7];
|
||||
event.sameScreen = values[8];
|
||||
} else if (type == 7 || type == 8) { //EnterNotify || LeaveNotify
|
||||
event.name = type === 7 ? 'EnterNotify' : 'LeaveNotify';
|
||||
var values = raw.unpack('LLLssssSC');
|
||||
event.root = values[0]
|
||||
event.wid = values[1]
|
||||
event.child = values[2];
|
||||
event.rootx = values[3];
|
||||
event.rooty = values[4];
|
||||
event.x = values[5];
|
||||
event.y = values[6];
|
||||
event.values = values
|
||||
|
||||
} else if (type == 12) { // Expose
|
||||
var values = raw.unpack('SSSSS');
|
||||
event.name = 'Expose'
|
||||
event.wid = extra;
|
||||
event.x = values[0];
|
||||
event.y = values[1];
|
||||
event.width = values[2];
|
||||
event.height = values[3];
|
||||
event.count = values[4]; // TODO: ???
|
||||
} else if (type == 16) { // CreateNotify
|
||||
var values = raw.unpack('LssSSSc');
|
||||
event.name = 'CreateNotify'
|
||||
event.parent = extra;
|
||||
event.wid = values[0];
|
||||
event.x = values[1];
|
||||
event.y = values[2];
|
||||
event.width = values[3];
|
||||
event.height = values[4];
|
||||
event.borderWidth = values[5];
|
||||
event.overrideRedirect = values[6] ? true : false;
|
||||
// x, y, width, height, border
|
||||
} else if (type == 17) { // destroy notify
|
||||
var values = raw.unpack('L');
|
||||
event.name = 'DestroyNotify'
|
||||
event.event = extra;
|
||||
event.wid = values[0];
|
||||
} else if (type == 18) { // UnmapNotify
|
||||
var values = raw.unpack('LC');
|
||||
event.name = 'UnmapNotify'
|
||||
event.event = extra;
|
||||
event.wid = values[0];
|
||||
event.fromConfigure = values[1] ? true : false;
|
||||
} else if (type == 19) { // MapNotify
|
||||
var values = raw.unpack('LC');
|
||||
event.name = 'MapNotify'
|
||||
event.event = extra;
|
||||
event.wid = values[0];
|
||||
event.overrideRedirect = values[1] ? true : false;
|
||||
} else if (type == 20) {
|
||||
var values = raw.unpack('L');
|
||||
event.name = 'MapRequest'
|
||||
event.parent = extra;
|
||||
event.wid = values[0];
|
||||
} else if (type == 22) {
|
||||
var values = raw.unpack('LLssSSSC');
|
||||
event.name = 'ConfigureNotify';
|
||||
event.wid = extra;
|
||||
// TODO rename
|
||||
event.wid1 = values[0];
|
||||
event.aboveSibling = values[1];
|
||||
event.x = values[2];
|
||||
event.y = values[3];
|
||||
event.width = values[4];
|
||||
event.height = values[5];
|
||||
event.borderWidth = values[6];
|
||||
event.overrideRedirect = values[7];
|
||||
} else if (type == 23) {
|
||||
var values = raw.unpack('LLssSSSS');
|
||||
event.name = 'ConfigureRequest';
|
||||
event.stackMode = code;
|
||||
event.parent = extra;
|
||||
event.wid = values[0];
|
||||
event.sibling = values[1];
|
||||
event.x = values[2]
|
||||
event.y = values[3]
|
||||
event.width = values[4]
|
||||
event.height = values[5]
|
||||
event.borderWidth = values[6];
|
||||
//
|
||||
// The value-mask indicates which components were specified in
|
||||
// the request. The value-mask and the corresponding values are reported as given
|
||||
// in the request. The remaining values are filled in from the current geometry of the
|
||||
// window, except in the case of sibling and stack-mode, which are reported as None
|
||||
// and Above (respectively) if not given in the request.
|
||||
event.mask = values[6];
|
||||
// 322, [ 12582925, 0, 0, 484, 316, 1, 12, 0
|
||||
//console.log([extra, code, values]);
|
||||
} else if (type == 28) {// PropertyNotify
|
||||
event.name = 'PropertyNotify';
|
||||
var values = raw.unpack('LLC');
|
||||
event.wid = extra;
|
||||
event.atom = values[0];
|
||||
event.time = values[1];
|
||||
event.state = values[2];
|
||||
} else if (type == 29) {// SelectionClear
|
||||
event.name = 'SelectionClear';
|
||||
event.time = extra;
|
||||
var values = raw.unpack('LL');
|
||||
event.owner = values[0];
|
||||
event.selection = values[1];
|
||||
} else if (type == 30) {// SelectionRequest
|
||||
event.name = 'SelectionRequest';
|
||||
// TODO check this
|
||||
event.time = extra;
|
||||
var values = raw.unpack('LLLLL');
|
||||
event.owner = values[0];
|
||||
event.requestor = values[1];
|
||||
event.selection = values[2];
|
||||
event.target = values[3];
|
||||
event.property = values[4];
|
||||
} else if (type == 31) {// SelectionNotify
|
||||
event.name = 'SelectionNotify';
|
||||
// TODO check this
|
||||
event.time = extra;
|
||||
var values = raw.unpack('LLLL');
|
||||
event.requestor = values[0];
|
||||
event.selection = values[1];
|
||||
event.target = values[2];
|
||||
event.property = values[3];
|
||||
} else if (type == 33) {// ClientMessage
|
||||
event.name = 'ClientMessage';
|
||||
event.format = code;
|
||||
event.wid = extra;
|
||||
event.message_type = raw.unpack('L')[0];
|
||||
var format = (code === 32) ? 'LLLLL' : (code === 16) ? 'SSSSSSSSSS' : 'CCCCCCCCCCCCCCCCCCCC';
|
||||
event.data = raw.unpack(format, 4);
|
||||
} else if (type == 34) {
|
||||
event.name = 'MappingNotify';
|
||||
event.request = headerBuf[4];
|
||||
event.firstKeyCode = headerBuf[5];
|
||||
event.count = headerBuf[6];
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
XClient.prototype.expectReplyHeader = function()
|
||||
{
|
||||
// TODO: move error parsers to corereqs.js
|
||||
|
||||
var client = this;
|
||||
client.pack_stream.get( 8, function(headerBuf) {
|
||||
var res = headerBuf.unpack('CCSL');
|
||||
var type = res[0];
|
||||
var seq_num = res[2];
|
||||
var bad_value = res[3];
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
var error_code = res[1];
|
||||
var error = new Error();
|
||||
error.error = error_code;
|
||||
error.seq = seq_num;
|
||||
if (client.options.debug) {
|
||||
error.longstack = client.seq2stack[error.seq]
|
||||
console.log(client.seq2stack[error.seq].stack);
|
||||
}
|
||||
|
||||
// unpack error packet (32 bytes for all error types, 8 of them in CCSL header)
|
||||
client.pack_stream.get(24, function(buf) {
|
||||
|
||||
var res = buf.unpack('SC');
|
||||
error.message = xerrors.errorText[error_code];
|
||||
error.badParam = bad_value;
|
||||
error.minorOpcode = res[0];
|
||||
error.majorOpcode = res[1];
|
||||
|
||||
var extUnpacker = client.errorParsers[error_code];
|
||||
if (extUnpacker) {
|
||||
extUnpacker(error, error_code, seq_num, bad_value, buf);
|
||||
}
|
||||
|
||||
var handler = client.replies[seq_num];
|
||||
if (handler) {
|
||||
var callback = handler[1];
|
||||
var handled = callback(error);
|
||||
if (!handled)
|
||||
client.emit('error', error);
|
||||
// TODO: should we delete seq2stack and reply even if there is no handler?
|
||||
if (client.options.debug)
|
||||
delete client.seq2stack[seq_num];
|
||||
delete client.replies[seq_num];
|
||||
} else
|
||||
client.emit('error', error);
|
||||
client.expectReplyHeader();
|
||||
} );
|
||||
return;
|
||||
} else if (type > 1)
|
||||
{
|
||||
client.pack_stream.get(24, function(buf) {
|
||||
var extra = res[3];
|
||||
var code = res[1];
|
||||
var ev = client.unpackEvent(type, seq_num, extra, code, buf, headerBuf);
|
||||
|
||||
// raw event 32-bytes packet (primarily for use in SendEvent);
|
||||
// TODO: Event::pack based on event parameters, inverse to unpackEvent
|
||||
ev.rawData = new Buffer(32);
|
||||
headerBuf.copy(ev.rawData);
|
||||
buf.copy(ev.rawData, 8);
|
||||
|
||||
client.emit('event', ev);
|
||||
var ee = client.event_consumers[ev.wid];
|
||||
if (ee) {
|
||||
ee.emit('event', ev);
|
||||
}
|
||||
if (ev.parent) {
|
||||
ee = client.event_consumers[ev.parent];
|
||||
if (ee)
|
||||
ee.emit('child-event', ev);
|
||||
}
|
||||
client.expectReplyHeader();
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
var opt_data = res[1];
|
||||
var length_total = res[3]; // in 4-bytes units, _including_ this header
|
||||
var bodylength = 24 + length_total*4; // 24 is rest if 32-bytes header
|
||||
|
||||
client.pack_stream.get( bodylength, function( data ) {
|
||||
|
||||
var handler = client.replies[seq_num];
|
||||
if (handler) {
|
||||
var unpack = handler[0];
|
||||
if (client.pending_atoms[seq_num]) {
|
||||
opt_data = seq_num;
|
||||
}
|
||||
|
||||
var result = unpack.call(client, data, opt_data);
|
||||
var callback = handler[1];
|
||||
callback(null, result);
|
||||
// TODO: add multiple replies flag and delete handler only after last reply (eg ListFontsWithInfo)
|
||||
delete client.replies[seq_num];
|
||||
}
|
||||
// wait for new packet from server
|
||||
client.expectReplyHeader();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
XClient.prototype.startHandshake = function() {
|
||||
var client = this;
|
||||
|
||||
handshake.writeClientHello(this.pack_stream, this.displayNum, this.authHost, this.authFamily);
|
||||
handshake.readServerHello(this.pack_stream, function(err, display)
|
||||
{
|
||||
if (err) {
|
||||
client.emit('error', err);
|
||||
return;
|
||||
}
|
||||
client.expectReplyHeader();
|
||||
client.display = display;
|
||||
display.client = client;
|
||||
client.emit('connect', display);
|
||||
});
|
||||
}
|
||||
|
||||
XClient.prototype.require = function(extName, callback)
|
||||
{
|
||||
var self = this;
|
||||
var ext = this._extensions[extName];
|
||||
if (ext) {
|
||||
return process.nextTick(function() {
|
||||
callback(null, ext);
|
||||
});
|
||||
}
|
||||
|
||||
ext = Require('x11/core/ext/' + extName);
|
||||
ext.requireExt(this.display, function(err, _ext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._extensions[extName] = _ext;
|
||||
callback(null, _ext);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.createClient = function(options, initCb)
|
||||
{
|
||||
if (typeof options === 'function') {
|
||||
initCb = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!options) options = {};
|
||||
|
||||
var display = options.display;
|
||||
if (!display)
|
||||
display = (process.env.DISPLAY) ? process.env.DISPLAY : ':0';
|
||||
|
||||
var displayMatch = display.match(/^(?:[^:]*?\/)?(.*):(\d+)(?:.(\d+))?$/);
|
||||
if (!displayMatch)
|
||||
throw new Error("Cannot parse display");
|
||||
|
||||
var host = displayMatch[1];
|
||||
|
||||
var displayNum = displayMatch[2];
|
||||
if (!displayNum)
|
||||
displayNum = 0;
|
||||
var screenNum = displayMatch[3];
|
||||
if (!screenNum)
|
||||
screenNum = 0;
|
||||
|
||||
// open stream
|
||||
var stream;
|
||||
var connected = false;
|
||||
var cbCalled = false;
|
||||
var socketPath;
|
||||
|
||||
// try local socket on non-windows platforms
|
||||
if ( ['cygwin', 'win32', 'win64'].indexOf(process.platform) < 0 )
|
||||
{
|
||||
if (process.platform == 'darwin' || process.platform == 'mac')
|
||||
{
|
||||
// socket path on OSX is /tmp/launch-(some id)/org.x:0
|
||||
if (display[0] == '/')
|
||||
{
|
||||
socketPath = display;
|
||||
}
|
||||
} else if(!host)
|
||||
socketPath = '/tmp/.X11-unix/X' + displayNum;
|
||||
}
|
||||
var client = new XClient(displayNum, screenNum, options);
|
||||
|
||||
var connectStream = function() {
|
||||
if (socketPath) {
|
||||
stream = net.createConnection(socketPath);
|
||||
} else {
|
||||
stream = net.createConnection(6000 + parseInt(displayNum), host);
|
||||
}
|
||||
stream.on('connect', function() {
|
||||
connected = true;
|
||||
client.init(stream);
|
||||
});
|
||||
stream.on('error', function(err) {
|
||||
if (!connected && socketPath && err.code === 'ENOENT') {
|
||||
// Retry connection with TCP on localhost
|
||||
socketPath = null;
|
||||
host = 'localhost';
|
||||
connectStream();
|
||||
} else if (initCb && !cbCalled) {
|
||||
cbCalled = true;
|
||||
initCb(err);
|
||||
} else {
|
||||
client.emit('error', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
connectStream();
|
||||
if (initCb)
|
||||
{
|
||||
client.on('connect', function(display) {
|
||||
// opt-in BigReq
|
||||
if (!options.disableBigRequests) {
|
||||
client.require('big-requests', function(err, BigReq) {
|
||||
if (err)
|
||||
return initCb(err)
|
||||
BigReq.Enable(function(err, maxLen) {
|
||||
display.max_request_length = maxLen;
|
||||
cbCalled = true;
|
||||
initCb(undefined, display);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
cbCalled = true;
|
||||
initCb(undefined, display);
|
||||
}
|
||||
});
|
||||
}
|
||||
return client;
|
||||
}
|
Loading…
Reference in New Issue
Block a user