Last active
August 24, 2019 01:36
-
-
Save reconbot/844f9ab4dd8d2b950385d66badce49b9 to your computer and use it in GitHub Desktop.
This parses the ms serial mouse wire protol into randomly named events.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Transform } = require('stream') | |
const debug = require('debug')('serialport/mouse-parser') | |
const StartByteMarkerByte = 0b01000000 | |
const LeftMouseButtonByte = 0b00100000 | |
const RightMouseButtonByte = 0b00010000 | |
const isStart = byte => byte & StartByteMarkerByte | |
const DEFAULT_STATE = Object.freeze({ | |
leftMouseButton: false, | |
rightMouseButton: false, | |
}) | |
/** | |
* A transform stream that does something pretty cool. | |
* @extends Transform | |
* @param {Object} options parser options object | |
* @example | |
// To use the `MouseParser` stream: | |
const MouseParser = require('mouse-parser') | |
const mouseParser = new MouseParser() | |
mouseParser.on('data', console.log) | |
mouseParser.write(Buffer.from([1,2,3])) | |
*/ | |
class MouseParser extends Transform { | |
constructor(options = {}) { | |
super({ | |
readableObjectMode: true, | |
...options, | |
}) | |
this.buffer = [] | |
this._state = DEFAULT_STATE | |
} | |
get state() { | |
return this._state | |
} | |
_transform(chunk, encoding, cb) { | |
const data = [...this.buffer, ...Array.from(chunk)] | |
debug('processing', data) | |
let packet = [] | |
for (const byte of data) { | |
if (isStart(byte) && packet.length > 0) { | |
this._emitEvent(packet) | |
packet = [] | |
} | |
packet.push(byte) | |
if (packet.length === 3) { | |
this._emitEvent(packet) | |
packet = [] | |
} | |
} | |
debug('buffering', packet) | |
this.buffer = packet | |
cb() | |
} | |
_flush(cb) { | |
this._emitEvent(this.buffer) | |
this.buffer = [] | |
cb() | |
} | |
/** | |
* Converts a 3 byte packet into events | |
* @param {[number, number number]} packet] | |
* | |
* Data layout | |
D7 D6 D5 D4 D3 D2 D1 D0 | |
Byte 1 X 1 LB RB Y7 Y6 X7 X6 | |
Byte 2 X 0 X5 X4 X3 X2 X1 X0 | |
Byte 3 X 0 Y5 Y4 Y3 Y2 Y1 Y0 | |
LB is the state of the left button (1 means down) | |
RB is the state of the right button (1 means down) | |
X7-X0 movement in X direction since last packet (signed byte) | |
Y7-Y0 movement in Y direction since last packet (signed byte) | |
* | |
*/ | |
_emitEvent(packet) { | |
debug('emitting packet', packet) | |
if (packet.length !== 3) { | |
debug(`bad packet length ${packet}`) | |
return | |
} | |
const leftMouseButton = Boolean(packet[0] & LeftMouseButtonByte) | |
const rightMouseButton = Boolean(packet[0] & RightMouseButtonByte) | |
const events = [] | |
if (this._state.leftMouseButton !== leftMouseButton) { | |
debug('left mouse click', leftMouseButton) | |
events.push({ type: 'onClick' }) | |
} | |
if (this._state.rightMouseButton !== rightMouseButton) { | |
debug('right mouse click', rightMouseButton) | |
events.push({ type: 'onRightClick' }) | |
} | |
if (events.length) { | |
this._state = Object.freeze({ leftMouseButton, rightMouseButton }) | |
debug('updating state', this._state) | |
} | |
// xy events | |
const xBuffer = Buffer.from([((packet[0] & 0b0000011) << 6) | (packet[1] & 0b00111111)]) | |
const x = xBuffer.readInt8() | |
const yBuffer = Buffer.from([((packet[0] & 0b0001100) << 4) | (packet[2] & 0b00111111)]) | |
const y = yBuffer.readInt8() | |
if (x || y) { | |
events.push({ event: 'move', x, y }) | |
} | |
for (const event of events) { | |
this.push({ ...event, state: this._state }) | |
} | |
} | |
} | |
module.exports = { MouseParser } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment