All Downloads are FREE. Search and download functionalities are using the official Maven repository.

native-glass.lens.input.udev.udevInput.c Maven / Gradle / Ivy

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE // for strcasestr
#endif

#include "LensCommon.h"
#include "input/LensInput.h"

#include "com_sun_glass_events_WindowEvent.h"
#include "com_sun_glass_events_KeyEvent.h"
#include "com_sun_glass_events_MouseEvent.h"
#include "com_sun_glass_events_TouchEvent.h"
#include "com_sun_glass_ui_lens_LensApplication.h"

#include "wm/LensWindowManager.h"

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


///// MACROS

// BIT handling macros
// TEST_BIT works on arrays of bytes
#define TEST_BIT(bit, array)  (array [bit / 8] & (1 << (bit % 8)))
// IS_BITSET and other similar macros work on arrays of unsigned longs
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define IS_BITSET(x,y) (((x)[LONG(y)] & BIT(y)) != 0)
#define SET_BIT(x,y) ((x)[LONG(y)] |= BIT(y))
#define OFF(x)   ((x)%BITS_PER_LONG)
#define LONG(x)  ((x)/BITS_PER_LONG)
#define BIT(x)   (1ul << OFF(x))

// event related macros
#define MAX_NUM_OF_DEVICES_SUPPORTED 20  // max number of concurrent connected devices
#define EVENTS_PER_READ 32 // number of queued events to try to handle at a time


//keyboard macros
#define LENSFB_KEY_PRESSED   1  // when key pressed
#define LENSFB_KEY_RELEASED  0  // when key released
#define LENSFB_KEY_REPEAT    2  // when key switches to repeating after short delay

//Only one touch screen is currently supported
#define TOUCH_SCREEN_ID 1

// The environment variable used to set the test input device
#define LENS_TEST_INPUT "LENS_TEST_INPUT"


////// data structures

typedef enum _LensInputTouchActionState {
    /* Initial state */
    TOUCH_DEFAULT,
    /* Press - withing the tapping radius */
    TOUCH_TAPPING,
    /* Press - crossed the tapping radius */
    TOUCH_DRAGGING,
    /* Release pending */
    TOUCH_RELEASING
} LensInputTouchActionState;


typedef struct _LensInputMouseState {
    /* device state */
    int                     x;
    int                     y;
    int                     rel[REL_MAX + 1];
    int                     abs[ABS_MAX + 1];
    int                     prevabs[ABS_MAX + 1];

    /* pending input events that have not yet been reported to the upper stack */
    struct input_event      *pendingInputEvents;
    int                     pendingInputEventCount;
    int                     pendingInputEventCapacity;

    /* holds the state of the touch action. */
    LensInputTouchActionState touchState;
    struct input_event releaseEvent;
    int pressedX;
    int pressedY;

} LensInputMouseState;

typedef struct {
    /* eventMask contains a bitset mask of supported event types, from 0 to
     * EV_MAX */
    unsigned long eventMask[NBITS(EV_CNT)];
    /* keybits contains a bitset mask of supported keys, from 0 to KEY_MAX */
    unsigned long keybits[NBITS(KEY_MAX + 1)];
    /* relbits contains a bitset mask of supported relative axes, from 0 to
     * REL_MAX */
    unsigned long relbits[NBITS(REL_MAX + 1)];
    /* absbits contains a bitset mask of supported absoluted axes, from 0 to
     * ABS_MAX */
    unsigned long absbits[NBITS(ABS_MAX + 1)];
    /* absinfo contains data for each axis on which absolute coordinate data
     * can be provided. struct input_absinfo is defined in linux/input.h */
    struct input_absinfo absinfo[ABS_MAX + 1];
} LensInputDeviceCapabilities;

typedef struct _LensInputDeviceInfo {
    char *name;
    char *sysPath; //absolute path  (under /sys),
    char *devNode; //virtual path (under /dev)
    char *productString; //string containing vendorID and productID
    unsigned int vendorId;
    unsigned int productId;

    /* device capabilities */
    LensInputDeviceCapabilities caps;

} LensInputDeviceInfo;

typedef struct _LensInputDevice {

    int deviceIndex;
    int fd;
    int type;
    void *state;
    LensInputDeviceInfo *info;
    jboolean isNotified; // did we notify Glass of this device's capabilities?
    jboolean isEnabled;
    jboolean isKeyboard;
    jboolean isPointer;
    jboolean isTouch;
    /* isTestDevice is JNI_TRUE for a device created by the test input handler */
    jboolean isTestDevice;

    /* timeout.tv_sec == 0 means no timeout.
     * timeout.tv_sec > 0 means active timeout request.*/
    struct timeval timeout;
    void (*onTimeoutFunc)(struct _LensInputDevice *);

    struct _LensInputDevice *previousDevice;
    struct _LensInputDevice *nextDevice;

} LensInputDevice;


/** Keybits for 5-way selector */
static const int KEYBITS_ARROWS[] = {
    KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 0
};
static const int KEYBITS_SELECT[] = {
    KEY_ENTER, KEY_SELECT, 0
};
/** Keybits for PC keyboard */
static const int KEYBITS_PC[] = {
    KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
    KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
    KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,
    KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
    KEY_LEFTSHIFT, KEY_TAB, 0
};

//// Global variables


/**
 * Describe the number of devices currently attached.
 */
static int gNumOfAttachedDevices = 0;

//Screen
static int screenWidth = 640;
static int screenHeight = 480;



//Mouse coordinates
static int mousePosX = 0;
static int mousePosY = 0;
static int newMousePosX = 0;
static int newMousePosY = 0;

// Touch
static const int gTapRadius = 20;//pixels
static const int gReleasePendingTimeout = 100;//pixels


//JNI
static JNIEnv *gJNIEnv = NULL;

static LensInputDevice *lensInputDevicesList_head = NULL;
static LensInputDevice *lensInputDevicesList_tail = NULL;


static int doLoop = 1;        //controlls the main polling loop

struct udev_monitor *udev_monitor;
/* Either eventLoop, udev monitor or test input monitor can have access to the
 * device list */
static pthread_mutex_t devicesLock = PTHREAD_MUTEX_INITIALIZER;
/* File descriptor used for polling input devices */
static int epollFd;
/* File descriptor used for the test input monitor */
static int testInputFD = -1;

//// Forward declarations


static jboolean lens_input_deviceCheckProperties(LensInputDevice *device,
                                                 const char *key,
                                                 const char *value);
static void lens_input_listAdd(LensInputDevice *device);
static LensResult lens_input_deviceInitCapabilities(LensInputDevice *device);
static LensResult lens_input_deviceOpen(JNIEnv *env, LensInputDevice *device);
static LensResult lens_input_deviceGrab(LensInputDevice *device, int grab);
static LensResult lens_input_mouseStateAllocateAndInit(LensInputDevice *newDevice);
static void lens_input_printDevices();
static void lens_input_deviceRemove(JNIEnv *env, LensInputDevice *device);
void lens_input_eventLoop(JNIEnv *env, void *handle);
static void lens_input_pointerEvents_handleEvent(LensInputDevice *device,
                                                 struct input_event *event);
static void lens_input_keyEvents_handleEvent(LensInputDevice *device,
                                             struct input_event *event);
static void lens_input_pointerEvents_handleSync(LensInputDevice *device);
static void lens_input_pointerEvents_handleRelMotion(LensInputDevice *device,
        struct input_event *pointerEvent);
static void lens_input_pointerEvents_handleAbsMotion(LensInputDevice *device,
        struct input_event *pointerEvent);
static void lens_input_pointerEvents_enqueuePendingEvent(LensInputMouseState *mouseState,
        struct input_event *event);
static void lens_input_pointerEvents_handleTimeout(LensInputDevice *device);

static void lens_input_deviceRelease(JNIEnv *env, LensInputDevice *device);
static void lens_input_printEvent(struct input_event event);
static LensResult lens_input_testInputHandleEvent(JNIEnv *env);
static void lens_input_deviceInfoRelease(LensInputDevice *device);
static void lens_input_deviceNotify(JNIEnv *env,
                                    LensInputDevice *device, jboolean attach);

// udev functions
static void lens_input_udevFindDevices(JNIEnv *env);
static jboolean lens_input_isUdevDeviceExists(struct udev_device *udev_device,
                                              LensInputDevice **device);
static jboolean lens_input_isDeviceExists(LensInputDevice *device);
static LensInputDevice *lens_input_deviceAllocateAndInit(JNIEnv *env,
        struct udev_device *udev_device);
static jboolean lens_input_udevMonitorStart(JNIEnv *env);
void lens_input_udevMonitorLoop(JNIEnv *env, void *handle);
static void lens_input_udevMonitorHandleEvent(JNIEnv *env);
static LensInputDeviceInfo *lens_input_deviceInfoAllocateAndInit(struct udev_device *udev_device,
        LensInputDevice *device);
static LensResult lens_input_udevParseProductID(struct udev_device *udev_device,
                                                unsigned int *vendorId,
                                                unsigned int *productId);

// test input functions
static void lens_input_testInputMonitorLoop(JNIEnv *env, void *handle);
static LensResult lens_input_testInputRead(void *_p, size_t n);
static LensResult lens_input_testInputReadInt(jint *i);
static LensResult lens_input_testInputReadString(char **pS);
static LensResult lens_input_testInputReadBitSet(unsigned long *bitset, int max);

///// stubs for connectivity with fbInputs - will be removed



///////////////////



//// Initialization section

/**
 * Initialize the input devices and start listening to events
 *
 * @param env
 */
jboolean lens_input_initialize(JNIEnv *env) {

    screenWidth = glass_screen_getMainScreen()->width;
    screenHeight = glass_screen_getMainScreen()->height;

    GLASS_LOG_FINE("screen size=%ix%i", screenWidth, screenHeight);

    lens_wm_setPointerPosition(screenWidth / 2, screenHeight / 2);

    glass_application_request_native_event_loop(env, &lens_input_eventLoop, NULL);

    return JNI_TRUE;

}

/**
 * Traverse /dev/input for input devices.
 * When supported input device recognized they will be added to
 * LensInputDevicesList
 *
 */
static void lens_input_udevFindDevices(JNIEnv *env) {
    struct udev *udev;
    struct udev_enumerate *enumerate;
    struct udev_list_entry *udev_devices, *udev_device;
    LensInputDevice *device;

    udev = udev_new();
    if (!udev) {
        GLASS_LOG_SEVERE("Can't create udev\n");
        exit(-1);
    }

    GLASS_LOG_CONFIG("Enumerating input devices... start");

    enumerate = udev_enumerate_new(udev);
    /* Create a list of the devices in the 'input' subsystem. */
    udev_enumerate_add_match_subsystem(enumerate, "input");


    udev_enumerate_scan_devices(enumerate);
    udev_devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(udev_device, udev_devices) {

        const char *syspath = udev_list_entry_get_name(udev_device);
        struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);

        GLASS_LOG_FINER("Device syspath = %s", syspath);

        if (!udev_device) {
            GLASS_LOG_FINER("No udev_device, continue");
            continue;
        }

        //check that device support input events
        if (udev_device_get_property_value(udev_device, "ID_INPUT")) {
            //add device if not exists
            if (!lens_input_isUdevDeviceExists(udev_device, NULL)) {
                device = lens_input_deviceAllocateAndInit(env, udev_device);
                if (device) {
                    lens_input_listAdd(device);
                }
            } else {
                GLASS_LOG_FINE("Device %s allready registered",
                               udev_device_get_devpath(udev_device));
            }
        } else {
            GLASS_LOG_FINE("ignoring device without input capabilities [device path %s]",
                           udev_device_get_devpath(udev_device));
        }
        //Free device object
        udev_device_unref(udev_device);
    }

    /* Free the enumerator object */
    udev_enumerate_unref(enumerate);

    /* Free udev object*/
    udev_unref(udev);

    lens_input_printDevices();
    GLASS_LOG_CONFIG("Enumerating input devices... finished");
}

/**
 * Allocate a LensInputDevice and init its fields according the
 * information from the udev_device
 *
 *
 * @param udev_device
 *
 * @return LensInputDevice* NULL if device is not valid, or
 *         error occurred
 */
static LensInputDevice *lens_input_deviceAllocateAndInit(JNIEnv *env,
        struct udev_device *udev_device) {

    const char *key, *value;
    struct udev_list_entry *set, *entry;

    LensInputDevice *device = calloc(1, sizeof(LensInputDevice));
    GLASS_LOG_FINE("Allocated device %p", device);

    const char *path = udev_device_get_devnode(udev_device);
    LensInputDeviceInfo *info;


    jboolean isValidDevice = JNI_FALSE;

    if (!device) {
        GLASS_LOG_SEVERE("Failed to allocate LensInputDevice");
        return NULL;
    }

    device->fd = -1;
    device->timeout.tv_sec = 0;
    device->onTimeoutFunc = NULL;

    info = lens_input_deviceInfoAllocateAndInit(udev_device, device);
    if (!info) {
        GLASS_LOG_FINE("Failed to allocate LensInputDeviceInfo");
        lens_input_deviceRelease(env, device);
        return NULL;
    }

    GLASS_LOG_CONFIG("Trying to register %s [%s] as an input device", info->name, info->devNode);

    device->info = info;

    //traverse the device properties
    set = udev_device_get_properties_list_entry(udev_device);

    udev_list_entry_foreach(entry, set) {


        key = udev_list_entry_get_name(entry);
        if (!key) {
            continue;
        }
        value = udev_list_entry_get_value(entry);

        isValidDevice |= lens_input_deviceCheckProperties(device, key, value);
    }

    if (!isValidDevice) {
        GLASS_LOG_CONFIG("Device is not a valid input device (not a keyboard/mouse/touch), skipping");
        lens_input_deviceRelease(env, device);
        return NULL;
    }

    if (lens_input_deviceOpen(env, device)) {
        return NULL;
    }

    return device;
}

static jboolean lens_input_deviceCheckProperties(LensInputDevice *device,
                                                 const char *key,
                                                 const char *value) {
    jboolean isValidDevice = JNI_FALSE;

    GLASS_LOG_FINER("key[%s]=>value[%s]\n", key, value);
    if (!strcmp(key, "ID_INPUT_KEYBOARD")) {
        device->isKeyboard = JNI_TRUE;
        isValidDevice = JNI_TRUE;
        GLASS_LOG_FINE("Device is a keyboard");
    } else if (!strcmp(key, "ID_INPUT_MOUSE")) {
        device->isPointer = JNI_TRUE;
        isValidDevice = JNI_TRUE;
        GLASS_LOG_FINE("Device is a pointer");
    } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) {
        device->isTouch = JNI_TRUE;
        isValidDevice = JNI_TRUE;
        GLASS_LOG_FINE("Device is a touch screen");
    }
    return isValidDevice;
}

/**
 * Allocate a LensInputDeviceInfo, init it from the udev_device
 * information and attach it to a LensInputDevice
 *
 * @param udev_device
 * @param device
 *
 * @return LensInputDeviceInfo*  NULL if device is not valid, or
 *         error occurred
 */
static LensInputDeviceInfo *lens_input_deviceInfoAllocateAndInit(struct udev_device *udev_device,
        LensInputDevice *device) {
    const char *devNode, *sysPath, *product = "", *name = "";
    struct udev_device *parent;
    LensInputDeviceInfo *info;

    unsigned int vendorId, productId;

    device->info = NULL;

    //first get the name of the device, its important to initialize internal device data
    //especially when using virtual devices (uinput)
    parent = udev_device_get_parent(udev_device);
    if (parent) {

        product = udev_device_get_property_value(parent, "PRODUCT");
        if (!product) {
            product = "";
        }

        name = udev_device_get_sysattr_value(parent, "name");
        if (!name) {
            name = udev_device_get_property_value(parent, "NAME");
        }
    }

    if (!name) {
        name = "";
    }

    sysPath = udev_device_get_syspath(udev_device);
    //all devices must have a /sys path
    if (!sysPath || !strcmp(sysPath, "")) {
        GLASS_LOG_FINE("Device dosen't have a valid sys path - skipping");
        return NULL;
    }

    devNode = udev_device_get_devnode(udev_device);

    //Some devices don't have a /dev node,but the ones we are instrested in do
    if (!devNode || !strcmp(devNode, "")) {
        GLASS_LOG_FINE("Device %s dosen't have a valid dev node - skipping", sysPath);
        return NULL;
    }

    info = (LensInputDeviceInfo *)calloc(1, sizeof(LensInputDeviceInfo));
    GLASS_LOG_FINE("Allocated device info %p", info);

    if (!info) {
        return NULL;
    }

    lens_input_udevParseProductID(udev_device, &info->vendorId, &info->productId);

    info->devNode = strdup(devNode);
    info->sysPath = strdup(sysPath);
    info->productString = strdup(product);
    info->name = strdup(name);

    device->info = info;

    if (!info->devNode ||
            !info->sysPath ||
            !info->productString ||
            !info->name) {
        GLASS_LOG_SEVERE("Failed to copy strings\n");
        lens_input_deviceInfoRelease(device);
        device->info = NULL;
        return NULL;
    }

    return info;
}

#define ABS_UNSET   -65535

/**
 * Configure device to be a pointer device. Used for mouse,
 * touch screen, etc.
 *
 * @param device  the device
 *
 * @return LensResult LENS_OK on success
 */
static LensResult lens_input_mouseStateAllocateAndInit(LensInputDevice *device) {

    LensInputDeviceCapabilities *caps = &device->info->caps;
    LensInputMouseState *state;

    if (device->state) {
        GLASS_LOG_FINE("Pointer is already initialized for this device [%s]",
                       device->info->name);
        return LENS_OK;
    }

    GLASS_LOG_CONFIG("Setting up mouse for %s", device->info->name);
    device->state = calloc(1, sizeof(LensInputMouseState));
    GLASS_LOG_FINE("Allocated device pointer state %p", device->state);

    if (!device->state) {
        GLASS_LOG_SEVERE("Failed to allocate LensInputMouseState");
        return LENS_FAILED;
    }

    state = (LensInputMouseState *) device->state;
    if (IS_BITSET(caps->eventMask, EV_ABS)) {
        int i;
        for (i = 0; i <= ABS_MAX; i++) {
            state->prevabs[i] = ABS_UNSET;
        }
    }

    state->touchState = TOUCH_DEFAULT;
    state->pressedX = 0;
    state->pressedY = 0;

    return LENS_OK;
}

static LensResult lens_input_deviceOpen(JNIEnv *env, LensInputDevice *device) {
    device->fd = open(device->info->devNode, O_RDONLY);
    GLASS_LOG_FINE("open(%s) returned %i", device->info->devNode, device->fd);

    if (device->fd == -1) {
        GLASS_LOG_SEVERE("Failed to open %s [%s], %s",
                         device->info->name, device->info->devNode,
                         strerror(errno));
        lens_input_deviceRelease(env, device);
        return LENS_FAILED;
    }
    if (lens_input_deviceInitCapabilities(device)) {
        return LENS_FAILED;
    }
    if (device->isPointer || device->isTouch) {
        if (lens_input_mouseStateAllocateAndInit(device) != LENS_OK) {
            GLASS_LOG_SEVERE("Failed to setup pointer device");
            lens_input_deviceRelease(env, device);
            return LENS_FAILED;
        }
    }

    if (lens_input_deviceGrab(device, 1)) {
        GLASS_LOG_SEVERE("Failed to grab pointer device");
        lens_input_deviceRelease(env, device);
        return LENS_FAILED;
    }

    GLASS_LOG_CONFIG("Device %s registered for inputs", device->info->name);
    lens_input_deviceNotify(env, device, JNI_TRUE);
    return LENS_OK;
}

/**
 * Notify Glass when a device is attached or detached
 */
