// Chatter launcher daemon. Runs always (systemd service), reads the touch device
// (event3) WITHOUT grabbing it, and when it sees a multi-finger hold gesture
// (>= FINGERS contacts held >= HOLD_MS) while xochitl is the active app, it
// switches to Chatter. This is the "return to Chatter from the standard
// interface" trigger (xochitl has no plugin API to add a real button).

#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define DEV "/dev/input/event3"
#define MAX_SLOTS 16
#define FINGERS 4          // contacts required
#define HOLD_MS 700        // how long they must be held
#define COOLDOWN_MS 4000   // ignore re-triggers for this long

static long now_ms(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
}

int main(void)
{
    long lastTrigger = 0;
    fprintf(stderr, "chatter-launcher: started (fingers=%d hold=%dms)\n", FINGERS, HOLD_MS);

    // Outer loop: (re)open the device forever, so a read interruption (e.g. the
    // device sleeping/waking) never kills the daemon.
    for (;;) {
        int fd = open(DEV, O_RDONLY);
        if (fd < 0) { sleep(2); continue; }

        int slot = 0;
        int tid[MAX_SLOTS];
        for (int i = 0; i < MAX_SLOTS; i++)
            tid[i] = -1;
        long holdStart = 0;
        int triggered = 0;

        struct input_event ev;
        while (read(fd, &ev, sizeof(ev)) == (ssize_t)sizeof(ev)) {
            if (ev.type == EV_ABS) {
                if (ev.code == ABS_MT_SLOT) {
                    slot = ev.value;
                    if (slot < 0) slot = 0;
                    if (slot >= MAX_SLOTS) slot = MAX_SLOTS - 1;
                } else if (ev.code == ABS_MT_TRACKING_ID) {
                    tid[slot] = ev.value; // >=0 active, -1 released
                }
            } else if (ev.type == EV_SYN && ev.code == SYN_REPORT) {
                int active = 0;
                for (int i = 0; i < MAX_SLOTS; i++)
                    if (tid[i] >= 0) active++;

                const long t = now_ms();
                if (active >= FINGERS) {
                    if (holdStart == 0)
                        holdStart = t;
                    else if (!triggered && (t - holdStart) >= HOLD_MS &&
                             (t - lastTrigger) >= COOLDOWN_MS) {
                        triggered = 1;
                        lastTrigger = t;
                        if (system("systemctl is-active --quiet xochitl") == 0) {
                            fprintf(stderr, "chatter-launcher: gesture -> switching to Chatter\n");
                            system("/home/root/chatter/to-chatter.sh >/dev/null 2>&1 &");
                        }
                    }
                } else {
                    holdStart = 0;
                    triggered = 0;
                }
            }
        }

        close(fd);
        fprintf(stderr, "chatter-launcher: input read ended, reopening\n");
        sleep(1);
    }
    return 0;
}
