<commit_before>/*
 * The switch wrapper works by polling a file for events and
 * maintaining a list of callbacks.
 *
 * This polling activity is executed in a different thread to avoid blocking the
 * rest of the application. This thread is called the monitoring thread.
 *
 * The monitoring thread waits for events being written to /dev/input/event1. To do
 * so, it relies on the poll function whose return value indicates if some data can
 * be read or not. Once an event is read, the monitoring thread iterates through
 * the list of callbacks and call those who subscribed to this event. Notice, that
 * the monitoring thread never modifies (add/remove entries) the callback list.
 *
 * The rest of the code is executed in the main thread. To ensure that the
 * monitoring thread cannot use the list while a callback is added or removed,
 * a mutex is used.
 */

#include <fcntl.h>
#include <linux/input.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <letmecreate/core/switch.h>

#define DEVICE_FILE         "/dev/input/event0"
#define TIMEOUT             (20)        /* 20 ms timeout while polling */
#define SWITCH_1_CODE       (257)
#define SWITCH_2_CODE       (258)

static int fd = -1;
static pthread_t thread;
static pthread_mutex_t mutex;
static bool running = false;
static int switch_callback_ID = 0;

struct switch_callback
{
    int ID;
    uint8_t event_mask;
    void (*f)(void);
    struct switch_callback *next;
};
static struct switch_callback *callback_list_head = NULL;

static void process_event(uint8_t switch_event)
{
    struct switch_callback *cur = NULL;

    pthread_mutex_lock(&mutex);
    cur = callback_list_head;
    while (cur) {
        if (cur->event_mask & switch_event)
            cur->f();

        cur = cur->next;
    }
    pthread_mutex_unlock(&mutex);
}

static void* switch_update(void __attribute__ ((unused))*arg)
{
    int ret;
    struct input_event event[2];
    struct pollfd pfd;
    pfd.fd = fd;
    pfd.events = POLLIN;

    running = true;
    while (running) {
        /*
         * Polls on file /dev/input/event1.
         * The timeout is set to 20ms. Hence, the function will return after at most
         * 20ms. This is required to ensure that the boolean variable running
         * is checked frequently. If no timeout was given and no events detected,
         * the thread would be stuck in the poll function and would never exit even
         * if the running variable is set to false in switch_release().
         */
        ret = poll(&pfd, 1, TIMEOUT);

        if (ret < 0) {
            fprintf(stderr, "switch: Error while polling file descriptor\n");
            continue;
        } else if (ret == 0) {
            continue;
        } else {
            if (read(fd, &event, sizeof(event)) != sizeof(event)) {
                fprintf(stderr, "switch: Error while reading event from file descriptor\n");
                continue;
            }

            switch (event[0].code) {
            case SWITCH_1_CODE:
                if (event[0].value == 0)
                    process_event(SWITCH_1_RELEASED);
                else if (event[0].value == 1)
                    process_event(SWITCH_1_PRESSED);
                break;
            case SWITCH_2_CODE:
                if (event[0].value == 0)
                    process_event(SWITCH_2_RELEASED);
                else if (event[0].value == 1)
                    process_event(SWITCH_2_PRESSED);
                break;
            default:
                fprintf(stderr, "switch: Unrecognized event code\n");
                break;
            }
        }
    }

    return NULL;
}

int switch_init(void)
{
    if (fd >= 0)
        return 0;

    if ((fd = open(DEVICE_FILE, O_RDONLY)) < 0) {
        fprintf(stderr, "switch: Error while opening device file\n");
        return -1;
    }

    if (pthread_mutex_init(&mutex, NULL) != 0) {
        fprintf(stderr, "switch: Error while initialising mutex\n");
        return -1;
    }

    if (pthread_create(&thread, NULL, switch_update, NULL) != 0) {
        pthread_mutex_destroy(&mutex);
        close(fd);
        fd = -1;
        return -1;
    }

    return 0;
}