static void lens_input_deviceNotify(JNIEnv *env,
                                    LensInputDevice *device, jboolean attach) {
    jint flags = 0;
    int i;
    jboolean is5Way;
    jboolean isPCKeyboard;
    if (attach && device->isNotified) {
        return; // already told Glass about this device
    }
    if (!attach && !device->isNotified) {
        return; // don't notify on detachment if we did not notify on attachment
    }
    if (device->isTouch) {
        flags |= 1 << com_sun_glass_ui_lens_LensApplication_DEVICE_TOUCH;
    } else if (device->isPointer) {
        flags |= 1 << com_sun_glass_ui_lens_LensApplication_DEVICE_POINTER;
    }
    unsigned long *keybits = &device->info->caps.keybits[0];
    is5Way = JNI_TRUE;
    for (i = 0; KEYBITS_ARROWS[i] != 0; i++) {
        int key = KEYBITS_ARROWS[i];
        if (!IS_BITSET(keybits, key)) {
            is5Way = JNI_FALSE;
            GLASS_LOG_CONFIG("Not a 5-way, missing key %i", key);
            break;
        }
    }
    if (is5Way) {
        jboolean hasSelect = JNI_FALSE;
        for (i = 0; KEYBITS_SELECT[i] != 0; i++) {
            int key = KEYBITS_SELECT[i];
            if (IS_BITSET(keybits, key)) {
                GLASS_LOG_CONFIG("Is a 5-way, has arrow keys and key %i", key);
                hasSelect = JNI_TRUE;
                break;
            }
        }
        if (!hasSelect) {
            GLASS_LOG_CONFIG("Not a 5-way, has arrow keys but no select key");
        }
        is5Way = hasSelect;
    }
    if (is5Way) {
        flags |= 1 << com_sun_glass_ui_lens_LensApplication_DEVICE_5WAY;
        // a 5-way selector could also be a PC keyboard
        jboolean isPCKeyboard = JNI_TRUE;
        for (i = 0; KEYBITS_PC[i] != 0; i++) {
            int key = KEYBITS_PC[i];
            if (!IS_BITSET(keybits, key)) {
                isPCKeyboard = JNI_FALSE;
                GLASS_LOG_CONFIG("Not a PC keyboard, missing key %i", key);
                break;
            }
        }
        if (isPCKeyboard) {
            GLASS_LOG_CONFIG("Is a PC keyboard");
            flags |= 1 << com_sun_glass_ui_lens_LensApplication_DEVICE_PC_KEYBOARD;
        }
    }
    glass_application_notifyDeviceEvent(env, flags, attach);
    if (attach) {
        device->isNotified = JNI_TRUE; // record that we notified Glass about the device
    } else {
        device->isNotified = JNI_FALSE; // Glass no longer knows about the device
    }
}

/**
 * Close all registered devices that where opened by
 * lens_input_initialize() and free their resources
 */
void lens_input_shutdownDevices(JNIEnv *env) {

    LensInputDevice *device = lensInputDevicesList_head;
    LensInputDevice *nextDevice;
    while (device) {
        nextDevice = device->nextDevice;
        lens_input_deviceRemove(env, device);
        device = nextDevice;
    }
    if (testInputFD >= 0) {
        GLASS_LOG_FINE("close(%i) (test input monitor)", testInputFD);
        close(testInputFD);
    }
}

/**
 * Grabs or releases a device
 *
 * @param grab 1 to grab a device, 0 to release it
 */
LensResult lens_input_deviceGrab(LensInputDevice *device, int grab) {
    if (device->isTestDevice) {
        // this is a test device, we don't need to grab or release it
        return LENS_OK;
    }
    GLASS_LOG_FINER("ioctl(%s, EVIOCGRAB, %i)", device->info->name, grab);
    if (ioctl(device->fd, EVIOCGRAB, grab) < 0) {
        if (grab) {
            GLASS_LOG_SEVERE("Grabbing device [%s] failed - %s",
                             device->info->name, strerror(errno));
        } else {
            GLASS_LOG_WARNING("Ungrabbing device %s [fd-%i] failed - %s",
                              device->info->name, device->fd, strerror(errno));
        }
        return LENS_FAILED;
    } else {
        return LENS_OK;
    }
}

/** Wraps a call to ioctl of type EVIOCGBIT, reporting a SEVERE error if the
 * call fails */
static LensResult eviocgbit(LensInputDevice *device,
                            int type, size_t dstLength, void *dst) {
    GLASS_LOG_FINEST("ioctl(%s, EVIOCGBIT %i)", device->info->name, type);
    if (ioctl(device->fd, EVIOCGBIT(type, dstLength), dst) < 0) {
        GLASS_LOG_SEVERE("EVIOCGBIT(%i) error %i: %s", type,
                         errno, strerror(errno));
        return LENS_FAILED;
    } else {
        return LENS_OK;
    }
}

/**
 * Sets up the capabilities of a device
 *
 * On success returns either LENS_OK with the LensInputDeviceCapabilities
 * structure of the given device filled in. On failure, returns LENS_FAILED and
 * logs a SEVERE error.
 */
static LensResult lens_input_deviceInitCapabilities(LensInputDevice *device) {
    if (device->isTestDevice) {
        return LENS_OK;
    } else {
        LensInputDeviceCapabilities *caps = &device->info->caps;
        if (eviocgbit(device, 0 /* EV_ */,
                      sizeof(caps->eventMask), &caps->eventMask)) {
            return LENS_FAILED;
        }
        if (IS_BITSET(caps->eventMask, EV_KEY)) {
            GLASS_LOG_CONFIG("Init keybits");
            if (eviocgbit(device, EV_KEY,
                          sizeof(caps->keybits), &caps->keybits)) {
                return LENS_FAILED;
            }
        }
        if (IS_BITSET(caps->eventMask, EV_REL)) {
            if (eviocgbit(device, EV_REL,
                          sizeof(caps->relbits), &caps->relbits)) {
                return LENS_FAILED;
            }
        }
        if (IS_BITSET(caps->eventMask, EV_ABS)) {
            int axis;
            if (eviocgbit(device, EV_ABS,
                          sizeof(caps->absbits), &caps->absbits)) {
                return LENS_FAILED;
            }
            for (axis = 0; axis <= ABS_MAX; axis++) {
                if (IS_BITSET(caps->absbits, axis)) {
                    GLASS_LOG_FINEST("ioctl(%s, EVIOCABS %i)",
                                     device->info->name, axis);
                    if (ioctl(device->fd,
                              EVIOCGABS(axis), &caps->absinfo[axis]) < 0) {
                        GLASS_LOG_SEVERE("EVIOCGABS(%i) error %i: %s",
                                         axis, errno, strerror(errno));
                        return LENS_FAILED;
                    }
                    GLASS_LOG_CONFIG("Range for axis 0x%02x is %i..%i", axis,
                                     caps->absinfo[axis].minimum,
                                     caps->absinfo[axis].maximum);
                }
            }
        }
        return LENS_OK;
    }
}

//// Initialization section - END


/// epoll functions

/**
 * Remove and disable device notifications
 *
 * @param device the device to remove
 */
void lens_input_epollRemoveDevice(LensInputDevice *device) {

    int ret;

    if (device) {

        //remove from epoll list

        GLASS_LOG_FINE("epollctl(%i, EPOLL_CTL_DEL, fd=%i)",
                       epollFd, device->fd);
        ret = epoll_ctl(epollFd, EPOLL_CTL_DEL, device->fd, NULL);

        if (ret == -1) {
            GLASS_LOG_SEVERE("Failed to EPOLL_CTL_DEL %s to epoll - [errno %i] %s",
                             device->info->name, errno, strerror(errno));
        }

        device->isEnabled = JNI_FALSE;
    }
}

/**
 * Enable notification for a device
 *
 * @param device the device to add into notification pool
 */
void lens_input_epolladdDevice(LensInputDevice *device) {

    struct epoll_event epollEvent;
    int ret;

    if (device) {

        //init
        memset(&epollEvent, 0, sizeof(epollEvent));
        epollEvent.events = EPOLLIN | EPOLLET;
        epollEvent.data.ptr = device;

        //add device to epoll list

        GLASS_LOG_FINE("epollctl(%i, EPOLL_CTL_ADD, fd=%i, device=%p)",
                       epollFd, device->fd, device);
        ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, device->fd, &epollEvent);
        if (ret == -1) {
            GLASS_LOG_WARNING("Failed to add %s to epoll, skipping - [errno %i] %s",
                              device->info->name, errno, strerror(errno));
        } else {
            device->isEnabled = JNI_TRUE;
        }
    }
}


///////// event handling
/**
 * Returns the timeout value, in millis, for epoll_wait(). This value is the max time
 * to be blocked on a call to epoll_wait().
 * @return timeout value, in millis, for epoll_wait(). -1 means no timeout.
 */
static int lens_input_eventLoop_checkTimeout(void) {

    struct timeval minTimeout = {INT_MAX, 0};
    struct timeval currentTime;
    LensInputDevice *device;

    gettimeofday(¤tTime, NULL);

    device = lensInputDevicesList_head;
    while (device) {
        if (device->timeout.tv_sec != 0) {
            if (timercmp(&device->timeout, ¤tTime, <=)) {
                //This is an immediate timeout
                minTimeout = currentTime;
                break;
            }
            if (timercmp(&device->timeout, &minTimeout, <)) {
                minTimeout = device->timeout;
            }
        }
        device = device->nextDevice;
    }


    if (minTimeout.tv_sec < INT_MAX) {
        struct timeval delta;
        timersub(&minTimeout, ¤tTime, &delta);

        return (int)(delta.tv_sec * 1000 + delta.tv_usec / 1000);
    }

    return -1;
}




/**
 * Checks if one of the devices needs a timeout call.
 */
static void lens_input_eventLoop_timeoutEvent(void) {

    LensInputDevice *device = lensInputDevicesList_head;
    struct timeval currentTime;

    gettimeofday(¤tTime, NULL);

    while (device) {
        if (device->timeout.tv_sec != 0 && timercmp(&device->timeout, ¤tTime, <=)) {
            // Timeout occurred
            device->timeout.tv_sec = 0;
            device->timeout.tv_usec = 0;
            if (device->onTimeoutFunc) {
                (device->onTimeoutFunc)(device);
            }
        }
        device = device->nextDevice;
    }
}


/**
 * The main event loop that polls events from the system and
 * later call the relevant event handlers.
 * A nativeEventLoopCallback() implementation
 *
 * @param env
 * @param handle always NULL at this point, required as
 *               nativeEventLoopCallback signature
 */
