// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.

import { createReadStream } from 'fs';
import { nextTick } from 'process';
import { log } from './log.js';

const read = (b, off, size, bigE) => {
    let v = 0;
    let p, d;
    if (bigE) {
        p = off;
        d = 1;
    } else {
        p = off+size-1;
        d = -1;
    }
    for (let i = 0; i < size; i++) {
        v = (v << 8) + b.readUInt8(p);
        p += d;
    }
    return v;
}

// Parameter 'device' is device handle name, e.g. '/dev/input/event0'.
//
// Parameter 'eventSizes' is the list that is output from 'eventsizes.c'.
//
// Parameter 'actions' is a list of lists where each list is
//
//     [type, code, value, fun]
//
// where type, code, and value are integers that specify an event, and
// fun is a callback function with arguments (type, code, value,
// time), where time is milliseconds since the epoch.
//
// See /usr/include/linux/input-event-codes.h for meanings of event
// type and code. For normal key events the values are
//
// type:  1 (EV_KEY)
// code:  a key/button event code
// value: 1 for button pressed, 0 for released, 2 for repeat (events
//        with value 2 keep coming for repeating keys, until the key
//        is released, resulting in a value 0 event)
export const listenForEvents = (device, eventSizes, getCallable, actions) => {
    const [eventSize,
           secOff, secSize,
           usecOff, usecSize,
           typeOff, typeSize,
           codeOff, codeSize,
           valueOff, valueSize,
           bigE]
    = eventSizes;

    const k = (type, code, value) => [Number(type), Number(code), Number(value)].toString();

    const tbl = { };
    for (const e of actions) {
        tbl[k(e[0], e[1], e[2])] = e[3];
    }

    let saved = 0, evOff = 0;
    const saveBuf = Buffer.alloc(eventSize);
    createReadStream(device).on('data', chunk => {
        let off = 0;
        while (saved+off+eventSize <= chunk.length) {
            let b, p;
            if (saved) {
                chunk.copy(saveBuf, saved, off, eventSize-saved);
                b = saveBuf;
                p = 0;
            } else {
                b = chunk;
                p = off;
            }
            const type = read(b, p+typeOff, typeSize, bigE);
            const code = read(b, p+codeOff, codeSize, bigE);
            const value = read(b, p+valueOff, valueSize, bigE);
            log.debug([type, code, value]);
            const f = tbl[k(type, code, value)];
            if (f) {
                const sec = read(b, p+secOff, secSize, bigE);
                const usec = read(b, p+usecOff, usecSize, bigE);
                Promise.resolve()
                    .then(() => getCallable())
                    .then(callable => { f(callable, type, code, value, sec*1000+usec/1000) })
                    .catch(err => log.debug("Failed to handle event: "+err));
            }
            off += eventSize-saved;
            saved = 0;
        }
        if (saved+off < chunk.length) {
            chunk.copy(saveBuf, saved, off, chunk.length-off);
            saved += chunk.length-off;
        }
    }).on('error', err => { log.error("Error on device "+device) });
}

Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home