123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /**
- * gpmclient.js - support the gpm mouse protocol
- * Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
- * https://github.com/chjj/blessed
- */
- var net = require('net');
- var fs = require('fs');
- var EventEmitter = require('events').EventEmitter;
- var GPM_USE_MAGIC = false;
- var GPM_MOVE = 1
- , GPM_DRAG = 2
- , GPM_DOWN = 4
- , GPM_UP = 8;
- var GPM_DOUBLE = 32
- , GPM_MFLAG = 128;
- var GPM_REQ_NOPASTE = 3
- , GPM_HARD = 256;
- var GPM_MAGIC = 0x47706D4C;
- var GPM_SOCKET = '/dev/gpmctl';
- // typedef struct Gpm_Connect {
- // unsigned short eventMask, defaultMask;
- // unsigned short minMod, maxMod;
- // int pid;
- // int vc;
- // } Gpm_Connect;
- function send_config(socket, Gpm_Connect, callback) {
- var buffer;
- if (GPM_USE_MAGIC) {
- buffer = new Buffer(20);
- buffer.writeUInt32LE(GPM_MAGIC, 0);
- buffer.writeUInt16LE(Gpm_Connect.eventMask, 4);
- buffer.writeUInt16LE(Gpm_Connect.defaultMask, 6);
- buffer.writeUInt16LE(Gpm_Connect.minMod, 8);
- buffer.writeUInt16LE(Gpm_Connect.maxMod, 10);
- buffer.writeInt16LE(process.pid, 12);
- buffer.writeInt16LE(Gpm_Connect.vc, 16);
- } else {
- buffer = new Buffer(16);
- buffer.writeUInt16LE(Gpm_Connect.eventMask, 0);
- buffer.writeUInt16LE(Gpm_Connect.defaultMask, 2);
- buffer.writeUInt16LE(Gpm_Connect.minMod, 4);
- buffer.writeUInt16LE(Gpm_Connect.maxMod, 6);
- buffer.writeInt16LE(Gpm_Connect.pid, 8);
- buffer.writeInt16LE(Gpm_Connect.vc, 12);
- }
- socket.write(buffer, function() {
- if (callback) callback();
- });
- }
- // typedef struct Gpm_Event {
- // unsigned char buttons, modifiers; // try to be a multiple of 4
- // unsigned short vc;
- // short dx, dy, x, y; // displacement x,y for this event, and absolute x,y
- // enum Gpm_Etype type;
- // // clicks e.g. double click are determined by time-based processing
- // int clicks;
- // enum Gpm_Margin margin;
- // // wdx/y: displacement of wheels in this event. Absolute values are not
- // // required, because wheel movement is typically used for scrolling
- // // or selecting fields, not for cursor positioning. The application
- // // can determine when the end of file or form is reached, and not
- // // go any further.
- // // A single mouse will use wdy, "vertical scroll" wheel.
- // short wdx, wdy;
- // } Gpm_Event;
- function parseEvent(raw) {
- var evnt = {};
- evnt.buttons = raw[0];
- evnt.modifiers = raw[1];
- evnt.vc = raw.readUInt16LE(2);
- evnt.dx = raw.readInt16LE(4);
- evnt.dy = raw.readInt16LE(6);
- evnt.x = raw.readInt16LE(8);
- evnt.y = raw.readInt16LE(10);
- evnt.type = raw.readInt16LE(12);
- evnt.clicks = raw.readInt32LE(16);
- evnt.margin = raw.readInt32LE(20);
- evnt.wdx = raw.readInt16LE(24);
- evnt.wdy = raw.readInt16LE(26);
- return evnt;
- }
- function GpmClient(options) {
- if (!(this instanceof GpmClient)) {
- return new GpmClient(options);
- }
- EventEmitter.call(this);
- var pid = process.pid;
- // check tty for /dev/tty[n]
- var path;
- try {
- path = fs.readlinkSync('/proc/' + pid + '/fd/0');
- } catch (e) {
- ;
- }
- var tty = /tty[0-9]+$/.exec(path);
- if (tty === null) {
- // TODO: should also check for /dev/input/..
- }
- var vc;
- if (tty) {
- tty = tty[0];
- vc = +/[0-9]+$/.exec(tty)[0];
- }
- var self = this;
- if (tty) {
- fs.stat(GPM_SOCKET, function(err, stat) {
- if (err || !stat.isSocket()) {
- return;
- }
- var conf = {
- eventMask: 0xffff,
- defaultMask: GPM_MOVE | GPM_HARD,
- minMod: 0,
- maxMod: 0xffff,
- pid: pid,
- vc: vc
- };
- var gpm = net.createConnection(GPM_SOCKET);
- this.gpm = gpm;
- gpm.on('connect', function() {
- send_config(gpm, conf, function() {
- conf.pid = 0;
- conf.vc = GPM_REQ_NOPASTE;
- //send_config(gpm, conf);
- });
- });
- gpm.on('data', function(packet) {
- var evnt = parseEvent(packet);
- switch (evnt.type & 15) {
- case GPM_MOVE:
- if (evnt.dx || evnt.dy) {
- self.emit('move', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- }
- if (evnt.wdx || evnt.wdy) {
- self.emit('mousewheel',
- evnt.buttons, evnt.modifiers,
- evnt.x, evnt.y, evnt.wdx, evnt.wdy);
- }
- break;
- case GPM_DRAG:
- if (evnt.dx || evnt.dy) {
- self.emit('drag', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- }
- if (evnt.wdx || evnt.wdy) {
- self.emit('mousewheel',
- evnt.buttons, evnt.modifiers,
- evnt.x, evnt.y, evnt.wdx, evnt.wdy);
- }
- break;
- case GPM_DOWN:
- self.emit('btndown', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- if (evnt.type & GPM_DOUBLE) {
- self.emit('dblclick', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- }
- break;
- case GPM_UP:
- self.emit('btnup', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- if (!(evnt.type & GPM_MFLAG)) {
- self.emit('click', evnt.buttons, evnt.modifiers, evnt.x, evnt.y);
- }
- break;
- }
- });
- gpm.on('error', function() {
- self.stop();
- });
- });
- }
- }
- GpmClient.prototype.__proto__ = EventEmitter.prototype;
- GpmClient.prototype.stop = function() {
- if (this.gpm) {
- this.gpm.end();
- }
- delete this.gpm;
- };
- GpmClient.prototype.ButtonName = function(btn) {
- if (btn & 4) return 'left';
- if (btn & 2) return 'middle';
- if (btn & 1) return 'right';
- return '';
- };
- GpmClient.prototype.hasShiftKey = function(mod) {
- return (mod & 1) ? true : false;
- };
- GpmClient.prototype.hasCtrlKey = function(mod) {
- return (mod & 4) ? true : false;
- };
- GpmClient.prototype.hasMetaKey = function(mod) {
- return (mod & 8) ? true : false;
- };
- module.exports = GpmClient;
|