void lens_input_eventLoop(JNIEnv *env, void *handle) {

    int numOfEpollEvents;
    struct input_event events[EVENTS_PER_READ];
    struct epoll_event epollEvent;
    struct epoll_event *epollEvents = calloc(MAX_NUM_OF_DEVICES_SUPPORTED,
                                             sizeof(struct epoll_event));
    int i;
    LensInputDevice *device;
    const char *testInputPath = getenv(LENS_TEST_INPUT);
    jboolean useTestInput;
    gJNIEnv = env;

    if (epollEvents == NULL) {
        GLASS_LOG_SEVERE("Failed to alloc epollEvents - [errno %i] %s", errno, strerror(errno));
        exit(-1);
    }
    GLASS_LOG_FINE("Allocated epollEvents %p", epollEvents);

    useTestInput = testInputPath != NULL && strlen(testInputPath) > 0;

    if (!useTestInput) {
        // find and register our devices
        lens_input_udevFindDevices(env);
    }

    //+1 is to make sure we don't call epoll_create(0) that might
    //cause an 'invalid argument' error when no devices are connected/detected
    epollFd =  epoll_create(gNumOfAttachedDevices + 1);

    if (epollFd == -1) {
        GLASS_LOG_SEVERE("Failed to create epoll - [errno %i] %s", errno, strerror(errno));
        exit(-1);
    }

    GLASS_LOG_FINER("epollFd = %i\n", epollFd);

    //register the devices we want to get input events from
    device = lensInputDevicesList_head;
    while (device) {
        lens_input_epolladdDevice(device);
        device = device->nextDevice;
    }

    //grab the lock so the event loop will start before monitor events
    pthread_mutex_lock(&devicesLock);

    //start monitoring hot plug
    if (useTestInput) {
        glass_application_request_native_event_loop(
            env, lens_input_testInputMonitorLoop, (void *) testInputPath);
    } else {
        lens_input_udevMonitorStart(env);
    }

    while (doLoop) {
        int epoll_errno;
        int epoll_timeout = lens_input_eventLoop_checkTimeout();;

        //Before wait release the lock
        GLASS_LOG_FINER("Releasing lock before epoll_wait()");
        pthread_mutex_unlock(&devicesLock);


        if (epoll_timeout >= 0) {
            numOfEpollEvents = epoll_wait(epollFd, epollEvents,
                                          MAX_NUM_OF_DEVICES_SUPPORTED, epoll_timeout);
        } else {
            numOfEpollEvents = epoll_wait(epollFd, epollEvents,
                                          MAX_NUM_OF_DEVICES_SUPPORTED, -1);
        }

        epoll_errno = errno;

        GLASS_LOG_FINEST("epoll_wait(fd=%i) returned() %i",
                         epollFd, numOfEpollEvents);

        //we got input event(s), process them before udev monitor will chage
        //stuff around
        GLASS_LOG_FINER("Trying to capture lock before reading events");
        pthread_mutex_lock(&devicesLock);
        GLASS_LOG_FINER("lock captured");

        if (numOfEpollEvents == -1) {
            if (epoll_errno == EINTR) {
                //we got interrupted
                GLASS_LOG_FINER("epoll_wait(): %s", strerror(epoll_errno));
            } else {
                GLASS_LOG_WARNING("epoll_wait(): error %i (%s)",
                                  epoll_errno, strerror(epoll_errno));
            }
            continue;
        } else if (numOfEpollEvents == 0) {
            lens_input_eventLoop_timeoutEvent();
            continue;
        }

        for (i = 0 ; i < numOfEpollEvents ; i++) {
            epollEvent = epollEvents[i];
            device = (LensInputDevice *)epollEvent.data.ptr;
            GLASS_LOG_FINEST("epoll event %i out of %i, device=%p",
                             i, numOfEpollEvents, device);
            if (!lens_input_isDeviceExists(device)) {
                GLASS_LOG_FINE("Device %p doesn't exist anymore, skipping event", device);
                continue;
            }

            GLASS_LOG_FINEST("events=0x%x, device=%p (%s), device->fd=%d",
                             epollEvent.events,
                             device,
                             (device) ? device->info->name : NULL,
                             (device) ? device->fd : -1);
            //error handling
            if ((epollEvent.events & EPOLLERR) ||
                    (epollEvent.events & EPOLLHUP) ||
                    (!(epollEvent.events & EPOLLIN))) {
                int ret;
                /* An error has occurred on this fd, or the socket is not
                   ready for reading (why were we notified then?) */
                GLASS_LOG_FINEST("epoll error");

                /* Explicitly remove the item from the epoll list
                   udev monitor will remove the device from
                   lensInputDevicesList*/
                lens_input_epollRemoveDevice(device);
                continue;
            }

            //handle events
            if (device) {
                int numOfEvents;
                int eventIndex;

                int numOfBytesRead = read(device->fd, events, sizeof(struct input_event) * EVENTS_PER_READ);

                if (numOfBytesRead < 0 && errno != EINTR) {
                    GLASS_LOG_SEVERE("error reading %s, fd=%i, errno=%i (%s)",
                                     device->info->name, device->fd, errno, strerror(errno));
                    lens_input_epollRemoveDevice(device);
                    continue;
                } else if (numOfBytesRead < 0 && errno == EINTR) {
                    GLASS_LOG_WARNING("reading interrupted on %s, fd=%i",
                                      device->info->name, device->fd);
                    continue;
                } else {

                    numOfEvents = numOfBytesRead / (int)sizeof(struct input_event);

                    GLASS_LOG_FINEST("Got event on %s, count=%i",
                                     device->info->name, numOfEvents);


                    for (eventIndex = 0; eventIndex < numOfEvents; eventIndex ++) {

                        lens_input_printEvent(events[eventIndex]);

                        if (device->isKeyboard) {
                            lens_input_keyEvents_handleEvent(device, &events[eventIndex]);
                        } else if (device->isPointer || device->isTouch) {
                            lens_input_pointerEvents_handleEvent(device, &events[eventIndex]);
                        }
                    }
                }

            } else {
                GLASS_LOG_WARNING("Null device, skipping event");
                continue;
            }
        }//for
    }//while
}

////// mouse and touch events handling

/**
 * Called to trigger timeout in current time + timeout
 * milliseconds.
 * @param device the for this timeout.
 * @param timeout timeout value in milliseconds.
 * @param func the function that will be called on timeout. If
 *        func == NULL, the timeout will be canceled.
 */
static void lens_input_eventLoop_triggerTimeout(LensInputDevice *device, int timeout,
                                                void (*func)(LensInputDevice *)) {

    struct timeval tmp;
    struct timeval current;

    if (func == NULL) {
        device->timeout.tv_sec = 0;
        device->onTimeoutFunc = NULL;
        return;
    }

    gettimeofday(¤t, NULL);
    tmp.tv_sec = 0;
    tmp.tv_usec = timeout * 1000;

    device->onTimeoutFunc = func;

    //Set timeout in another *timeout* millis
    timeradd(¤t, &tmp, &device->timeout);
}




/**
 * Service function that translate FB button code into FX code.
 * @return  int com_sun_glass_events_MouseEvent_BUTTON_*
 */

int lens_input_convertButtonToFXButtonCode(int button) {
    switch (button) {
        case 0:
            return com_sun_glass_events_MouseEvent_BUTTON_NONE;
        case BTN_LEFT:
        case BTN_TOUCH:
            return com_sun_glass_events_MouseEvent_BUTTON_LEFT;
        case BTN_MIDDLE:
            return com_sun_glass_events_MouseEvent_BUTTON_OTHER;
        case BTN_RIGHT:
            return com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
        default:
            GLASS_LOG_WARNING("Error: unknown button=%02d return NONE", button);
            return com_sun_glass_events_MouseEvent_BUTTON_NONE;
    }
}





/**
 * Handle pointer device events
 *
 * @param device the device that produced the event
 * @param event the event produced
 */
static void lens_input_pointerEvents_handleEvent(LensInputDevice *device,
                                                 struct input_event *event) {

    lens_input_printEvent(*event);

    switch (event->type) {
        case EV_SYN:
            lens_input_pointerEvents_handleSync(device);
            break;
        case EV_KEY:
        case EV_REL:
        case EV_ABS:
            lens_input_pointerEvents_enqueuePendingEvent(
                (LensInputMouseState *)device->state, event);
            break;
        default:
            GLASS_LOG_FINEST("unsupported event Mouse type=0x%x code=%i value=%i"
                             " - skipping", event->type, event->code, event->value);
    }
}

/**
 * Handle pointer absolute coordinates notification
 *
 * @param device the device that produced the event
 * @param pointerEvent the event produced
 */
static void lens_input_pointerEvents_handleAbsMotion(LensInputDevice *device,
        struct input_event *pointerEvent) {
    LensInputMouseState *mouseState = device->state;
    LensInputDeviceCapabilities *caps = &device->info->caps;
    int axis = pointerEvent->code;
    float scalar;

    // Handle absolute coordinate changes
    // This only works for direct touch devices such as touch screens
    // but not devices that need to be converted to relative motion
    // such as a touchpad

    mouseState->abs[axis] = pointerEvent->value;
    if (mouseState->abs[axis] < caps->absinfo[axis].minimum) {
        mouseState->abs[axis] = caps->absinfo[axis].minimum;
    }
    if (mouseState->abs[axis] > caps->absinfo[axis].maximum) {
        mouseState->abs[axis] = caps->absinfo[axis].maximum;
    }
    scalar = ((pointerEvent->value - caps->absinfo[axis].minimum))
             / (float)(caps->absinfo[axis].maximum - caps->absinfo[axis].minimum);
    GLASS_LOG_FINER("Absolute motion on axis 0x%02x, value = %i..%i, value=%i, scalar=%f\n",
                    axis, caps->absinfo[axis].minimum, caps->absinfo[axis].maximum,
                    pointerEvent->value, scalar);
    switch (axis) {
        case ABS_X:
            newMousePosX = (int) roundf(scalar * screenWidth);
            break;
        case ABS_Y:
            newMousePosY = (int) roundf(scalar * screenHeight);
            break;
    }
    GLASS_LOG_FINER("Pointer absolute axis 0x%02x is now %i, pointer at %i,%i",
                    axis, mouseState->abs[axis], newMousePosX, newMousePosY);
    mouseState->prevabs[axis] = mouseState->abs[axis];
}

/**
 * Handle pointer relative coordinates notification
 *
 * @param device the device that produced the event
 * @param pointerEvent the event produced
 */
static void lens_input_pointerEvents_handleRelMotion(LensInputDevice *device,
        struct input_event *pointerEvent) {
    LensInputMouseState *mouseState = device->state;
    int axis = pointerEvent->code;
    mouseState->rel[axis] += pointerEvent->value;
    switch (axis) {
        case REL_X:
            newMousePosX = mousePosX + pointerEvent->value;
            if (newMousePosX > screenWidth) {
                newMousePosX = screenWidth;
            } else if (newMousePosX < 0) {
                newMousePosX = 0;
            }
            break;
        case REL_Y:
            newMousePosY = mousePosY + pointerEvent->value;
            if (newMousePosY > screenHeight) {
                newMousePosY = screenHeight;
            } else if (newMousePosY < 0) {
                newMousePosY = 0;
            }
            break;
    }
    GLASS_LOG_FINER("Pointer relative axis 0x%02x is now %i, pointer at %i,%i",
                    axis, mouseState->rel[axis], newMousePosX, newMousePosY);
}




