// http://www.x.org/releases/X11R7.6/doc/randrproto/randrproto.txt var x11 = Require('x11/core/x11'); // TODO: move to templates exports.requireExt = function(display, callback) { var X = display.client; X.QueryExtension('RANDR', function(err, ext) { if (!ext.present) return callback(new Error('extension not available')); //ext.ReportLevel = { //}; ext.QueryVersion = function(clientMaj, clientMin, callback) { X.seq_num++; X.pack_stream.pack('CCSLL', [ext.majorOpcode, 0, 3, clientMaj, clientMin]); X.replies[X.seq_num] = [ function(buf, opt) { var res = buf.unpack('LL'); return res; }, callback ]; X.pack_stream.flush(); }, ext.events = { RRScreenChangeNotify: 0 }, ext.NotifyMask = { ScreenChange: 1, CrtcChange: 2, OutputChange: 4, OutputProperty: 8, All: 15 }; ext.Rotation = { Rotate_0: 1, Rotate_90: 2, Rotate_180: 4, Rotate_270: 8, Reflect_X: 16, Reflect_Y: 32 }; ext.ConfigStatus = { Sucess: 0, InvalidConfigTime: 1, InvalidTime: 2, Failed: 3 }; ext.ModeFlag = { HSyncPositive: 1, HSyncNegative: 2, VSyncPositive: 4, VSyncNegative: 8, Interlace: 16, DoubleScan: 32, CSync: 64, CSyncPositive: 128, CSyncNegative: 256, HSkewPresent: 512, BCast: 1024, PixelMultiplex: 2048, DoubleClock: 4096, ClockDivideBy2: 8192 } ext.SetScreenConfig = function(win, ts, configTs, sizeId, rotation, rate, cb) { X.seq_num ++; X.pack_stream.pack('CCSLLLSSSS', [ext.majorOpcode, 2, 6, win, ts, configTs, sizeId, rotation, rate, 0]); X.replies[X.seq_num] = [ function(buf, opt) { var res = buf.unpack('LLLSSLL'); return { status : opt, newTs : res [0], configTs : res[1], root : res[2], subpixelOrder : res[3] } }, function(err, res) { var err; if (res.status !== 0) { err = new Error('SetScreenConfig error'); err.code = res.status; } cb(err, res); } ]; X.pack_stream.flush(); }, ext.SelectInput = function(win, mask) { X.seq_num++; X.pack_stream.pack('CCSLSS', [ext.majorOpcode, 4, 3, win, mask, 0]); X.pack_stream.flush(); }, ext.GetScreenInfo = function(win, cb) { X.seq_num ++; X.pack_stream.pack('CCSL', [ext.majorOpcode, 5, 2, win]); X.replies[X.seq_num] = [ function(buf, opt) { var i, j; var res = buf.unpack('LLLSSSSSS'); var info = { rotations : opt, root : res [0], timestamp : res[1], config_timestamp : res[2], sizeID : res[4], rotation : res[5], rate : res[6], rates: [] }; var nSizes = res[3]; var nRates = res[7]; var screens_len = nSizes << 2; var format = Array(screens_len + 1).join('S'); res = buf.unpack(format, 24); info.screens = []; for (i = 0; i < screens_len; i += 4) { info.screens.push({ px_width : res[i], px_height : res[i + 1], mm_width : res[i + 2], mm_height : res[i + 3] }); } format = Array(nRates + 1).join('S'); info.rates = buf.unpack(format, 24 + screens_len * 2); return info; }, cb ]; X.pack_stream.flush(); }, ext.GetScreenResources = function(win, cb) { X.seq_num ++; X.pack_stream.pack('CCSL', [ext.majorOpcode, 8, 2, win]); X.replies[X.seq_num] = [ function(buf, opt) { var i; var pos = 0; var res = buf.unpack('LLSSSSxxxxxxxx'); var resources = { timestamp : res[0], config_timestamp : res[1], modeinfos : [] }; pos += 24; var format = Array(res[2] + 1).join('L'); resources.crtcs = buf.unpack(format, pos); pos += res[2] << 2; format = Array(res[3] + 1).join('L'); resources.outputs = buf.unpack(format, pos); pos += res[3] << 2; format = Array(res[4] + 1).join('LSSLSSSSSSSSL'); res_modes = buf.unpack(format, pos); pos += res[4] << 5; for (i = 0; i < res[4]; i+= 13) { resources.modeinfos.push({ id : res_modes[i + 0], width : res_modes[i + 1], height : res_modes[i + 2], dot_clock : res_modes[i + 3], h_sync_start : res_modes[i + 4], h_sync_end : res_modes[i + 5], h_total : res_modes[i + 6], h_skew : res_modes[i + 7], v_sync_start : res_modes[i + 8], v_sync_end : res_modes[i + 9], v_total : res_modes[i + 10], modeflags : res_modes[i + 12], name : buf.slice(pos, pos + res_modes[i + 11]).toString() }); pos += res_modes[i + 11]; } return resources; }, cb ]; X.pack_stream.flush(); }, ext.GetOutputInfo = function(output, ts, cb) { X.seq_num ++; X.pack_stream.pack('CCSLL', [ext.majorOpcode, 9, 3, output, ts ]); X.replies[X.seq_num] = [ function(buf, opt) { var i; var pos = 0; var res = buf.unpack('LLLLCCSSSSS'); var info = { timestamp : res[0], crtc : res[1], mm_width : res[2], mm_height : res[3], connection : res[4], subpixelOrder : res[5], preferredModes: res[8] }; pos += 28; var format = Array(res[6] + 1).join('L'); info.crtcs = buf.unpack(format, pos); pos += res[6] << 2; format = Array(res[7] + 1).join('L'); info.modes = buf.unpack(format, pos); pos += res[7] << 2; format = Array(res[9] + 1).join('L'); info.clones = buf.unpack(format, pos); pos += res[9] << 2; info.name = buf.slice(pos, pos + res_modes[10]).toString('binary'); return info; }, cb ]; X.pack_stream.flush(); }, ext.GetCrtcInfo = function(crtc, configTs, cb) { X.seq_num ++; X.pack_stream.pack('CCSLL', [ext.majorOpcode, 20, 3, crtc, configTs ]); X.replies[X.seq_num] = [ function(buf, opt) { var pos = 0; var res = buf.unpack('LssSSLSSSS'); var info = { status : opt, timestamp : res[0], x : res[1], y : res[2], width : res[3], height : res[4], mode : res[5], rotation : res[6], rotations : res[7] }; pos += 24; var format = Array(res[8] + 1).join('L'); info.output = buf.unpack(format, pos); format = Array(res[9] + 1).join('L'); info.possible = buf.unpack(format, pos); return info; }, cb ]; X.pack_stream.flush(); }, X.eventParsers[ext.firstEvent + ext.events.RRScreenChangeNotify] = function(type, seq, extra, code, raw) { var event = {}; event.raw = raw; event.type = type event.seq = seq; event.rotation = code; var values = raw.unpack('LLLSSSSSS'); event.time = extra event.configtime = values[0]; event.root = values[1]; event.requestWindow = values[2]; event.sizeId = values[3]; event.subpixelOrder = values[4]; event.width = values[5]; event.height = values[6]; event.physWidth = values[7]; event.physHeight = values[8]; event.name = 'RRScreenChangeNotify'; return event; }; ext.QueryVersion(255, 255, function(err, version) { if (err) return callback(err); ext.major_version = version[0]; ext.minor_version = version[1]; callback(null, ext); }); }); }