2022-02-15 04:33:19 +01:00
const logger = require ( './logger.js' ) ;
const util = require ( './util.js' ) ;
const Keyfilter = require ( './keyfilter.js' ) ;
const cli = require ( './cli.js' ) ;
const spawn = require ( 'child_process' ) . spawn ;
2022-03-10 12:29:41 +01:00
const sudo = require ( 'sudo' ) ;
2022-02-15 04:33:19 +01:00
const inputDevices = '/dev/input/' ;
const inputDevicesById = '/dev/input/by-id/' ;
class Watcher {
constructor ( config , callback ) {
2022-03-10 12:29:41 +01:00
if ( config === undefined || config . device === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
2022-03-10 12:29:41 +01:00
for ( let key in config ) {
2022-02-15 04:33:19 +01:00
this [ key ] = config [ key ] ;
}
2022-03-03 03:35:34 +01:00
this . keyfilter = new Keyfilter ( config . keys , config . combos ) ;
2022-02-24 00:07:57 +01:00
this . restart = config . restart ;
2022-02-15 04:33:19 +01:00
this . callback = callback ;
}
2022-03-10 12:29:41 +01:00
async start ( ) {
if ( this . process !== undefined ) {
return ;
}
if ( this . sudo ) {
logger . debug ( 'starting sudo watcher \'' + this . device + '\'...' ) ;
this . process = sudo ( [ 'evtest' , this . device ] , { cachePassword : true , prompt : 'sudo password:' } ) ;
} else {
2022-02-24 00:07:57 +01:00
logger . debug ( 'starting watcher \'' + this . device + '\'...' ) ;
2022-03-10 12:29:41 +01:00
this . process = spawn ( 'evtest' , [ this . device ] ) ;
}
try {
await this . attachListeners ( ) ;
} catch ( err ) {
logger . error ( err ) ;
}
2022-02-15 04:33:19 +01:00
}
stop ( ) {
2022-03-10 12:29:41 +01:00
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
logger . debug ( 'stopping watcher \'' + this . device + '\'...' ) ;
this . process . kill ( ) ;
2022-02-16 03:28:17 +01:00
logger . info ( 'watcher \'' + this . device + '\' stopped' ) ;
2022-02-15 04:33:19 +01:00
}
2022-03-10 12:29:41 +01:00
async attachListeners ( ) {
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
this . addStdOutListener ( ) ;
this . addStdErrListener ( ) ;
2022-03-10 12:29:41 +01:00
this . addErrorListener ( ) ;
this . addCloseListener ( ) ;
await this . addSpawnListener ( ) ;
2022-02-15 04:33:19 +01:00
}
addStdOutListener ( ) {
2022-03-10 12:29:41 +01:00
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
logger . debug ( 'adding stdout listener to watcher \'' + this . device + '\'...' ) ;
this . process . stdout . on ( 'data' , ( data ) => {
if ( this . keyfilter == undefined ) {
return ;
}
2022-03-10 12:29:41 +01:00
let filtered = this . keyfilter . filter ( data ) ;
if ( filtered === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
if ( filtered . delayed ) {
logger . debug ( 'delaying captured \'' + filtered . type + '\' event for \'' + filtered . key + '\' from watcher \'' + this . device + '\'' ) ;
return ;
}
2022-03-03 03:35:34 +01:00
if ( filtered . combo ) {
if ( ! filtered . combo . finished ) {
logger . debug ( 'captured \'' + filtered . combo . type . action + '\' event for \'' + filtered . combo . key + '\' from watcher \'' + this . device + '\' is part of a combo (next key: \'' + filtered . combo . remaining [ 0 ] + '\')' ) ;
return ;
}
logger . debug ( 'captured \'' + filtered . combo . type . action + '\' event for \'' + filtered . combo . key + '\' from watcher \'' + this . device + '\' is the last part of a combo' ) ;
}
this . keyfilter . resetCurrentCombo ( ) ;
2022-03-10 14:05:08 +01:00
logger . info ( 'executing command \'' + filtered . command . name + '\' registered for captured \'' + filtered . type + '\' event for \'' + filtered . key + '\' from watcher \'' + this . device + '\'' ) ;
cli . execute ( filtered . command . cmd , filtered . command . args , filtered . command . sudo )
2022-02-15 04:33:19 +01:00
. then ( logger . info )
. catch ( logger . error ) ;
} ) ;
}
addStdErrListener ( ) {
2022-03-10 12:29:41 +01:00
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
logger . debug ( 'adding stderr listener to watcher \'' + this . device + '\'...' ) ;
this . process . stderr . on ( 'data' , ( data ) => {
2022-03-10 12:29:41 +01:00
this . error = data . toString ( ) . trim ( ) ;
2022-02-15 04:33:19 +01:00
} ) ;
}
2022-03-10 12:29:41 +01:00
addSpawnListener ( ) {
return new Promise ( ( resolve , reject ) => {
logger . debug ( 'adding spawn listener to watcher \'' + this . device + '\'...' ) ;
this . process . on ( 'spawn' , ( ) => {
logger . info ( 'watcher \'' + this . device + '\' initialized and capturing configured events' ) ;
resolve ( ) ;
} ) ;
2022-02-24 00:07:57 +01:00
} ) ;
}
2022-02-15 04:33:19 +01:00
addCloseListener ( ) {
2022-03-10 12:29:41 +01:00
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
logger . debug ( 'adding close listener to watcher \'' + this . device + '\'...' ) ;
this . process . on ( 'close' , ( code ) => {
2022-03-10 12:29:41 +01:00
if ( code === undefined ) {
2022-02-16 03:28:17 +01:00
code = 0 ;
2022-03-10 12:29:41 +01:00
if ( this . error !== undefined ) {
code = 1 ;
}
}
if ( this . error !== undefined ) {
logger . error ( 'watcher \'' + this . device + '\' encountered an error > ' + this . error ) ;
this . restart = false ;
2022-02-16 03:28:17 +01:00
}
2022-02-24 00:07:57 +01:00
this . process = undefined ;
2022-02-16 03:28:17 +01:00
this . code = code ;
2022-02-15 04:33:19 +01:00
logger . info ( 'watcher \'' + this . device + '\' finished with exit code ' + code ) ;
2022-03-10 12:29:41 +01:00
if ( this . callback !== undefined ) {
2022-02-24 00:07:57 +01:00
this . callback ( this ) ;
2022-02-15 04:33:19 +01:00
}
} ) ;
}
2022-02-24 00:07:57 +01:00
addErrorListener ( reject ) {
2022-03-10 12:29:41 +01:00
if ( this . process === undefined ) {
2022-02-15 04:33:19 +01:00
return ;
}
logger . debug ( 'adding error listener to \'' + this . device + '\'...' ) ;
this . process . on ( 'error' , ( err ) => {
2022-02-24 00:07:57 +01:00
reject ( logger . error ( 'watcher \'' + this . device + '\' encountered an error >>> ' + err ) ) ;
2022-02-15 04:33:19 +01:00
} ) ;
}
check ( ) {
return new Promise ( ( resolve , reject ) => {
2022-02-16 02:08:21 +01:00
if ( ! this . keyfilter . isValid ( ) ) {
reject ( 'no key(s) defined for watcher \'' + this . device + '\'' ) ;
}
2022-03-10 12:29:41 +01:00
Promise . any ( [ this . device , inputDevices + this . device , inputDevicesById + this . device ] . map ( util . getFileInfo ) )
2022-02-15 04:33:19 +01:00
. then ( ( result ) => {
2022-03-10 12:29:41 +01:00
if ( result . path !== this . device ) {
2022-02-15 04:33:19 +01:00
logger . info ( 'resolved watcher for device \'' + this . device + '\' to \'' + result . path + '\'' )
}
this . device = result . path ;
2022-02-16 03:28:17 +01:00
resolve ( this ) ;
2022-02-15 04:33:19 +01:00
} )
. catch ( reject ) ;
} ) ;
}
}
module . exports = Watcher ;