static void lens_input_pointerEvents_handleKeyEvent(LensInputDevice *device,
        struct input_event *pointerEvent) {

    jboolean isPressed = (pointerEvent->value == 1) ? JNI_TRUE : JNI_FALSE;

    if (device->isTouch) {
        //tap event
        jint eventType = (isPressed) ? com_sun_glass_events_TouchEvent_TOUCH_PRESSED
                         : com_sun_glass_events_TouchEvent_TOUCH_RELEASED;

        GLASS_LOG_FINE("Notify touch event on screen id %i - tap %s fx event code %i at %i,%i",
                       TOUCH_SCREEN_ID,
                       (isPressed) ? "pressed" : "released",
                       eventType,
                       mousePosX, mousePosY);

        lens_wm_notifyTouchEvent(gJNIEnv, eventType,
                                 TOUCH_SCREEN_ID, mousePosX, mousePosY);
    }

    int button = lens_input_convertButtonToFXButtonCode(pointerEvent->code);

    GLASS_LOG_FINE("Notify button event %i %s at %i,%i",
                   button,
                   isPressed ? "pressed" : "released",
                   mousePosX, mousePosY);
    lens_wm_notifyButtonEvent(gJNIEnv, isPressed,
                              button,
                              mousePosX, mousePosY);
}




/**
 * Handle pointer sync notification. The event is complete we
 * can now notify upper layers for pointer event
 *
 * @param device the device that produced the event
 * @param pointerEvent the event produced
 */
static void lens_input_pointerEvents_handleSync(LensInputDevice *device) {
    int i;
    LensInputMouseState *mouseState = device->state;
    int keyEventIndex = -1;
    jboolean reportMove = JNI_FALSE;

    //Pass on the events of this sync
    for (i = 0; i < mouseState->pendingInputEventCount; i++) {
        struct input_event *pointerEvent = &mouseState->pendingInputEvents[i];

        switch (pointerEvent->type) {
            case EV_KEY:
                keyEventIndex = i;
                break;
            case EV_REL:
                lens_input_pointerEvents_handleRelMotion(device, pointerEvent);
                reportMove = JNI_TRUE;
                break;
            case EV_ABS:
                lens_input_pointerEvents_handleAbsMotion(device, pointerEvent);
                reportMove = JNI_TRUE;
                break;
            default:
                // The queue should not hold other event
                assert(0);
        }
    }


    //Update new current position
    mousePosX = newMousePosX;
    mousePosY = newMousePosY;

    if (keyEventIndex >= 0) {
        //Press or release event
        if (mouseState->pendingInputEvents[keyEventIndex].value == 1) {
            // press
            jboolean sendEvent = JNI_TRUE;
            if (device->isTouch) {
                if (mouseState->touchState == TOUCH_DEFAULT) {
                    //normal first stage
                    mouseState->touchState = TOUCH_TAPPING;
                    mouseState->pressedX = mousePosX;
                    mouseState->pressedY = mousePosY;

                } else if (mouseState->touchState == TOUCH_RELEASING) {
                    //cancels the release
                    lens_input_eventLoop_triggerTimeout(device, 0, NULL);
                    mouseState->touchState = TOUCH_TAPPING;
                    sendEvent = JNI_FALSE;
                }
            }

            if (sendEvent) {
                lens_input_pointerEvents_handleKeyEvent(device,
                                                        &mouseState->pendingInputEvents[keyEventIndex]);
            }

        } else {
            //release
            if (!device->isTouch ||
                    (device->isTouch && mouseState->touchState == TOUCH_DRAGGING)) {
                mouseState->touchState = TOUCH_DEFAULT;
                lens_input_pointerEvents_handleKeyEvent(device,
                                                        &mouseState->pendingInputEvents[keyEventIndex]);
            }

            if (device->isTouch && mouseState->touchState == TOUCH_TAPPING) {
                mouseState->touchState = TOUCH_RELEASING;
                mouseState->releaseEvent = mouseState->pendingInputEvents[keyEventIndex];
                lens_input_eventLoop_triggerTimeout(device, gReleasePendingTimeout, lens_input_pointerEvents_handleTimeout);
            }
        }
    }

    if (reportMove) {

        if (device->isTouch && mouseState->touchState == TOUCH_TAPPING) {
            int dX = mousePosX - mouseState->pressedX;
            int dY = mousePosY - mouseState->pressedY;
            if (dX * dX + dY * dY >= gTapRadius * gTapRadius) {
                mouseState->touchState = TOUCH_DRAGGING;
            }
        }

        if (!device->isTouch ||
                (device->isTouch && mouseState->touchState == TOUCH_DRAGGING)) {
            lens_wm_notifyMotionEvent(gJNIEnv, mousePosX, mousePosY, device->isTouch, 1);
        }
    }

    if (mouseState->rel[REL_WHEEL] != 0) {
        //report wheel
        lens_wm_notifyScrollEvent(gJNIEnv, mousePosX, mousePosY,
                                  mouseState->rel[REL_WHEEL]);
    }

    for (i = 0; i < REL_MAX + 1; i++) {
        mouseState->rel[i] = 0;
    }

    mouseState->pendingInputEventCount = 0;

}




static void lens_input_pointerEvents_handleTimeout(LensInputDevice *device) {
    LensInputMouseState *mouseState = device->state;

    if (mouseState->touchState == TOUCH_RELEASING) {
        // Make the release action.
        mouseState->touchState = TOUCH_DEFAULT;
        mousePosX = newMousePosX = mouseState->pressedX;
        mousePosY = newMousePosY = mouseState->pressedY;

        lens_input_pointerEvents_handleKeyEvent(device, &mouseState->releaseEvent);

    } else {
        GLASS_LOG_WARNING("Touch state %d != from TOUCH_RELEASING", mouseState->touchState);
    }
}


/**
 * enqueue tap and button events to be handled when sync
 * notification arrives
 *
 * @param mouseState holds the queue
 * @param event the event produced
 */
static void lens_input_pointerEvents_enqueuePendingEvent(LensInputMouseState *mouseState,
        struct input_event *event) {
    //create queue if required
    if (mouseState->pendingInputEventCapacity == 0) {
        mouseState->pendingInputEvents = calloc(1, sizeof(struct input_event));
        if (mouseState->pendingInputEvents == NULL) {
            GLASS_LOG_SEVERE("Out of memory: skipping an input event");
            return;
        }
        GLASS_LOG_FINE("Allocated pendingInputEvents %p",
                       mouseState->pendingInputEvents);
        mouseState->pendingInputEventCapacity = 1;
    } else if (mouseState->pendingInputEventCount == mouseState->pendingInputEventCapacity) {
        //resize queue if full, by factor of 2
        struct input_event *newArray = calloc(
                                           mouseState->pendingInputEventCapacity * 2,
                                           sizeof(struct input_event));
        GLASS_LOG_FINE("Reallocated pendingInputEvents %p", newArray);
        if (newArray == NULL) {
            GLASS_LOG_SEVERE("Out of memory: skipping an input event");
            return;
        }
        memcpy(newArray, mouseState->pendingInputEvents,
               sizeof(struct input_event) * mouseState->pendingInputEventCapacity);
        GLASS_LOG_FINE("free(%p) (old pendingInputEvents)",
                       mouseState->pendingInputEvents);
        free(mouseState->pendingInputEvents);
        mouseState->pendingInputEvents = newArray;
        mouseState->pendingInputEventCapacity *= 2;
    }
    mouseState->pendingInputEvents[mouseState->pendingInputEventCount++] = *event;
}

///// mouse and touch events handling - END



//// Keyboard events handling
/**
 * Handle and notify for keyboard events
 *
 * @param device the device that produced the event
 * @param event the event produced
 */
static void lens_input_keyEvents_handleEvent(LensInputDevice *device,
                                             struct input_event *event) {

    int jfxKeyCode;
    int eventType;
    NativeWindow window;
    struct input_event keyEvent = *event;
    jboolean isRepeatEvent = JNI_FALSE;


    if (keyEvent.type == EV_KEY) {

        window = glass_window_getFocusedWindow();

        if (window == NULL) {
            GLASS_LOG_FINE("Skipping event, no focused window");
            return;
        }

        GLASS_LOG_FINE("Keyboard raw type=0x%02x code=%d value=%d\n",
                       keyEvent.type, keyEvent.code, keyEvent.value);



        //determine events
        if (keyEvent.value == LENSFB_KEY_PRESSED) {
            eventType = com_sun_glass_events_KeyEvent_PRESS;
        } else if (keyEvent.value == LENSFB_KEY_RELEASED) {
            eventType = com_sun_glass_events_KeyEvent_RELEASE;
        } else if (keyEvent.value == LENSFB_KEY_REPEAT) {
            eventType = com_sun_glass_events_KeyEvent_PRESS;
            isRepeatEvent = JNI_TRUE;
        } else {
            GLASS_LOG_FINE("Skipping event, unsupported event[%d]", keyEvent.value);
            return;
        }

        jfxKeyCode = glass_inputEvents_getJavaKeycodeFromPlatformKeyCode(keyEvent.code);

        GLASS_LOG_FINEST("Notifying key event on windows %d[%p] - "
                         "event type %d, key code %d, is repeat?%s",
                         window->id, window, eventType, jfxKeyCode,
                         (isRepeatEvent ? "yes" : "no"));
        glass_application_notifyKeyEvent(gJNIEnv, window, eventType, jfxKeyCode, isRepeatEvent);


    } else {
        GLASS_LOG_FINEST("Event type[%i] is not a key event, skipping ",
                         keyEvent.type);
    }



}
//// Keyboard events handling - END


//// udev monitor
/**
 * Start the hot plug monitoring and notifications using udev
 *
 * @param env
 *
 * @return jboolean JNI_TRUE on success
 */
static jboolean lens_input_udevMonitorStart(JNIEnv *env) {
    struct udev *udev;
    udev = udev_new();


    if (!udev) {
        GLASS_LOG_SEVERE("failed to create udev");
        return JNI_FALSE;
    }
    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
    if (!udev_monitor) {
        GLASS_LOG_SEVERE("failed to create udev_monitor\n");
        udev_unref(udev);
        return JNI_FALSE;
    }

    //listen to device changes on /dev/input
    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input", NULL);

    if (udev_monitor_enable_receiving(udev_monitor)) {
        GLASS_LOG_SEVERE("failed to bind the udev monitor");
        udev_unref(udev);
        udev_monitor_unref(udev_monitor);
        return JNI_FALSE;
    }

    glass_application_request_native_event_loop(env, lens_input_udevMonitorLoop , NULL);

    return JNI_TRUE;
}

/**
 * Polling loop for udev notifications
 *
 * @param env
 * @param handle not used
 */