int switch_add_callback(uint8_t event_mask, void (*callback)(void))
{
    struct switch_callback *entry = NULL;
    if (fd < 0) {<commit_msg>switch: Find file descriptor path from /proc/bus/input/devices<commit_after>/*
 * The switch wrapper works by polling a file for events and
 * maintaining a list of callbacks.
 *
 * This polling activity is executed in a different thread to avoid blocking the
 * rest of the application. This thread is called the monitoring thread.
 *
 * The monitoring thread waits for events being written to /dev/input/event1. To do
 * so, it relies on the poll function whose return value indicates if some data can
 * be read or not. Once an event is read, the monitoring thread iterates through
 * the list of callbacks and call those who subscribed to this event. Notice, that
 * the monitoring thread never modifies (add/remove entries) the callback list.
 *
 * The rest of the code is executed in the main thread. To ensure that the
 * monitoring thread cannot use the list while a callback is added or removed,
 * a mutex is used.
 */

#include <fcntl.h>
#include <linux/input.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <letmecreate/core/common.h>
#include <letmecreate/core/switch.h>

#define TIMEOUT             (20)        /* 20 ms timeout while polling */
#define SWITCH_1_CODE       (257)
#define SWITCH_2_CODE       (258)

static int fd = -1;
static pthread_t thread;
static pthread_mutex_t mutex;
static bool running = false;
static int switch_callback_ID = 0;

struct switch_callback
{
    int ID;
    uint8_t event_mask;
    void (*f)(void);
    struct switch_callback *next;
};
static struct switch_callback *callback_list_head = NULL;

static int read_line(int fd, char *line)
{
    char c = 0;
    while(read(fd, &c, 1) == 1) {
        if (c == '\n') {
            *line = '\0';
            return 0;
        }
        *line = c;
        ++line;
    }

    return -1;
}

static int find_file_descriptor(char *file_descriptor_path)
{
    int ret = 0;
    char line[MAX_STR_LENGTH];
    char file_descriptor_name[30];
    bool found_gpio_keys_device = false;

    if ((fd = open("/proc/bus/input/devices", O_RDONLY)) < 0) {
        fprintf(stderr, "switch: Failed to open /proc/bus/input/devices.\n");
        return -1;
    }

    while((ret = read_line(fd, line)) == 0) {
        if (strstr(line, "N: Name=\"gpio_keys\"") != NULL)
            found_gpio_keys_device = true;

        if (found_gpio_keys_device
        && sscanf(line, "H: Handlers=%s", file_descriptor_name) == 1) {
            if (sprintf(file_descriptor_path, "/dev/input/%s", file_descriptor_name) < 0)
                ret = -1;
            goto find_file_descriptor_end;
        }
    }

find_file_descriptor_end:
    close(fd);
    return ret;
}

static void process_event(uint8_t switch_event)
{
    struct switch_callback *cur = NULL;

    pthread_mutex_lock(&mutex);
    cur = callback_list_head;
    while (cur) {
        if (cur->event_mask & switch_event)
            cur->f();

        cur = cur->next;
    }
    pthread_mutex_unlock(&mutex);
}

static void* switch_update(void __attribute__ ((unused))*arg)
{
    int ret;
    struct input_event event[2];
    struct pollfd pfd;
    pfd.fd = fd;
    pfd.events = POLLIN;

    running = true;
    while (running) {
        /*
         * Polls on file /dev/input/event1.
         * The timeout is set to 20ms. Hence, the function will return after at most
         * 20ms. This is required to ensure that the boolean variable running
         * is checked frequently. If no timeout was given and no events detected,
         * the thread would be stuck in the poll function and would never exit even
         * if the running variable is set to false in switch_release().
         */
        ret = poll(&pfd, 1, TIMEOUT);

        if (ret < 0) {
            fprintf(stderr, "switch: Error while polling file descriptor\n");
            continue;
        } else if (ret == 0) {
            continue;
        } else {
            if (read(fd, &event, sizeof(event)) != sizeof(event)) {
                fprintf(stderr, "switch: Error while reading event from file descriptor\n");
                continue;
            }

            switch (event[0].code) {
            case SWITCH_1_CODE:
                if (event[0].value == 0)
                    process_event(SWITCH_1_RELEASED);
                else if (event[0].value == 1)
                    process_event(SWITCH_1_PRESSED);
                break;
            case SWITCH_2_CODE:
                if (event[0].value == 0)
                    process_event(SWITCH_2_RELEASED);
                else if (event[0].value == 1)
                    process_event(SWITCH_2_PRESSED);
                break;
            default:
                fprintf(stderr, "switch: Unrecognized event code\n");
                break;
            }
        }
    }

    return NULL;
}

int switch_init(void)
{
    char file_descriptor_path[MAX_STR_LENGTH];

    if (fd >= 0)
        return 0;

    if (find_file_descriptor(file_descriptor_path) < 0)
        return -1;

    if ((fd = open(file_descriptor_path, O_RDONLY)) < 0) {
        fprintf(stderr, "switch: Error while opening device file\n");
        return -1;
    }

    if (pthread_mutex_init(&mutex, NULL) != 0) {
        fprintf(stderr, "switch: Error while initialising mutex\n");
        return -1;
    }

    if (pthread_create(&thread, NULL, switch_update, NULL) != 0) {
        pthread_mutex_destroy(&mutex);
        close(fd);
        fd = -1;
        return -1;
    }

    return 0;
}

int switch_add_callback(uint8_t event_mask, void (*callback)(void))
{
    struct switch_callback *entry = NULL;
    if (fd < 0) {
{'fraction_non_alphanumeric': 0.10142208176970176, 'fraction_numerical': 0.01224570412798736, 'mean_word_length': 2.2720516962843296, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6566685', 'n_tokens_mistral': 3169, 'n_tokens_neox': 3024, 'n_words': 993}