fixed combos

This commit is contained in:
Daniel Sommer 2022-03-11 15:47:48 +01:00
parent 1eb5ed1d5a
commit 5dd86716e9
4 changed files with 123 additions and 87 deletions

View file

@ -3,6 +3,9 @@
"level": "debug",
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
},
"combos": {
"delay": 5000
},
"watchers": [
{
"device": "/dev/input/by-id/usb-Chicony_HP_Elite_USB_Keyboard-event-kbd",
@ -23,7 +26,8 @@
{
"key": "key_f1",
"combo": [
"key_f3"
"key_f1",
"key_f2"
],
"event": "EV_KEY",
"type": "keydown",

View file

@ -1,4 +1,5 @@
const config = require('../config.json');
const comboConfig = require('../config.json').combos;
const commandConfig = require('../config.json').commands;
const LINE_START = 'Event: time';
@ -6,30 +7,52 @@ const ACTION_KEYUP = { id: 0, action: 'keyup' };
const ACTION_KEYDOWN = { id: 1, action: 'keydown' };
const ACTION_KEYHOLD = { id: 2, action: 'keyhold' };
const VARIABLE_KEY = '{{ key }}';
const VARIABLE_TYPE = '{{ type }}';
class Keyfilter {
constructor(keys) {
if ((keys === undefined || keys.length === 0)) {
constructor(config) {
if (config === undefined || config.length === 0) {
return;
}
this.actions = new Map();
for (let index = 0; index < keys.length; index++) {
this.setAction(keys[index]);
for (let index = 0; index < config.length; index++) {
this.setAction(config[index]);
}
this.currentCombo = undefined;
}
setAction(config) {
let type = ACTION_KEYDOWN;
switch (config.type.toLowerCase()) {
case ACTION_KEYUP.action:
type = ACTION_KEYUP;
break;
case ACTION_KEYHOLD.action:
type = ACTION_KEYHOLD;
break;
getCommand(command) {
if (command === undefined || commandConfig === undefined) {
return;
}
const result = commandConfig[command];
if (result === undefined) {
return;
}
result.name = command;
return result;
}
getActionType(type) {
let result = ACTION_KEYDOWN;
if (isNaN(type)) {
switch (type.toLowerCase()) {
case ACTION_KEYUP.action:
result = ACTION_KEYUP;
break;
case ACTION_KEYHOLD.action:
result = ACTION_KEYHOLD;
break;
}
} else {
switch (type) {
case ACTION_KEYUP.id:
result = ACTION_KEYUP;
break;
case ACTION_KEYHOLD.id:
result = ACTION_KEYHOLD;
break;
}
}
return result;
}
setAction(config) {
let key = config.key.toUpperCase();
if (config.combo !== undefined && config.combo.length > 0) {
const tmp = JSON.parse(JSON.stringify(config.combo));
@ -38,9 +61,9 @@ class Keyfilter {
}
this.actions.set(key,
{
type: type,
type: this.getActionType(config.type),
event: config.event,
command: getCommand(config.command),
command: this.getCommand(config.command),
combo: config.combo,
delay: function () {
if (config.combo === undefined) {
@ -73,27 +96,25 @@ class Keyfilter {
if (this.currentCombo === undefined && !this.isParsedEventValid(key, event, parsedEvent)) {
continue;
}
if (this.isStartOfCombo(key, event, parsedEvent)) {
return this.getFilterResult(key, event, 'combo', this.currentCombo);
if (this.isStartOfCombo(parsedEvent)) {
return this.setComboResult(this.getFilterResult(parsedEvent.key, parsedEvent.type.action));
}
if (this.isPartOfCombo(parsedEvent)) {
if (parsedEvent.ignore) {
continue;
}
const result = this.getFilterResult(key, event, 'combo', this.currentCombo);
if (this.currentCombo.finished) {
this.resetCurrentCombo();
}
return result;
return this.setComboResult(this.getFilterResult(parsedEvent.key, parsedEvent.type.action));
}
if (!this.isParsedEventValid(key, event, parsedEvent)) {
continue;
}
if (this.shouldBeDelayed(event)) {
return this.getFilterResult(key, event, 'delayed', true);
const result = this.getFilterResult(parsedEvent.key, event.type.action, event.command);
result.delayed = true;
return result;
}
event.captured = new Date().getTime();
return this.getFilterResult(key, event);
return this.getFilterResult(parsedEvent.key, event.type.action, event.command);
}
}
}
@ -102,29 +123,39 @@ class Keyfilter {
if (value.combo !== undefined && value.combo.length > 0) {
keyCheck = key.includes(parsed.key);
}
return keyCheck && (value.event === undefined || value.event === parsed.event) && value.type.id === parsed.type;
return keyCheck && (value.event === undefined || value.event === parsed.event) && value.type.id === parsed.type.id;
}
getFilterResult(key, event, extraName, extra) {
if (key === undefined || event === undefined) {
setComboResult(result) {
if (result === undefined || this.currentCombo === undefined) {
return;
}
let result = {
key: key,
type: event.type.action,
command: event.command,
delay: event.delay
};
if (extraName !== undefined && extra !== undefined) {
result[extraName] = extra;
if (!this.currentCombo.finished) {
result.combo = { possibilities: this.currentCombo.possibilities.length };
} else {
result.combo = {
finished: true,
done: this.currentCombo.done
};
result.command = this.currentCombo.command;
}
return result;
return result
}
getFilterResult(key, type, command) {
if (key === undefined || type === undefined) {
return;
}
return {
key: key,
type: type,
command: command
};
}
parseLine(line) {
try {
const parts = line.split(',');
const event = parts[1].substring(parts[1].indexOf('(') + 1, parts[1].lastIndexOf(')'));
const key = parts[2].substring(parts[2].indexOf('(') + 1, parts[2].indexOf(')'));
const type = parseInt(parts[3].split(' ').pop());
const type = this.getActionType(parseInt(parts[3].split(' ').pop()));
return { event: event, key: key, type: type };
} catch (err) {
return;
@ -136,8 +167,8 @@ class Keyfilter {
}
return new Date().getTime() - event.captured < event.delay;
}
isStartOfCombo(key, event, parsedEvent) {
if (event.combo === undefined || this.currentCombo !== undefined) {
isStartOfCombo(parsedEvent) {
if (this.currentCombo !== undefined) {
return false;
}
let possibilities = [];
@ -146,17 +177,20 @@ class Keyfilter {
continue;
}
if (!actionKey.toUpperCase().startsWith(parsedEvent.key)) {
continue;
continue;
}
possibilities.push(actionEvent.combo);
possibilities.push(JSON.parse(JSON.stringify(actionEvent)));
}
if (possibilities.length === 0) {
return false;
}
this.currentCombo = {
key: key,
type: event.type,
delay: event.delay,
key: parsedEvent.key.toUpperCase(),
timestamp: new Date().getTime(),
type: parsedEvent.type,
event: parsedEvent.event,
done: [parsedEvent.key],
possibilities: [event.combo]
possibilities: possibilities
};
return true;
}
@ -164,42 +198,49 @@ class Keyfilter {
if (this.hasComboTimedOut()) {
this.resetCurrentCombo();
}
if (this.currentCombo === undefined) {
if (this.currentCombo === undefined || this.currentCombo.type.id !== parsedEvent.type.id || this.currentCombo.event !== parsedEvent.event) {
return false;
}
if (this.currentCombo.done.includes(parsedEvent.key)) {
parsedEvent.ignore = true;
return true;
}
if (this.currentCombo.type.id !== parsedEvent.type) {
return false;
}
let combos = [];
// if (this.currentCombo.done.includes(parsedEvent.key)) {
// parsedEvent.ignore = true;
// return true;
// }
let possibilities = [];
for (let index = 0; index < this.currentCombo.possibilities.length; index++) {
const possibility = this.currentCombo.possibilities[index];
if (possibility[0].toUpperCase() !== parsedEvent.key) {
if (possibility.combo.length === 0) {
break;
}
if (possibility.combo[0].toUpperCase() !== parsedEvent.key) {
continue;
}
combos.push(possibility);
possibility.combo.shift();
possibilities.push(possibility);
}
if (combos.length === 0) {
if (possibilities.length === 0) {
return false;
}
this.currentCombo.key = parsedEvent.key;
this.currentCombo.timestamp = new Date().getTime();
this.currentCombo.done.push(parsedEvent.key);
if (this.currentCombo.possibilities.length === 1) {
this.currentCombo.finished = true;
this.currentCombo.possibilities = possibilities;
this.currentCombo.finished = false;
if (this.currentCombo.possibilities.length === 1 && this.currentCombo.possibilities[0].combo.length === 0) {
const tmp = this.currentCombo.possibilities[0];
delete tmp.combo;
tmp.finished = true;
tmp.timestamp = this.currentCombo.timestamp;
tmp.done = this.currentCombo.done;
this.currentCombo = tmp;
}
return true;
}
hasComboTimedOut() {
return false;
const result = this.currentCombo !== undefined &&
this.currentCombo.delay !== undefined &&
return comboConfig !== undefined &&
comboConfig.delay !== undefined &&
this.currentCombo !== undefined &&
this.currentCombo.timestamp !== undefined &&
new Date().getTime() - this.currentCombo.timestamp > this.currentCombo.delay;
return result;
new Date().getTime() - this.currentCombo.timestamp > comboConfig.delay;
}
resetCurrentCombo() {
this.currentCombo = undefined;
@ -210,17 +251,7 @@ class Keyfilter {
}
function getCommand(command) {
if (command === undefined || config.commands === undefined) {
return;
}
const result = config.commands[command];
if (result === undefined) {
return;
}
result.name = command;
return result;
}
module.exports = Keyfilter;

View file

@ -69,19 +69,20 @@ class Watcher {
if (filtered === undefined) {
return;
}
logger.debug('handling captured \'' + filtered.type + '\' event for key \'' + filtered.key + '\' from watcher \'' + this.device + '\'...');
if (filtered.delayed) {
logger.debug('delaying captured \'' + filtered.type + '\' event for \'' + filtered.key + '\' from watcher \'' + this.device + '\'');
logger.debug('delaying captured event...');
return;
}
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 (possibilities: \'' + filtered.combo.possibilities.length + '\')');
logger.debug('captured event is part of ' + filtered.combo.possibilities + ' possible combo(s) and not yet finished')
return;
}
logger.debug('captured \'' + filtered.combo.type.action + '\' event for \'' + filtered.combo.key + '\' from watcher \'' + this.device + '\' is the last part of a combo');
logger.debug('captured event finished combo \'' + filtered.combo.done.toString().toUpperCase()+ '\'');
}
this.keyfilter.resetCurrentCombo();
logger.info('executing command \'' + filtered.command.name + '\' registered for captured \'' + filtered.type + '\' event for \'' + filtered.key + '\' from watcher \'' + this.device + '\'');
logger.info('executing command \'' + filtered.command.name + '\' registered for captured event...');
cli.execute(filtered.command.cmd, filtered.command.args, filtered.command.sudo)
.then(logger.info)
.catch(logger.error);

View file

@ -4,11 +4,11 @@ const Watcher = require('./watcher.js');
const watchers = [];
async function initialize(config) {
if (config == undefined) {
if (config === undefined) {
throw new Error('could not initialize watchers, no config defined');
}
if (config.length == 0) {
throw new Error('no watchers in config \'' + global.config.path + '\' defined');
if (config.length === 0) {
throw new Error('no watchers defined');
}
for (var index = 0; index < config.length; index++) {
var watcher = new Watcher(config[index], watcherCallback);