void lens_input_udevMonitorLoop(JNIEnv *env, void *handle) {

    int monitorFD = udev_monitor_get_fd(udev_monitor);
    struct udev *udev = udev_monitor_get_udev(udev_monitor);
    fd_set readFdSet;

    if (monitorFD == -1) {
        udev_monitor_unref(udev_monitor);
        udev_unref(udev);
        GLASS_LOG_SEVERE("Error in udev_monitor_get_fd(), hot plug disabled");
        return;
    }

    GLASS_LOG_FINE("Starting hot plug thread monitoring on fd[%i]\n", monitorFD);

    FD_ZERO(&readFdSet);
    while (1) {
        FD_SET(monitorFD, &readFdSet);

        select(monitorFD + 1, &readFdSet, NULL, NULL, NULL);

        //while handling udev monitor events, prevent input events
        //from been processed
        GLASS_LOG_FINER("Trying to capture lock before processing udev monitor events");
        pthread_mutex_lock(&devicesLock);
        GLASS_LOG_FINER("lock captured");

        if (FD_ISSET(monitorFD, &readFdSet)) {
            lens_input_udevMonitorHandleEvent(env);
        }

        //continue processing input events
        GLASS_LOG_FINER("Releasing lock");
        pthread_mutex_unlock(&devicesLock);
    }
}

/**
 * Handle add, update, and remove notifications from udev
 *
 */
static void lens_input_udevMonitorHandleEvent(JNIEnv *env) {

    struct udev_device *udev_device;
    const char *action;
    LensInputDevice *device = NULL;
    struct epoll_event epollEvent;

    udev_device = udev_monitor_receive_device(udev_monitor);
    if (!udev_device) {
        GLASS_LOG_WARNING("No device found");
        return;
    }

    action = udev_device_get_action(udev_device);

    GLASS_LOG_CONFIG("Got udev event - action = %s", action);
    if (action) {
        if (!strcmp(action, "add") || !strcmp(action, "change")) {

            lens_input_isUdevDeviceExists(udev_device, &device);
            //remove the device on change action
            if (!strcmp(action, "change") && device) {
                lens_input_deviceRemove(env, device);
                device = NULL;
            }

            if (!device) {
                //add the device
                device = lens_input_deviceAllocateAndInit(env, udev_device);
                if (device) {
                    lens_input_listAdd(device);
                    lens_input_epolladdDevice(device);
                    lens_input_printDevices();
                }
            }
        } else if (!strcmp(action, "remove")) {

            if (lens_input_isUdevDeviceExists(udev_device, &device)) {
                //Device was removed, so fd is closed and not valid.
                //mark it to avoid problem when releasing the device
                device->fd = -1;
                lens_input_deviceRemove(env, device);
                lens_input_printDevices();
            } else {
                GLASS_LOG_CONFIG("Device not in the list, skipping remove");
            }
        }
    } else {
        GLASS_LOG_CONFIG("Taking no action on udev event");
    }
    udev_device_unref(udev_device);
    GLASS_LOG_CONFIG("udev event action processing done");
}

//// memory management

/**
 * Add device to the attched devices list [lensInputDevicesList]
 *
 * @param device the device to add
 */
static void lens_input_listAdd(LensInputDevice *device) {

    if (device) {
        //add device to list
        if (!lensInputDevicesList_head) {
            lensInputDevicesList_head = device;
        }
        if (lensInputDevicesList_tail) {
            lensInputDevicesList_tail->nextDevice = device;
        }
        device->previousDevice = lensInputDevicesList_tail;
        lensInputDevicesList_tail = device;

        gNumOfAttachedDevices++;
    }

}


/**
 * Remove device from the attched devices list
 * [lensInputDevicesList]
 *
 * @param device the device to remove
 */
void lens_input_listRemove(LensInputDevice *device) {
    //detach from list
    if (device) {
        if (device->previousDevice) {
            device->previousDevice->nextDevice = device->nextDevice;
        } else {
            lensInputDevicesList_head = device->nextDevice;
        }

        if (device->nextDevice) {
            device->nextDevice->previousDevice = device->previousDevice;
        } else {
            lensInputDevicesList_tail = device->previousDevice;
        }
        gNumOfAttachedDevices--;
    }
}

/**
 * Safe release for LensInputDeviceInfo data structure
 *
 * @param device the device that own the info data
 */
static void lens_input_deviceInfoRelease(LensInputDevice *device) {
    GLASS_LOG_FINE("Release device %p (%s): %s", device,
                   (device->info ? device->info->devNode : NULL),
                   (device->info ? device->info->name : NULL));
    if (device && device->info) {
        if (device->info->devNode)   {
            free(device->info->devNode);
        }
        if (device->info->sysPath)   {
            free(device->info->sysPath);
        }
        if (device->info->name)      {
            free(device->info->name);
        }
        if (device->info->productString) {
            free(device->info->productString);
        }
        GLASS_LOG_FINE("free(%p) (device info)", device->info);
        free(device->info);
        device->info = NULL;
    }
}

/**
 * Safe release for LensMouseState data structure
 *
 * @param device the device that own the mouse state data
 */
void lens_input_mouseStateFree(LensInputDevice *device) {
    LensInputMouseState *mouseState = device->state;

    if (mouseState) {
        if (mouseState->pendingInputEvents) {
            GLASS_LOG_FINE("free(%p) (pendingInputEvents)",
                           mouseState->pendingInputEvents);
            free(mouseState->pendingInputEvents);
            mouseState->pendingInputEvents = NULL;
            mouseState->pendingInputEventCapacity = 0;
            mouseState->pendingInputEventCount = 0;
        }
        GLASS_LOG_FINE("free(%p) (device pointer state)", mouseState);
        free(mouseState);
    }

    device->state = NULL;

}

/**
 * Safe release of all LensInputDevice internal resources and
 * its pointer
 *
 * @param device the device to release
 */
static void lens_input_deviceRelease(JNIEnv *env, LensInputDevice *device) {
    if (device) {

        if (device->fd != -1) {
            lens_input_deviceGrab(device, 0);
            GLASS_LOG_FINER("close(%i)", device->fd);
            close(device->fd);
            device->fd = -1;
        }
        lens_input_deviceNotify(env, device, JNI_FALSE);

        GLASS_LOG_FINER("Freeing mouseState");
        lens_input_mouseStateFree(device);

        GLASS_LOG_FINER("Freeing deviceInfo");
        lens_input_deviceInfoRelease(device);

        GLASS_LOG_FINE("free(%p) (device)", device);
        free(device);
    }
}



//// utilities
/**
 * Parse the PRODUCT string from udev entry and convert into unsigned int
 *
 * @param udev_device [IN] the device to parse
 * @param vendorId [OUT] usb vendor id number
 * @param productId [OUT] usb product id number
 *
 * @return LensResult LES_OK on success
 */
static LensResult lens_input_udevParseProductID(struct udev_device *udev_device,
                                                unsigned int *vendorId,
                                                unsigned int *productId) {

    struct udev_device *parent = udev_device_get_parent(udev_device);

    LensResult result = LENS_FAILED;

    if (parent) {
        const char *product = udev_device_get_property_value(parent, "PRODUCT");
        int matchedStrings;
        unsigned int _productId, _vendorId;

        if (product) {
            //first try to parse as hex
            matchedStrings = sscanf(product, "%*x/%4x/%4x/%*x", &_vendorId, &_productId);

            if (matchedStrings == 2) {
                *vendorId = _vendorId;
                *productId = _productId;
                result =  LENS_OK;
            } else {
                GLASS_LOG_FINE("Failed to parse PRODUCT [%s]", product);
                *vendorId = 0;
                *productId = 0;
            }
        }
    }
    return result;
}

/**
 * Remove LensInputDevice from the list of deevices and free its
 * resources
 *
 * @param device the device to remove
 */
void lens_input_deviceRemove(JNIEnv *env, LensInputDevice *device) {

    if (device->isEnabled) {
        // On some platforms device will receive EPOLLHUP when disconnected.
        // When it happens the epoll handling will consider this as an error
        // unregister the device, and mark it as disabled.
        //if we are here that didn't happen, so we need to unregister the device
        GLASS_LOG_FINE("Unregistering device from epoll");
        lens_input_epollRemoveDevice(device);
    }

    GLASS_LOG_FINE("Removing device from device list\n");
    lens_input_listRemove(device);
    GLASS_LOG_FINE("Releasing device resources");
    lens_input_deviceRelease(env, device);
}

/**
 * Check if udev_device exists in the attached devices list, and
 * optionally return a reference for that device
 *
 * @param udev_device [IN] the device to check
 * @param device [OUT] reference to existing device, optional
 *
 * @return jboolean JNI_TRUE if device exists
 */
static jboolean lens_input_isUdevDeviceExists(struct udev_device *udev_device,
                                              LensInputDevice **device) {

    LensInputDevice *_device = lensInputDevicesList_head;
    const char *devNode = udev_device_get_devnode(udev_device);


    unsigned int vendorId, productId;

    if (!_device) {
        GLASS_LOG_FINER("Device doesn't exist - Device list empty\n");
    } else {
        if (lens_input_udevParseProductID(udev_device, &vendorId, &productId) == LENS_OK) {
            while (_device) {
                GLASS_LOG_FINER("Comparing udev[%s, %x, %x] with device[%s, %x, %x]",
                                devNode, vendorId, productId,
                                _device->info->devNode,
                                _device->info->vendorId,
                                _device->info->productId);
                if ((_device->info->vendorId  == vendorId &&
                        _device->info->productId == productId) ||
                        (devNode && !(strcmp(_device->info->devNode, devNode)))) {
                    GLASS_LOG_FINER("Device found");
                    if (device) {
                        GLASS_LOG_FINER("referencing device");
                        *device = _device;
                    }
                    return JNI_TRUE;
                }

                _device = _device->nextDevice;
            }
        }
    }
    //no device found....
    if (device) {
        *device = NULL;
    }
    GLASS_LOG_FINER("Device not found");
    return JNI_FALSE;
}

/**
 * Check if device is still on the lensInputDevicesList
 *
 * @param device the device to search
 *
 * @return jboolean JNI_TRUE if exsits
 */
static jboolean lens_input_isDeviceExists(LensInputDevice *device) {
    LensInputDevice *_device = lensInputDevicesList_head;

    while (_device) {
        if (_device == device) {
            GLASS_LOG_FINER("Device %p exists", device);
            return JNI_TRUE;
        }
        _device = _device->nextDevice;
    }

    GLASS_LOG_FINER("Device %p was not found", device);
    return JNI_FALSE;

}

////// Printing functions

/**
 * Print the devices that currently monitored
 */
