190 lines
4.8 KiB
JavaScript
190 lines
4.8 KiB
JavaScript
|
var net=require('net');
|
||
|
var fs=require('fs');
|
||
|
var EventEmitter=require('events').EventEmitter;
|
||
|
var util = require('util')
|
||
|
|
||
|
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(sock, Gpm_Connect, cb) {
|
||
|
if (GPM_USE_MAGIC) {
|
||
|
var 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 {
|
||
|
var 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);
|
||
|
}
|
||
|
sock.write(buffer, function () {
|
||
|
if (cb) cb();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
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 tty=/tty[0-9]+$/.exec(fs.readlinkSync('/proc/'+pid+'/fd/0'));
|
||
|
if (tty === null) {
|
||
|
//TODO: should also check for /dev/input/..
|
||
|
}
|
||
|
|
||
|
var vc;
|
||
|
if (tty) {
|
||
|
var tty=tty[0];
|
||
|
vc=/[0-9]+$/.exec(tty)[0];
|
||
|
}
|
||
|
|
||
|
if (tty) {
|
||
|
|
||
|
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);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
var self=this;
|
||
|
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.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.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(err) {
|
||
|
console.log('GPM ERROR', err);
|
||
|
self.stop();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
GpmClient.prototype=new EventEmitter();
|
||
|
|
||
|
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;
|