mirror of
https://github.com/glfw/glfw.git
synced 2025-10-04 05:36:35 +00:00
evdev added to poll /dev/input devices for keyboard and mouse events
This commit is contained in:
parent
757f3f863b
commit
c6b3ba4b9e
@ -62,10 +62,10 @@ elseif (_GLFW_OSMESA)
|
||||
null_joystick.c posix_time.c posix_thread.c osmesa_context.c)
|
||||
elseif (_GLFW_VIVANTE)
|
||||
set(glfw_HEADERS ${common_HEADERS} vivante_platform.h xkb_unicode.h linux_joystick.h
|
||||
posix_time.h posix_thread.h egl_context.h)
|
||||
posix_time.h posix_thread.h egl_context.h evdev.h)
|
||||
set(glfw_SOURCES ${common_SOURCES} vivante_init.c null_monitor.c vivante_window.c
|
||||
xkb_unicode.c linux_joystick.c posix_time.c posix_thread.c
|
||||
egl_context.c)
|
||||
egl_context.c evdev.c)
|
||||
endif()
|
||||
|
||||
if (_GLFW_X11 OR _GLFW_WAYLAND)
|
||||
|
643
src/evdev.c
Normal file
643
src/evdev.c
Normal file
@ -0,0 +1,643 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 Linux - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SYN_DROPPED // < v2.6.39 kernel headers
|
||||
// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32
|
||||
#define SYN_DROPPED 3
|
||||
#endif
|
||||
|
||||
#define BITS_PER_LONG (sizeof(long) * 8)
|
||||
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
|
||||
#define OFF(x) ((x)%BITS_PER_LONG)
|
||||
#define BIT(x) (1UL<<OFF(x))
|
||||
#define LONG(x) ((x)/BITS_PER_LONG)
|
||||
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
|
||||
#define set_bit(bit, array) (array[LONG(bit)] |= BIT(OFF(bit)))
|
||||
#define reset_bit(bit, array) (array[LONG(bit)] &= ~BIT(OFF(bit)))
|
||||
|
||||
// Translate a GLFW key code to a char.
|
||||
static unsigned int translateGlfwKeyCodeToChar( int code, GLFWbool upper )
|
||||
{
|
||||
switch( code )
|
||||
{
|
||||
case GLFW_KEY_SPACE: return ' ';
|
||||
case GLFW_KEY_APOSTROPHE: return upper ? '\'' : '\"';
|
||||
case GLFW_KEY_COMMA: return upper ? '<' : ',';
|
||||
case GLFW_KEY_MINUS: return upper ? '_' : '-';
|
||||
case GLFW_KEY_PERIOD: return upper ? '>' : '.';
|
||||
case GLFW_KEY_SLASH: return upper ? '?' : '/';
|
||||
case GLFW_KEY_0: return upper ? ')' : '0';
|
||||
case GLFW_KEY_1: return upper ? '!' : '1';
|
||||
case GLFW_KEY_2: return upper ? '@' : '2';
|
||||
case GLFW_KEY_3: return upper ? '#' : '3';
|
||||
case GLFW_KEY_4: return upper ? '$' : '4';
|
||||
case GLFW_KEY_5: return upper ? '%' : '5';
|
||||
case GLFW_KEY_6: return upper ? '^' : '6';
|
||||
case GLFW_KEY_7: return upper ? '&' : '7';
|
||||
case GLFW_KEY_8: return upper ? '*' : '8';
|
||||
case GLFW_KEY_9: return upper ? '(' : '9';
|
||||
case GLFW_KEY_SEMICOLON: return upper ? ':' : ';';
|
||||
case GLFW_KEY_EQUAL: return upper ? '+' : '=';
|
||||
case GLFW_KEY_A: return upper ? 'A' : 'a';
|
||||
case GLFW_KEY_B: return upper ? 'B' : 'b';
|
||||
case GLFW_KEY_C: return upper ? 'C' : 'c';
|
||||
case GLFW_KEY_D: return upper ? 'D' : 'd';
|
||||
case GLFW_KEY_E: return upper ? 'E' : 'e';
|
||||
case GLFW_KEY_F: return upper ? 'F' : 'f';
|
||||
case GLFW_KEY_G: return upper ? 'G' : 'g';
|
||||
case GLFW_KEY_H: return upper ? 'H' : 'h';
|
||||
case GLFW_KEY_I: return upper ? 'I' : 'i';
|
||||
case GLFW_KEY_J: return upper ? 'J' : 'j';
|
||||
case GLFW_KEY_K: return upper ? 'K' : 'k';
|
||||
case GLFW_KEY_L: return upper ? 'L' : 'l';
|
||||
case GLFW_KEY_M: return upper ? 'M' : 'm';
|
||||
case GLFW_KEY_N: return upper ? 'N' : 'n';
|
||||
case GLFW_KEY_O: return upper ? 'O' : 'o';
|
||||
case GLFW_KEY_P: return upper ? 'P' : 'p';
|
||||
case GLFW_KEY_Q: return upper ? 'Q' : 'q';
|
||||
case GLFW_KEY_R: return upper ? 'R' : 'r';
|
||||
case GLFW_KEY_S: return upper ? 'S' : 's';
|
||||
case GLFW_KEY_T: return upper ? 'T' : 't';
|
||||
case GLFW_KEY_U: return upper ? 'U' : 'u';
|
||||
case GLFW_KEY_V: return upper ? 'V' : 'v';
|
||||
case GLFW_KEY_W: return upper ? 'W' : 'w';
|
||||
case GLFW_KEY_X: return upper ? 'X' : 'x';
|
||||
case GLFW_KEY_Y: return upper ? 'Y' : 'y';
|
||||
case GLFW_KEY_Z: return upper ? 'Z' : 'z';
|
||||
case GLFW_KEY_LEFT_BRACKET: return upper ? '{' : '[';
|
||||
case GLFW_KEY_BACKSLASH: return upper ? '|' : '\\';
|
||||
case GLFW_KEY_RIGHT_BRACKET: return upper ? '}' : ']';
|
||||
case GLFW_KEY_GRAVE_ACCENT: return upper ? '~' : '`';
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// Translate an linux/input key code to a GLFW key code.
|
||||
static int translateKeyCodeToGlfw( int code )
|
||||
{
|
||||
switch( code )
|
||||
{
|
||||
/* Printable keys */
|
||||
case KEY_SPACE: return GLFW_KEY_SPACE;
|
||||
case KEY_APOSTROPHE: return GLFW_KEY_APOSTROPHE;
|
||||
case KEY_COMMA: return GLFW_KEY_COMMA;
|
||||
case KEY_MINUS: return GLFW_KEY_MINUS;
|
||||
case KEY_DOT: return GLFW_KEY_PERIOD;
|
||||
case KEY_SLASH: return GLFW_KEY_SLASH;
|
||||
case KEY_0: return GLFW_KEY_0;
|
||||
case KEY_1: return GLFW_KEY_1;
|
||||
case KEY_2: return GLFW_KEY_2;
|
||||
case KEY_3: return GLFW_KEY_3;
|
||||
case KEY_4: return GLFW_KEY_4;
|
||||
case KEY_5: return GLFW_KEY_5;
|
||||
case KEY_6: return GLFW_KEY_6;
|
||||
case KEY_7: return GLFW_KEY_7;
|
||||
case KEY_8: return GLFW_KEY_8;
|
||||
case KEY_9: return GLFW_KEY_9;
|
||||
case KEY_SEMICOLON: return GLFW_KEY_SEMICOLON;
|
||||
case KEY_EQUAL: return GLFW_KEY_EQUAL;
|
||||
case KEY_A: return GLFW_KEY_A;
|
||||
case KEY_B: return GLFW_KEY_B;
|
||||
case KEY_C: return GLFW_KEY_C;
|
||||
case KEY_D: return GLFW_KEY_D;
|
||||
case KEY_E: return GLFW_KEY_E;
|
||||
case KEY_F: return GLFW_KEY_F;
|
||||
case KEY_G: return GLFW_KEY_G;
|
||||
case KEY_H: return GLFW_KEY_H;
|
||||
case KEY_I: return GLFW_KEY_I;
|
||||
case KEY_J: return GLFW_KEY_J;
|
||||
case KEY_K: return GLFW_KEY_K;
|
||||
case KEY_L: return GLFW_KEY_L;
|
||||
case KEY_M: return GLFW_KEY_M;
|
||||
case KEY_N: return GLFW_KEY_N;
|
||||
case KEY_O: return GLFW_KEY_O;
|
||||
case KEY_P: return GLFW_KEY_P;
|
||||
case KEY_Q: return GLFW_KEY_Q;
|
||||
case KEY_R: return GLFW_KEY_R;
|
||||
case KEY_S: return GLFW_KEY_S;
|
||||
case KEY_T: return GLFW_KEY_T;
|
||||
case KEY_U: return GLFW_KEY_U;
|
||||
case KEY_V: return GLFW_KEY_V;
|
||||
case KEY_W: return GLFW_KEY_W;
|
||||
case KEY_X: return GLFW_KEY_X;
|
||||
case KEY_Y: return GLFW_KEY_Y;
|
||||
case KEY_Z: return GLFW_KEY_Z;
|
||||
case KEY_LEFTBRACE: return GLFW_KEY_LEFT_BRACKET;
|
||||
case KEY_BACKSLASH: return GLFW_KEY_BACKSLASH;
|
||||
case KEY_RIGHTBRACE: return GLFW_KEY_RIGHT_BRACKET;
|
||||
case KEY_GRAVE: return GLFW_KEY_GRAVE_ACCENT;
|
||||
// GLFW_KEY_WORLD_1 not found
|
||||
// GLFW_KEY_WORLD_2 not found
|
||||
|
||||
/* Function keys */
|
||||
case KEY_ESC: return GLFW_KEY_ESCAPE;
|
||||
case KEY_ENTER: return GLFW_KEY_ENTER;
|
||||
case KEY_TAB: return GLFW_KEY_TAB;
|
||||
case KEY_BACKSPACE: return GLFW_KEY_BACKSPACE;
|
||||
case KEY_INSERT: return GLFW_KEY_INSERT;
|
||||
case KEY_DELETE: return GLFW_KEY_DELETE;
|
||||
case KEY_RIGHT: return GLFW_KEY_RIGHT;
|
||||
case KEY_LEFT: return GLFW_KEY_LEFT;
|
||||
case KEY_DOWN: return GLFW_KEY_DOWN;
|
||||
case KEY_UP: return GLFW_KEY_UP;
|
||||
case KEY_PAGEUP: return GLFW_KEY_PAGE_UP;
|
||||
case KEY_PAGEDOWN: return GLFW_KEY_PAGE_DOWN;
|
||||
case KEY_HOME: return GLFW_KEY_HOME;
|
||||
case KEY_END: return GLFW_KEY_END;
|
||||
case KEY_CAPSLOCK: return GLFW_KEY_CAPS_LOCK;
|
||||
case KEY_SCROLLLOCK: return GLFW_KEY_SCROLL_LOCK;
|
||||
case KEY_NUMLOCK: return GLFW_KEY_NUM_LOCK;
|
||||
case KEY_SYSRQ: return GLFW_KEY_PRINT_SCREEN;
|
||||
case KEY_PAUSE: return GLFW_KEY_PAUSE;
|
||||
case KEY_F1: return GLFW_KEY_F1;
|
||||
case KEY_F2: return GLFW_KEY_F2;
|
||||
case KEY_F3: return GLFW_KEY_F3;
|
||||
case KEY_F4: return GLFW_KEY_F4;
|
||||
case KEY_F5: return GLFW_KEY_F5;
|
||||
case KEY_F6: return GLFW_KEY_F6;
|
||||
case KEY_F7: return GLFW_KEY_F7;
|
||||
case KEY_F8: return GLFW_KEY_F8;
|
||||
case KEY_F9: return GLFW_KEY_F9;
|
||||
case KEY_F10: return GLFW_KEY_F10;
|
||||
case KEY_F11: return GLFW_KEY_F11;
|
||||
case KEY_F12: return GLFW_KEY_F12;
|
||||
case KEY_F13: return GLFW_KEY_F13;
|
||||
case KEY_F14: return GLFW_KEY_F14;
|
||||
case KEY_F15: return GLFW_KEY_F15;
|
||||
case KEY_F16: return GLFW_KEY_F16;
|
||||
case KEY_F17: return GLFW_KEY_F17;
|
||||
case KEY_F18: return GLFW_KEY_F18;
|
||||
case KEY_F19: return GLFW_KEY_F19;
|
||||
case KEY_F20: return GLFW_KEY_F20;
|
||||
case KEY_F21: return GLFW_KEY_F21;
|
||||
case KEY_F22: return GLFW_KEY_F22;
|
||||
case KEY_F23: return GLFW_KEY_F23;
|
||||
case KEY_F24: return GLFW_KEY_F24;
|
||||
// GLFW_KEY_F25 not found
|
||||
case KEY_KP0: return GLFW_KEY_KP_0;
|
||||
case KEY_KP1: return GLFW_KEY_KP_1;
|
||||
case KEY_KP2: return GLFW_KEY_KP_2;
|
||||
case KEY_KP3: return GLFW_KEY_KP_3;
|
||||
case KEY_KP4: return GLFW_KEY_KP_4;
|
||||
case KEY_KP5: return GLFW_KEY_KP_5;
|
||||
case KEY_KP6: return GLFW_KEY_KP_6;
|
||||
case KEY_KP7: return GLFW_KEY_KP_7;
|
||||
case KEY_KP8: return GLFW_KEY_KP_8;
|
||||
case KEY_KP9: return GLFW_KEY_KP_9;
|
||||
case KEY_KPDOT: return GLFW_KEY_KP_DECIMAL;
|
||||
case KEY_KPSLASH: return GLFW_KEY_KP_DIVIDE;
|
||||
case KEY_KPASTERISK: return GLFW_KEY_KP_MULTIPLY;
|
||||
case KEY_KPMINUS: return GLFW_KEY_KP_SUBTRACT;
|
||||
case KEY_KPPLUS: return GLFW_KEY_KP_ADD;
|
||||
case KEY_KPENTER: return GLFW_KEY_KP_ENTER;
|
||||
case KEY_KPEQUAL: return GLFW_KEY_KP_EQUAL;
|
||||
case KEY_LEFTSHIFT: return GLFW_KEY_LEFT_SHIFT;
|
||||
case KEY_LEFTCTRL: return GLFW_KEY_LEFT_CONTROL;
|
||||
case KEY_LEFTALT: return GLFW_KEY_LEFT_ALT;
|
||||
case KEY_LEFTMETA: return GLFW_KEY_LEFT_SUPER;
|
||||
case KEY_RIGHTSHIFT: return GLFW_KEY_RIGHT_SHIFT;
|
||||
case KEY_RIGHTCTRL: return GLFW_KEY_RIGHT_CONTROL;
|
||||
case KEY_RIGHTALT: return GLFW_KEY_RIGHT_ALT;
|
||||
case KEY_RIGHTMETA: return GLFW_KEY_RIGHT_SUPER;
|
||||
case KEY_COMPOSE: return GLFW_KEY_MENU;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
// Translate an linux/input key code to a GLFW mouse button code.
|
||||
static int translateMouseButtonCodeToGlfw( int code )
|
||||
{
|
||||
switch( code )
|
||||
{
|
||||
case BTN_LEFT: return GLFW_MOUSE_BUTTON_1;
|
||||
case BTN_RIGHT: return GLFW_MOUSE_BUTTON_2;
|
||||
case BTN_MIDDLE: return GLFW_MOUSE_BUTTON_3;
|
||||
case BTN_SIDE: return GLFW_MOUSE_BUTTON_4;
|
||||
case BTN_EXTRA: return GLFW_MOUSE_BUTTON_5;
|
||||
case BTN_FORWARD: return GLFW_MOUSE_BUTTON_6;
|
||||
case BTN_BACK: return GLFW_MOUSE_BUTTON_7;
|
||||
case BTN_TASK: return GLFW_MOUSE_BUTTON_8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
// Translate an linux/input key action code to a GLFW action code.
|
||||
static int translateActionCodeToGlfw( int code )
|
||||
{
|
||||
switch( code )
|
||||
{
|
||||
case 0: return GLFW_RELEASE;
|
||||
case 1: return GLFW_PRESS;
|
||||
case 2: return GLFW_REPEAT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
static void updateMods()
|
||||
{
|
||||
int edid;
|
||||
int mods = 0;
|
||||
|
||||
_GLFWeventDevice* ed = NULL;
|
||||
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
ed = _glfw.evdev.devices + edid;
|
||||
if (ed->present){
|
||||
mods |= ed->mods;
|
||||
}
|
||||
}
|
||||
|
||||
_glfw.evdev.mods = mods;
|
||||
}
|
||||
|
||||
static void updateEventDeviceMods(_GLFWeventDevice* ed)
|
||||
{
|
||||
int mods = 0;
|
||||
|
||||
if(test_bit(KEY_LEFTSHIFT, ed->keyState))
|
||||
mods |= GLFW_MOD_SHIFT;
|
||||
if(test_bit(KEY_RIGHTSHIFT, ed->keyState))
|
||||
mods |= GLFW_MOD_SHIFT;
|
||||
|
||||
if(test_bit(KEY_LEFTCTRL, ed->keyState))
|
||||
mods |= GLFW_MOD_CONTROL;
|
||||
if(test_bit(KEY_RIGHTCTRL, ed->keyState))
|
||||
mods |= GLFW_MOD_CONTROL;
|
||||
|
||||
if(test_bit(KEY_LEFTALT, ed->keyState))
|
||||
mods |= GLFW_MOD_ALT;
|
||||
if(test_bit(KEY_RIGHTALT, ed->keyState))
|
||||
mods |= GLFW_MOD_ALT;
|
||||
|
||||
if(test_bit(KEY_LEFTMETA, ed->keyState))
|
||||
mods |= GLFW_MOD_SUPER;
|
||||
if(test_bit(KEY_RIGHTMETA, ed->keyState))
|
||||
mods |= GLFW_MOD_SUPER;
|
||||
|
||||
if(test_bit(LED_CAPSL, ed->ledState))
|
||||
mods |= GLFW_MOD_CAPS_LOCK;
|
||||
|
||||
if(test_bit(LED_NUML, ed->ledState))
|
||||
mods |= GLFW_MOD_NUM_LOCK;
|
||||
|
||||
ed->mods = mods;
|
||||
updateMods();
|
||||
}
|
||||
|
||||
// Apply an EV_KEY event to the specified event device
|
||||
//
|
||||
static void handleKeyEvent(_GLFWeventDevice* ed, int code, int value)
|
||||
{
|
||||
int button, key, action, mods;
|
||||
GLFWbool upper;
|
||||
unsigned int codepoint;
|
||||
if( value )
|
||||
set_bit(code, ed->keyState);
|
||||
else
|
||||
reset_bit(code, ed->keyState);
|
||||
updateEventDeviceMods(ed);
|
||||
mods = _glfw.evdev.mods;
|
||||
button = translateMouseButtonCodeToGlfw(code);
|
||||
action = translateActionCodeToGlfw(value);
|
||||
|
||||
if (button!=GLFW_KEY_UNKNOWN){
|
||||
_glfwEvdevInputMouseClick(button, action, mods);
|
||||
}else{
|
||||
key = translateKeyCodeToGlfw(code);
|
||||
_glfwEvdevInputKey(key, ed->scancode, action, mods);
|
||||
|
||||
if (action != GLFW_RELEASE){
|
||||
upper = ((mods & GLFW_MOD_SHIFT) != 0) != ((mods & GLFW_MOD_CAPS_LOCK) != 0); // shift xor capsl
|
||||
if ((codepoint = translateGlfwKeyCodeToChar(key, upper)))
|
||||
_glfwEvdevInputChar(codepoint, mods, GLFW_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply an EV_REL event to the specified event device
|
||||
//
|
||||
static void handleRelEvent(_GLFWeventDevice* ed, int code, int value)
|
||||
{
|
||||
if (code == REL_X){
|
||||
_glfwEvdevInputCursorPos(value, .0);
|
||||
}else if (code == REL_Y)
|
||||
_glfwEvdevInputCursorPos(.0, value);
|
||||
else if (code == REL_HWHEEL)
|
||||
_glfwEvdevInputScroll(value, .0);
|
||||
else if (code == REL_WHEEL)
|
||||
_glfwEvdevInputScroll(.0, value);
|
||||
}
|
||||
|
||||
// Apply an EV_LED event to the specified event device
|
||||
//
|
||||
static void handleLedEvent(_GLFWeventDevice* ed, int code, int value)
|
||||
{
|
||||
if( value )
|
||||
set_bit(code, ed->ledState);
|
||||
else
|
||||
reset_bit(code, ed->ledState);
|
||||
updateEventDeviceMods(ed);
|
||||
}
|
||||
|
||||
// Poll keys and leds state of the specified event device
|
||||
//
|
||||
static void pollEventDeviceState(_GLFWeventDevice* ed)
|
||||
{
|
||||
int r;
|
||||
|
||||
memset(ed->keyState, 0, sizeof(ed->keyState));
|
||||
memset(ed->ledState, 0, sizeof(ed->ledState));
|
||||
|
||||
r = ioctl(ed->fd, EVIOCGKEY( KEY_MAX ), ed->keyState);
|
||||
if (r == -1){
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Evdev: Failed to request keys state");
|
||||
}
|
||||
|
||||
r = ioctl(ed->fd, EVIOCGLED( LED_MAX ), ed->ledState);
|
||||
if (r == -1){
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Evdev: Failed to request leds state");
|
||||
}
|
||||
|
||||
updateEventDeviceMods(ed);
|
||||
}
|
||||
|
||||
// Attempt to open the specified event device
|
||||
//
|
||||
static GLFWbool openEventDevice(const char* path)
|
||||
{
|
||||
int fd;
|
||||
int edid;
|
||||
char name[256] = "";
|
||||
_GLFWeventDevice* ed = NULL;
|
||||
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
if (!_glfw.evdev.devices[edid].present)
|
||||
continue;
|
||||
if (strcmp(_glfw.evdev.devices[edid].path, path) == 0)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
if (!_glfw.evdev.devices[edid].present)
|
||||
break;
|
||||
}
|
||||
|
||||
if (edid > GLFW_EVENT_DEVICES_MAX)
|
||||
return GLFW_FALSE;
|
||||
|
||||
ed = _glfw.evdev.devices + edid;
|
||||
|
||||
fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
|
||||
strncpy(name, "Unknown", sizeof(name));
|
||||
|
||||
ed->fd = fd;
|
||||
ed->present = GLFW_TRUE;
|
||||
ed->name = _glfw_strdup(name);
|
||||
strncpy(ed->path, path, sizeof(ed->path));
|
||||
|
||||
pollEventDeviceState(ed);
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Frees all resources associated with the specified event device
|
||||
//
|
||||
static void closeEventDevice(_GLFWeventDevice* ed)
|
||||
{
|
||||
free(ed->name);
|
||||
close(ed->fd);
|
||||
memset(ed, 0, sizeof(_GLFWeventDevice));
|
||||
updateMods();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize evdev interface
|
||||
//
|
||||
GLFWbool _glfwInitEvdev()
|
||||
{
|
||||
DIR* dir;
|
||||
int count = 0;
|
||||
const char* dirname = "/dev/input";
|
||||
|
||||
_glfw.evdev.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (_glfw.evdev.inotify > 0)
|
||||
{
|
||||
// HACK: Register for IN_ATTRIB to get notified when udev is done
|
||||
// This works well in practice but the true way is libudev
|
||||
|
||||
_glfw.evdev.watch = inotify_add_watch(_glfw.evdev.inotify,
|
||||
dirname,
|
||||
IN_CREATE | IN_ATTRIB | IN_DELETE);
|
||||
}
|
||||
|
||||
// Continue without device connection notifications if inotify fails
|
||||
|
||||
if (regcomp(&_glfw.evdev.regex, "^event[0-9]\\+$", 0) != 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Evdev: Failed to compile regex");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
dir = opendir(dirname);
|
||||
if (dir)
|
||||
{
|
||||
struct dirent* entry;
|
||||
|
||||
while ((entry = readdir(dir)))
|
||||
{
|
||||
regmatch_t match;
|
||||
|
||||
if (regexec(&_glfw.evdev.regex, entry->d_name, 1, &match, 0) != 0)
|
||||
continue;
|
||||
|
||||
char path[PATH_MAX];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
|
||||
|
||||
if (openEventDevice(path))
|
||||
count++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
// Continue with no devices if enumeration fails
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwTerminateEvdev()
|
||||
{
|
||||
int edid;
|
||||
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
_GLFWeventDevice* ed = _glfw.evdev.devices + edid;
|
||||
if (ed->present)
|
||||
closeEventDevice(ed);
|
||||
}
|
||||
|
||||
regfree(&_glfw.evdev.regex);
|
||||
|
||||
if (_glfw.evdev.inotify > 0)
|
||||
{
|
||||
if (_glfw.evdev.watch > 0)
|
||||
inotify_rm_watch(_glfw.evdev.inotify, _glfw.evdev.watch);
|
||||
|
||||
close(_glfw.evdev.inotify);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwDetectEvdevConnection()
|
||||
{
|
||||
ssize_t offset = 0;
|
||||
char buffer[16384];
|
||||
|
||||
if (_glfw.evdev.inotify <= 0)
|
||||
return;
|
||||
|
||||
const ssize_t size = read(_glfw.evdev.inotify, buffer, sizeof(buffer));
|
||||
|
||||
while (size > offset)
|
||||
{
|
||||
regmatch_t match;
|
||||
const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
|
||||
|
||||
offset += sizeof(struct inotify_event) + e->len;
|
||||
|
||||
if (regexec(&_glfw.evdev.regex, e->name, 1, &match, 0) != 0)
|
||||
continue;
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
|
||||
|
||||
if (e->mask & (IN_CREATE | IN_ATTRIB))
|
||||
{
|
||||
openEventDevice(path);
|
||||
}
|
||||
else if (e->mask & IN_DELETE)
|
||||
{
|
||||
int edid;
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
if (strcmp(_glfw.evdev.devices[edid].path, path) == 0)
|
||||
{
|
||||
closeEventDevice(_glfw.evdev.devices + edid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPollEvdevDevice( _GLFWeventDevice* ed)
|
||||
{
|
||||
// Read all queued events (non-blocking)
|
||||
for (;;)
|
||||
{
|
||||
struct input_event e;
|
||||
|
||||
errno = 0;
|
||||
if (read(ed->fd, &e, sizeof(e)) < 0)
|
||||
{
|
||||
// Reset the event device slot if the device was disconnected
|
||||
if (errno == ENODEV)
|
||||
closeEventDevice(ed);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.type == EV_SYN)
|
||||
{
|
||||
if (e.code == SYN_DROPPED)
|
||||
ed->dropped = GLFW_TRUE;
|
||||
else if (e.code == SYN_REPORT)
|
||||
{
|
||||
ed->dropped = GLFW_FALSE;
|
||||
ed->scancode = 0;
|
||||
pollEventDeviceState(ed);
|
||||
}
|
||||
}
|
||||
|
||||
if (ed->dropped){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type == EV_MSC){
|
||||
if (e.code == MSC_SCAN){
|
||||
ed->scancode = e.value;
|
||||
}
|
||||
}else if (e.type == EV_KEY)
|
||||
handleKeyEvent(ed, e.code, e.value);
|
||||
else if (e.type == EV_REL)
|
||||
handleRelEvent(ed, e.code, e.value);
|
||||
else if (e.type == EV_LED)
|
||||
handleLedEvent(ed, e.code, e.value);
|
||||
}
|
||||
|
||||
return ed->present;
|
||||
}
|
||||
|
||||
void _glfwPollEvdevDevices()
|
||||
{
|
||||
int edid;
|
||||
_GLFWeventDevice* ed = NULL;
|
||||
|
||||
for (edid = 0; edid <= GLFW_EVENT_DEVICES_MAX; edid++)
|
||||
{
|
||||
ed = _glfw.evdev.devices + edid;
|
||||
if (ed->present)
|
||||
_glfwPollEvdevDevice(ed);
|
||||
}
|
||||
}
|
79
src/evdev.h
Normal file
79
src/evdev.h
Normal file
@ -0,0 +1,79 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/limits.h>
|
||||
#include <regex.h>
|
||||
|
||||
#define _GLFW_PLATFORM_LIBRARY_EVDEV_STATE _GLFWlibraryEvdev evdev
|
||||
|
||||
#define GLFW_EVENT_DEVICES_MAX 16
|
||||
|
||||
#define BITS_PER_LONG (sizeof(long) * 8)
|
||||
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
|
||||
|
||||
// Event device data
|
||||
//
|
||||
typedef struct GLFWeventDevice
|
||||
{
|
||||
GLFWbool present;
|
||||
char* name;
|
||||
int fd;
|
||||
GLFWbool dropped;
|
||||
int scancode;
|
||||
int mods;
|
||||
unsigned long keyState[NBITS(KEY_CNT)];
|
||||
unsigned long ledState[NBITS(LED_MAX)];
|
||||
char path[PATH_MAX];
|
||||
} _GLFWeventDevice;
|
||||
|
||||
// Evdev API data
|
||||
//
|
||||
typedef struct _GLFWlibraryEvdev
|
||||
{
|
||||
int inotify;
|
||||
int watch;
|
||||
regex_t regex;
|
||||
int mods;
|
||||
_GLFWeventDevice devices[GLFW_EVENT_DEVICES_MAX];
|
||||
} _GLFWlibraryEvdev;
|
||||
|
||||
#undef BITS_PER_LONG
|
||||
#undef NBITS
|
||||
|
||||
GLFWbool _glfwInitEvdev();
|
||||
void _glfwTerminateEvdev();
|
||||
void _glfwDetectEvdevConnection();
|
||||
int _glfwPollEvdevDevice(_GLFWeventDevice* ed);
|
||||
void _glfwPollEvdevDevices();
|
||||
|
||||
// Implement it to receive evdev input callbacks
|
||||
//
|
||||
void _glfwEvdevInputKey(int key, int scancode, int action, int mods);
|
||||
void _glfwEvdevInputChar(unsigned int codepoint, int mods, GLFWbool plain);
|
||||
void _glfwEvdevInputScroll(double xoffset, double yoffset);
|
||||
void _glfwEvdevInputMouseClick(int button, int action, int mods);
|
||||
void _glfwEvdevInputCursorPos(double xoffset, double yoffset);
|
@ -583,6 +583,8 @@ struct _GLFWlibrary
|
||||
_GLFW_EGL_LIBRARY_CONTEXT_STATE;
|
||||
// This is defined in osmesa_context.h
|
||||
_GLFW_OSMESA_LIBRARY_CONTEXT_STATE;
|
||||
// This is defined in evdev.h
|
||||
_GLFW_PLATFORM_LIBRARY_EVDEV_STATE;
|
||||
};
|
||||
|
||||
// Global state shared between compilation units of GLFW
|
||||
|
@ -100,6 +100,9 @@ int _glfwPlatformInit(void)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!_glfwInitEvdev())
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (!_glfwInitJoysticksLinux())
|
||||
return GLFW_FALSE;
|
||||
|
||||
@ -114,6 +117,8 @@ void _glfwPlatformTerminate(void)
|
||||
{
|
||||
_glfwTerminateJoysticksLinux();
|
||||
|
||||
_glfwTerminateEvdev();
|
||||
|
||||
if (_glfw.vivante.display)
|
||||
{
|
||||
fbDestroyDisplay(_glfw.vivante.display);
|
||||
@ -144,3 +149,27 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
;
|
||||
}
|
||||
|
||||
void _glfwEvdevInputKey(int key, int scancode, int action, int mods)
|
||||
{
|
||||
printf("_glfwEvdevInputKey key = %i, scancode = %i, action = %i, mods = %i\n", key, scancode, action, mods);
|
||||
}
|
||||
|
||||
void _glfwEvdevInputChar(unsigned int codepoint, int mods, GLFWbool plain)
|
||||
{
|
||||
printf("_glfwEvdevInputChar codepoint = %i, mods = %i, plain = %i\n", codepoint, mods, plain);
|
||||
}
|
||||
|
||||
void _glfwEvdevInputScroll(double xoffset, double yoffset)
|
||||
{
|
||||
printf("_glfwEvdevInputScroll xoffset = %f, yoffset = %f\n", xoffset, yoffset);
|
||||
}
|
||||
|
||||
void _glfwEvdevInputMouseClick(int button, int action, int mods)
|
||||
{
|
||||
printf("_glfwEvdevInputMouseClick button = %i, action = %i, mods = %i\n", button, action, mods);
|
||||
}
|
||||
|
||||
void _glfwEvdevInputCursorPos(double xoffset, double yoffset)
|
||||
{
|
||||
printf("_glfwEvdevInputCursorPos xoffset = %f, yoffset = %f\n", xoffset, yoffset);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
//========================================================================
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
|
||||
@ -37,6 +38,7 @@
|
||||
#include "xkb_unicode.h"
|
||||
#include "egl_context.h"
|
||||
#include "linux_joystick.h"
|
||||
#include "evdev.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
|
@ -297,6 +297,8 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window)
|
||||
|
||||
void _glfwPlatformPollEvents(void)
|
||||
{
|
||||
_glfwDetectEvdevConnection();
|
||||
_glfwPollEvdevDevices();
|
||||
}
|
||||
|
||||
void _glfwPlatformWaitEvents(void)
|
||||
|
@ -172,7 +172,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11
|
||||
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11
|
||||
|
||||
#define _GLFW_PLATFORM_LIBRARY_EVDEV_STATE
|
||||
|
||||
// X11-specific per-window data
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user