static void lens_input_printDevices() {
    LensInputDevice *device = lensInputDevicesList_head;

    GLASS_IF_LOG_CONFIG {

        GLASS_LOG_CONFIG("Input devices list:");

        if (!device) {
            GLASS_LOG_CONFIG("Device count = 0");
            return;
        }
        while (device) {
            GLASS_LOG_CONFIG("=========================");
            GLASS_LOG_CONFIG("Name: %s", device->info->name);
            GLASS_LOG_CONFIG("Path: %s", device->info->devNode);
            GLASS_LOG_CONFIG("sysPath %s", device->info->sysPath);
            GLASS_LOG_CONFIG("fd: %i", device->fd);
            GLASS_LOG_CONFIG("Product: %s", device->info->productString);
            GLASS_LOG_CONFIG("VendorId: %x", device->info->vendorId);
            GLASS_LOG_CONFIG("ProductId: %x", device->info->productId);

            if (device->isKeyboard) {
                GLASS_LOG_CONFIG("device is keyboard\n");
            }
            if (device->isPointer) {
                GLASS_LOG_CONFIG("device is pointer\n");
            }
            if (device->isTouch) {
                GLASS_LOG_CONFIG("device is touch\n");
            }
            GLASS_LOG_CONFIG("=========================\n");

            device = device->nextDevice;
        }

        GLASS_LOG_CONFIG("Device count = %i", gNumOfAttachedDevices);
    }
}

/**
 * Print input_event parameters in human readable form
 *
 * @param event
 */
static void lens_input_printEvent(struct input_event event) {
    char *tmp;

    GLASS_IF_LOG_FINEST {

        switch (event.type) {
            case EV_KEY:
                if (event.code > BTN_MISC) {
                    GLASS_LOG_FINEST("Button %d %s",
                    event.code & 0xff,
                    event.value ? "press" : "release");
                } else {
                    GLASS_LOG_FINEST("Key %d (0x%x) %s",
                                     event.code & 0xff,
                                     event.code & 0xff,
                                     event.value ? "press" : "release");
                }
                break;
            case EV_REL:
                switch (event.code) {
                    case REL_X:
                        tmp = "X";
                        break;
                    case REL_Y:
                        tmp = "Y";
                        break;
                    case REL_HWHEEL:
                        tmp = "HWHEEL";
                        break;
                    case REL_DIAL:
                        tmp = "DIAL";
                        break;
                    case REL_WHEEL:
                        tmp = "WHEEL";
                        break;
                    case REL_MISC:
                        tmp = "MISC";
                        break;
                    default:
                        tmp = "UNKNOWN";
                        break;
                }
                GLASS_LOG_FINEST("Relative %s %d", tmp, event.value);
                break;
            case EV_ABS:
                switch (event.code) {
                    case ABS_X:
                        tmp = "X";
                        break;
                    case ABS_Y:
                        tmp = "Y";
                        break;
                    case ABS_Z:
                        tmp = "Z";
                        break;
                    case ABS_RX:
                        tmp = "RX";
                        break;
                    case ABS_RY:
                        tmp = "RY";
                        break;
                    case ABS_RZ:
                        tmp = "RZ";
                        break;
                    case ABS_THROTTLE:
                        tmp = "THROTTLE";
                        break;
                    case ABS_RUDDER:
                        tmp = "RUDDER";
                        break;
                    case ABS_WHEEL:
                        tmp = "WHEEL";
                        break;
                    case ABS_GAS:
                        tmp = "GAS";
                        break;
                    case ABS_BRAKE:
                        tmp = "BRAKE";
                        break;
                    case ABS_HAT0X:
                        tmp = "HAT0X";
                        break;
                    case ABS_HAT0Y:
                        tmp = "HAT0Y";
                        break;
                    case ABS_HAT1X:
                        tmp = "HAT1X";
                        break;
                    case ABS_HAT1Y:
                        tmp = "HAT1Y";
                        break;
                    case ABS_HAT2X:
                        tmp = "HAT2X";
                        break;
                    case ABS_HAT2Y:
                        tmp = "HAT2Y";
                        break;
                    case ABS_HAT3X:
                        tmp = "HAT3X";
                        break;
                    case ABS_HAT3Y:
                        tmp = "HAT3Y";
                        break;
                    case ABS_PRESSURE:
                        tmp = "PRESSURE";
                        break;
                    case ABS_DISTANCE:
                        tmp = "DISTANCE";
                        break;
                    case ABS_TILT_X:
                        tmp = "TILT_X";
                        break;
                    case ABS_TILT_Y:
                        tmp = "TILT_Y";
                        break;
                    case ABS_MISC:
                        tmp = "MISC";
                        break;
                    default:
                        tmp = "UNKNOWN";
                        break;
                }
                GLASS_LOG_FINEST("Absolute %s %d", tmp, event.value);
                break;
            case EV_MSC:
                GLASS_LOG_FINEST("Misc");
                break;
            case EV_LED:
                GLASS_LOG_FINEST("Led");
                break;
            case EV_SND:
                GLASS_LOG_FINEST("Snd");
                break;
            case EV_REP:
                GLASS_LOG_FINEST("Rep");
                break;
            case EV_FF:
                GLASS_LOG_FINEST("FF");
                break;
                break;
        }

    }
}

/**
 * Utility function that display on the console device
 * properties and supported capabilities
 *
 * @param evtype_b supported events bitmask
 * @param keytype_b the key type (left, right etc. and physical
 *                  properties as well)
 * @param proptype_b input device type (direct, with buttons,
 *                   etc)
 */
static void lens_input_printDeviceProperties(
    u_int8_t *evtype_b, u_int8_t *keytype_b, u_int8_t *proptype_b) {
    GLASS_LOG_CONFIG("Supported device types:");
    int id;

    GLASS_IF_LOG_CONFIG {
        for (id = 0; id < EV_CNT; id++) {
            if (TEST_BIT(id, evtype_b)) {
                /* the bit is set in the event types list */
                switch (id) {
                    case EV_SYN :
                        GLASS_LOG_CONFIG("EV_SYN (0x%02x, Synch Events)", id);
                        break;
                    case EV_KEY :
                        GLASS_LOG_CONFIG("EV_KEY (0x%02x, Keys or Buttons)", id);
                        break;
                    case EV_REL :
                        GLASS_LOG_CONFIG("EV_REL (0x%02x, Relative Axes)", id);
                        break;
                    case EV_ABS :
                        GLASS_LOG_CONFIG("EV_ABS (0x%02x, Absolute Axes)", id);
                        break;
                    case EV_MSC :
                        GLASS_LOG_CONFIG("EV_MSC (0x%02x, Miscellaneous)", id);
                        break;
                    case EV_SW :
                        GLASS_LOG_CONFIG("EV_SW (0x%02x, SW)", id);
                        break;
                    case EV_LED :
                        GLASS_LOG_CONFIG("EV_LED (0x%02x, LEDs)", id);
                        break;
                    case EV_SND :
                        GLASS_LOG_CONFIG("EV_SND (0x%02x, Sounds)", id);
                        break;
                    case EV_REP :
                        GLASS_LOG_CONFIG("EV_REP (0x%02x, Repeat)", id);
                        break;
                    case EV_FF :
                    case EV_FF_STATUS:
                        GLASS_LOG_CONFIG("EV_FF/EV_FF_STATUS (0x%02x, Force Feedback)",
                        id);
                        break;
                    case EV_PWR:
                        GLASS_LOG_CONFIG("EV_PWR (0x%02x, Power Management)", id);
                        break;
                    default:
                        GLASS_LOG_CONFIG("(Unknown event: 0x%04hx)", id);
                }
            }
        }
        for (id = 0; id < KEY_CNT; id++) {
            if (TEST_BIT(id, keytype_b)) {
                switch (id) {
                    case BTN_LEFT:
                        GLASS_LOG_CONFIG("BTN_LEFT");
                        break;
                    case BTN_RIGHT:
                        GLASS_LOG_CONFIG("BTN_RIGHT");
                        break;
                    case BTN_MIDDLE:
                        GLASS_LOG_CONFIG("BTN_MIDDLE");
                        break;
                    case BTN_TOOL_PEN:
                        GLASS_LOG_CONFIG("BTN_TOOL_PEN / BTN_DIGI");
                        break;
                    case BTN_TOOL_RUBBER:
                        GLASS_LOG_CONFIG("BTN_TOOL_RUBBER");
                        break;
                    case BTN_TOOL_BRUSH:
                        GLASS_LOG_CONFIG("BTN_TOOL_BRUSH");
                        break;
                    case BTN_TOOL_PENCIL:
                        GLASS_LOG_CONFIG("BTN_TOOL_PENCIL");
                        break;
                    case BTN_TOOL_AIRBRUSH:
                        GLASS_LOG_CONFIG("BTN_TOOL_AIRBRUSH");
                        break;
                    case BTN_TOOL_FINGER:
                        GLASS_LOG_CONFIG("BTN_TOOL_FINGER");
                        break;
                    case BTN_TOOL_MOUSE:
                        GLASS_LOG_CONFIG("BTN_TOOL_MOUSE");
                        break;
                    case BTN_TOOL_LENS:
                        GLASS_LOG_CONFIG("BTN_TOOL_LENS");
                        break;
#ifdef BTN_TOOL_QUINTTAP
                    case BTN_TOOL_QUINTTAP:
                        GLASS_LOG_CONFIG("BTN_TOOL_QUINTTAP");
                        break;
#endif
                    case BTN_TOUCH:
                        GLASS_LOG_CONFIG("BTN_TOUCH");
                        break;
                    case BTN_STYLUS:
                        GLASS_LOG_CONFIG("BTN_STYLUS");
                        break;
                    case BTN_STYLUS2:
                        GLASS_LOG_CONFIG("BTN_STYLUS2");
                        break;
                    case BTN_TOOL_DOUBLETAP:
                        GLASS_LOG_CONFIG("BTN_TOOL_DOUBLETAP");
                        break;
                    case BTN_TOOL_TRIPLETAP:
                        GLASS_LOG_CONFIG("BTN_TOOL_TRIPLETAP");
                        break;
#ifdef BTN_TOOL_QUADTAP
                    case BTN_TOOL_QUADTAP:
                        GLASS_LOG_CONFIG("BTN_TOOL_QUADTAP");
                        break;
#endif
                    case KEY_ZOOM:
                        GLASS_LOG_CONFIG("KEY_ZOOM");
                        break;
                    default:
                        if (id > 0x100) {
                            GLASS_LOG_CONFIG("(Unknown key: 0x%04hx)", id);
                        } else {
                            GLASS_LOG_FINE("(Unknown key: 0x%04hx)", id);
                        }
                        break;
                }
            }
        }
#ifdef EVIOCGPROP
        for (id = 0; id < INPUT_PROP_CNT; id++) {
            if (TEST_BIT(id, proptype_b)) {
                switch (id) {
                    case INPUT_PROP_POINTER:
                        GLASS_LOG_CONFIG("INPUT_PROP_POINTER");
                        break;
                    case INPUT_PROP_DIRECT:
                        GLASS_LOG_CONFIG("INPUT_PROP_DIRECT");
                        break;
                    case INPUT_PROP_BUTTONPAD:
                        GLASS_LOG_CONFIG("INPUT_PROP_BUTTONPAD");
                        break;
                    case INPUT_PROP_SEMI_MT:
                        GLASS_LOG_CONFIG("INPUT_PROP_SEMI_MT");
                        break;
                    default:
                        GLASS_LOG_CONFIG("(Uknown input property: 0x%04hx)", id);
                        break;
                }
            }
        }
#endif

    }
}

////// Test input device functions

/* The test input device feature reads input device configuration data from a
 * monitor device defined by LENS_TESTINPUT. This allows regression testing of
 * different input peripherals without requiring the actual peripheral hardware
 * to be present. A test suite that defines LENS_TESTINPUT should also create
 * the input monitor device (using mkfifo) before starting JavaFX.
 *
 * Test input data is read from the monitor device with the following format:
 * action: jint: 1 for add, 2 for remove
 * for add:
 *   id: struct input_id
 *   name: zero-terminated string
 *   devNode: zero-terminated string
 *   product: zero-terminated string
 *   events: a list of event types as jints, terminated by -1
 *   keys: a list of key codes as jints, terminated by -1
 *   relativeAxes: a list of axis codes as jints, terminated by -1
 *   absAxes: a list of absolute axis codes as
 *     { jint axis; struct input_absinfo info }
 *     terminated by a single jint of -1
 *   ( key: zero-terminated string
 *     value: zero-terminated string ) *
 *   0: byte
 *
 * for remove:
 *   devNode: zero-terminated string
 *
 * All jints are in host order.
 *
 */

/** Polling loop for test input notifications
 *
 * @param env
 * @param handle the path of the test input device
 */
void lens_input_testInputMonitorLoop(JNIEnv *env, void *handle) {

    fd_set readFdSet;
    const char *testInputPath = (const char *) handle;

    assert(testInputPath);
    testInputFD = open(testInputPath, O_RDONLY | O_SYNC);
    GLASS_LOG_FINE("open(%s) returned %i", testInputPath, testInputFD);
    if (testInputFD < 0) {
        GLASS_LOG_SEVERE("Cannot open test input device %s (Error %i: %s)",
                         testInputPath, errno, strerror(errno));
        return;
    }

    GLASS_LOG_FINE("Starting test input monitoring on fd[%i]\n", testInputFD);

    FD_ZERO(&readFdSet);
    while (1) {
        FD_SET(testInputFD, &readFdSet);
        select(testInputFD + 1, &readFdSet, NULL, NULL, NULL);

        if (FD_ISSET(testInputFD, &readFdSet)) {
            if (lens_input_testInputHandleEvent(env)) {
                GLASS_LOG_SEVERE("Error processing test input stream: disconnecting %s",
                                 testInputPath);
                GLASS_LOG_FINE("close(%i)", testInputFD);
                close(testInputFD);
                testInputFD = -1;
                return;
            }
        }
    }
}


/**
 * Handle add and remove notifications from test input
 */
static LensResult lens_input_testInputHandleEvent(JNIEnv *env) {
    jint action;
    if (lens_input_testInputReadInt(&action)) {
        return LENS_FAILED;
    }
    if (action == 1) {
        LensInputDevice *device;
        LensInputDeviceCapabilities *caps;
        struct input_id id;
        int rc = 0;

        GLASS_LOG_FINE("Adding test device");
        device = calloc(1, sizeof(LensInputDevice));
        GLASS_LOG_FINE("Allocated device %p", device);
        if (device == NULL) {
            GLASS_LOG_SEVERE("Unable to allocate device structure");
            return LENS_FAILED;
        }
        device->info = calloc(1, sizeof(LensInputDeviceInfo));
        if (device->info == NULL) {
            GLASS_LOG_SEVERE("Unable to allocate device info");
            lens_input_deviceRelease(env, device);
            return LENS_FAILED;
        }
        GLASS_LOG_FINE("Allocated device info %p", device->info);
        caps = &device->info->caps;
        device->isTestDevice = JNI_TRUE;
        device->timeout.tv_sec = 0;
        device->onTimeoutFunc = NULL;

        GLASS_LOG_FINE("Reading device ID");
        if (lens_input_testInputRead(&id, sizeof(id))) {
            lens_input_deviceRelease(env, device);
            return LENS_FAILED;
        }
        device->info->vendorId = (unsigned int) id.vendor;
        device->info->productId = (unsigned int) id.product;
        rc |= lens_input_testInputReadString(&device->info->name);
        rc |= lens_input_testInputReadString(&device->info->devNode);
        rc |= lens_input_testInputReadString(&device->info->productString);
        if (rc) {
            lens_input_deviceRelease(env, device);
            return LENS_FAILED;
        }
        GLASS_LOG_FINEST("Reading event mask");
        rc |= lens_input_testInputReadBitSet(&caps->eventMask[0], EV_MAX);
        GLASS_LOG_FINEST("Reading key bitset");
        rc |= lens_input_testInputReadBitSet(&caps->keybits[0], KEY_MAX);
        GLASS_LOG_FINEST("Reading relative axis bitset");
        rc |= lens_input_testInputReadBitSet(&caps->relbits[0], REL_MAX);
        if (rc) {
            lens_input_deviceRelease(env, device);
            return LENS_FAILED;
        }
        GLASS_LOG_FINEST("Reading absolute axis data");
        do {
            jint i;
            if (lens_input_testInputReadInt(&i)) {
                lens_input_deviceRelease(env, device);
                return LENS_FAILED;
            }
            if (i < 0) {
                break;
            }
            if (i > ABS_MAX) {
                GLASS_LOG_SEVERE("Absolute axis index %i out of range", i);
                lens_input_deviceRelease(env, device);
                return LENS_FAILED;
            }
            SET_BIT(caps->absbits, i);
            lens_input_testInputRead(&caps->absinfo[i], sizeof(struct input_absinfo));
            GLASS_LOG_FINEST("Range on axis %i is %i..%i", i,
                             (int) caps->absinfo[i].minimum,
                             (int) caps->absinfo[i].maximum);
        } while (1);
        jboolean isValidDevice = JNI_FALSE;
        do {
            char *key, *value;
            rc |= lens_input_testInputReadString(&key);
            if (strlen(key) == 0) {
                free(key);
                break;
            }
            rc |= lens_input_testInputReadString(&value);
            if (rc) {
                lens_input_deviceRelease(env, device);
                return LENS_FAILED;
            }
            isValidDevice |= lens_input_deviceCheckProperties(device, key, value);
            free(key);
            free(value);
        } while (1);
        if (isValidDevice) {
            if (lens_input_deviceOpen(env, device)) {
                lens_input_deviceRelease(env, device);
                /* The input device monitor stream is left in a consistent
                 * state, so we return LENS_OK even though there was a failure.
                 * A SEVERE error will be logged by lens_input_deviceOpen on
                 * the failure to open the device. */
                return LENS_OK;
            }
            pthread_mutex_lock(&devicesLock);
            lens_input_listAdd(device);
            lens_input_epolladdDevice(device);
            lens_input_printDevices();
            pthread_mutex_unlock(&devicesLock);
        } else {
            GLASS_LOG_CONFIG("Not a keyboard, mouse or touchscreen - skipping");
            lens_input_deviceRelease(env, device);
        }
    } else if (action == 2) {
        char *devNode;
        GLASS_LOG_FINE("Removing test device");
        if (lens_input_testInputReadString(&devNode)) {
            return LENS_FAILED;
        }
        pthread_mutex_lock(&devicesLock);
        LensInputDevice *device = NULL;
        LensInputDevice *deviceList = lensInputDevicesList_head;
        while (deviceList) {
            if (strcmp(deviceList->info->devNode, devNode) == 0) {
                device = deviceList;
                break;
            }
            deviceList = deviceList->nextDevice;
        }
        if (device) {
            GLASS_LOG_FINE("Removing device %s", devNode);
            lens_input_deviceRemove(env, device);
            lens_input_printDevices();
        } else {
            GLASS_LOG_CONFIG("Device %s not in the list, skipping remove", devNode);
        }
        pthread_mutex_unlock(&devicesLock);
    } else {
        GLASS_LOG_SEVERE("Unknown action %i in test input stream", action);
        return LENS_FAILED;
    }
    return LENS_OK;
}

/** Reads n bytes from the test input monitor device */
static LensResult lens_input_testInputRead(void *_p, size_t n) {
    char *p = (char *) _p;
    size_t bytesRead = 0;
    while (bytesRead < n) {
        int rc = read(testInputFD, p + bytesRead, n - bytesRead);
        if (rc < 0) {
            if (errno == EAGAIN) {
                usleep(1000);
            } else {
                return LENS_FAILED;
            }
        } else {
            bytesRead += rc;
        }
    }
    return LENS_OK;
}

/**
 * Reads a jint (in host order) from the test input monitor device
 */
static LensResult lens_input_testInputReadInt(jint *i) {
    return lens_input_testInputRead((char *) i, sizeof(jint));
}

/**
 * Reads a null-terminated string from the test input device
 */
static LensResult lens_input_testInputReadString(char **pS) {
    char buffer[1024];
    char *p = (char *) buffer;
    char c;
    do {
        if (lens_input_testInputRead(&c, 1)) {
            return LENS_FAILED;
        } else {
            *p++ = c;
        }
    } while (c);
    GLASS_LOG_FINEST("Read test input string '%s'", buffer);
    *pS = strdup(buffer);
    if (*pS == NULL) {
        return LENS_FAILED;
    } else {
        return LENS_OK;
    }
}

/**
 * Reads an unpacked bitset from the input device as a list of jints terminated
 * by -1
 */
static LensResult lens_input_testInputReadBitSet(unsigned long *bitset, int max) {
    jint i;
    do {
        if (lens_input_testInputReadInt(&i)) {
            return LENS_FAILED;
        }
        if (i > max) {
            GLASS_LOG_SEVERE("Bitset value %i out of range", i);
            return LENS_FAILED;
        }
        if (i >= 0) {
            SET_BIT(bitset, i);
        }
    } while (i >= 0);
    return LENS_OK;
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy