blob: 867472233772c6f56da10584c64d6f062df6face [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <dirent.h>
#include <sys/sysmacros.h>
#include <linux/major.h>
#include "log.h"
#include "parse.h"
#include "file.h"
#include "device.h"
#include "async_handle.h"
#include "async_wait.h"
#include "async_io.h"
#include "hostcmd.h"
#include "bitmask.h"
#include "system.h"
#include "system_linux.h"
typedef struct {
PathProcessor *processor;
const PathProcessorParameters *parameters;
} PathProcessingData;
static int
processDirectoryEntry (const char *name, const PathProcessingData *ppd) {
const PathProcessorParameters *parameters = ppd->parameters;
const char *directory = parameters->path;
char path[strlen(directory) + 1 + strlen(name) + 1];
snprintf(
path, sizeof(path), "%s%c%s",
directory, PATH_SEPARATOR_CHARACTER, name
);
return processPathTree(path, ppd->processor, parameters->data);
}
int
processPathTree (const char *path, PathProcessor *processPath, void *data) {
PathProcessorParameters parameters = {
.path = path,
.data = data
};
PathProcessingData ppd = {
.processor = processPath,
.parameters = &parameters
};
int stop = 0;
DIR *directory;
if ((directory = opendir(path))) {
if (processPath(&parameters)) {
struct dirent *entry;
while ((entry = readdir(directory))) {
const char *name = entry->d_name;
if (strcmp(name, CURRENT_DIRECTORY_NAME) == 0) continue;
if (strcmp(name, PARENT_DIRECTORY_NAME) == 0) continue;
if (!processDirectoryEntry(name, &ppd)) {
stop = 1;
break;
}
}
} else {
stop = 1;
}
closedir(directory);
} else if (errno == ENOTDIR) {
if (!processPath(&parameters)) stop = 1;
} else {
logMessage(LOG_WARNING, "can't access path: %s: %s", path, strerror(errno));
}
return !stop;
}
int
compareGroups (gid_t group1, gid_t group2) {
if (group1 < group2) return -1;
if (group1 > group2) return 1;
return 0;
}
static int
groupSorter (const void *element1,const void *element2) {
const gid_t *group1 = element1;
const gid_t *group2 = element2;
return compareGroups(*group1, *group2);
}
void
sortGroups (gid_t *groups, size_t count) {
qsort(groups, count, sizeof(*groups), groupSorter);
}
void
removeDuplicateGroups (gid_t *groups, size_t *count) {
if (*count > 1) {
sortGroups(groups, *count);
gid_t *to = groups;
const gid_t *from = to + 1;
const gid_t *end = to + *count;
while (from < end) {
if (*from != *to) {
if (++to != from) {
*to = *from;
}
}
from += 1;
}
*count = ++to - groups;
}
}
void
processSupplementaryGroups (GroupsProcessor *processGroups, void *data) {
ssize_t size = getgroups(0, NULL);
if (size != -1) {
gid_t groups[size];
ssize_t result = getgroups(size, groups);
if (result != -1) {
size_t count = result;
removeDuplicateGroups(groups, &count);
processGroups(groups, count, data);
} else {
logSystemError("getgroups");
}
} else {
logSystemError("getgroups");
}
}
typedef struct {
const gid_t *groups;
size_t count;
unsigned char have:1;
} HaveGroupsData;
static void
haveGroups (const gid_t *groups, size_t count, void *data) {
HaveGroupsData *hgd = data;
const gid_t *need = hgd->groups;
const gid_t *needEnd = need + hgd->count;
const gid_t *have = groups;
const gid_t *haveEnd = have + count;
while (have < haveEnd) {
if (*have > *need) break;
if (*have++ < *need) continue;
if (++need == needEnd) {
hgd->have = 1;
return;
}
}
hgd->have = 0;
}
int
haveSupplementaryGroups (const gid_t *groups, size_t count) {
HaveGroupsData hgd = {
.groups = groups,
.count = count
};
processSupplementaryGroups(haveGroups, &hgd);
return hgd.have;
}
#ifdef HAVE_LINUX_INPUT_H
#include <linux/input.h>
#ifndef input_event_sec
#define input_event_sec time.tv_sec
#endif /* input_event_sec */
#ifndef input_event_usec
#define input_event_usec time.tv_usec
#endif /* input_event_usec */
#include "kbd_keycodes.h"
LINUX_KEY_MAP(xt00) = {
[XT_KEY_00_Escape] = KEY_ESC,
[XT_KEY_00_F1] = KEY_F1,
[XT_KEY_00_F2] = KEY_F2,
[XT_KEY_00_F3] = KEY_F3,
[XT_KEY_00_F4] = KEY_F4,
[XT_KEY_00_F5] = KEY_F5,
[XT_KEY_00_F6] = KEY_F6,
[XT_KEY_00_F7] = KEY_F7,
[XT_KEY_00_F8] = KEY_F8,
[XT_KEY_00_F9] = KEY_F9,
[XT_KEY_00_F10] = KEY_F10,
[XT_KEY_00_F11] = KEY_F11,
[XT_KEY_00_F12] = KEY_F12,
[XT_KEY_00_SystemRequest] = KEY_SYSRQ,
[XT_KEY_00_ScrollLock] = KEY_SCROLLLOCK,
[XT_KEY_00_F13] = KEY_F13,
[XT_KEY_00_F14] = KEY_F14,
[XT_KEY_00_F15] = KEY_F15,
[XT_KEY_00_F16] = KEY_F16,
[XT_KEY_00_F17] = KEY_F17,
[XT_KEY_00_F18] = KEY_F18,
[XT_KEY_00_F19] = KEY_F19,
[XT_KEY_00_F20] = KEY_F20,
[XT_KEY_00_F21] = KEY_F21,
[XT_KEY_00_F22] = KEY_F22,
[XT_KEY_00_F23] = KEY_F23,
[XT_KEY_00_F24] = KEY_F24,
[XT_KEY_00_Grave] = KEY_GRAVE,
[XT_KEY_00_1] = KEY_1,
[XT_KEY_00_2] = KEY_2,
[XT_KEY_00_3] = KEY_3,
[XT_KEY_00_4] = KEY_4,
[XT_KEY_00_5] = KEY_5,
[XT_KEY_00_6] = KEY_6,
[XT_KEY_00_7] = KEY_7,
[XT_KEY_00_8] = KEY_8,
[XT_KEY_00_9] = KEY_9,
[XT_KEY_00_0] = KEY_0,
[XT_KEY_00_Minus] = KEY_MINUS,
[XT_KEY_00_Equal] = KEY_EQUAL,
[XT_KEY_00_Backspace] = KEY_BACKSPACE,
[XT_KEY_00_Tab] = KEY_TAB,
[XT_KEY_00_Q] = KEY_Q,
[XT_KEY_00_W] = KEY_W,
[XT_KEY_00_E] = KEY_E,
[XT_KEY_00_R] = KEY_R,
[XT_KEY_00_T] = KEY_T,
[XT_KEY_00_Y] = KEY_Y,
[XT_KEY_00_U] = KEY_U,
[XT_KEY_00_I] = KEY_I,
[XT_KEY_00_O] = KEY_O,
[XT_KEY_00_P] = KEY_P,
[XT_KEY_00_LeftBracket] = KEY_LEFTBRACE,
[XT_KEY_00_RightBracket] = KEY_RIGHTBRACE,
[XT_KEY_00_Backslash] = KEY_BACKSLASH,
[XT_KEY_00_CapsLock] = KEY_CAPSLOCK,
[XT_KEY_00_A] = KEY_A,
[XT_KEY_00_S] = KEY_S,
[XT_KEY_00_D] = KEY_D,
[XT_KEY_00_F] = KEY_F,
[XT_KEY_00_G] = KEY_G,
[XT_KEY_00_H] = KEY_H,
[XT_KEY_00_J] = KEY_J,
[XT_KEY_00_K] = KEY_K,
[XT_KEY_00_L] = KEY_L,
[XT_KEY_00_Semicolon] = KEY_SEMICOLON,
[XT_KEY_00_Apostrophe] = KEY_APOSTROPHE,
[XT_KEY_00_Enter] = KEY_ENTER,
[XT_KEY_00_LeftShift] = KEY_LEFTSHIFT,
[XT_KEY_00_Europe2] = KEY_102ND,
[XT_KEY_00_Z] = KEY_Z,
[XT_KEY_00_X] = KEY_X,
[XT_KEY_00_C] = KEY_C,
[XT_KEY_00_V] = KEY_V,
[XT_KEY_00_B] = KEY_B,
[XT_KEY_00_N] = KEY_N,
[XT_KEY_00_M] = KEY_M,
[XT_KEY_00_Comma] = KEY_COMMA,
[XT_KEY_00_Period] = KEY_DOT,
[XT_KEY_00_Slash] = KEY_SLASH,
[XT_KEY_00_RightShift] = KEY_RIGHTSHIFT,
[XT_KEY_00_LeftControl] = KEY_LEFTCTRL,
[XT_KEY_00_LeftAlt] = KEY_LEFTALT,
[XT_KEY_00_Space] = KEY_SPACE,
[XT_KEY_00_NumLock] = KEY_NUMLOCK,
[XT_KEY_00_KPAsterisk] = KEY_KPASTERISK,
[XT_KEY_00_KPMinus] = KEY_KPMINUS,
[XT_KEY_00_KPPlus] = KEY_KPPLUS,
[XT_KEY_00_KPPeriod] = KEY_KPDOT,
[XT_KEY_00_KP0] = KEY_KP0,
[XT_KEY_00_KP1] = KEY_KP1,
[XT_KEY_00_KP2] = KEY_KP2,
[XT_KEY_00_KP3] = KEY_KP3,
[XT_KEY_00_KP4] = KEY_KP4,
[XT_KEY_00_KP5] = KEY_KP5,
[XT_KEY_00_KP6] = KEY_KP6,
[XT_KEY_00_KP7] = KEY_KP7,
[XT_KEY_00_KP8] = KEY_KP8,
[XT_KEY_00_KP9] = KEY_KP9,
[XT_KEY_00_KPComma] = KEY_KPCOMMA,
[XT_KEY_00_KPEqual] = KEY_KPEQUAL,
[XT_KEY_00_International1] = KEY_RO,
[XT_KEY_00_International2] = KEY_KATAKANAHIRAGANA,
[XT_KEY_00_International3] = KEY_YEN,
[XT_KEY_00_International4] = KEY_HENKAN,
[XT_KEY_00_International5] = KEY_MUHENKAN,
[XT_KEY_00_International6] = KEY_KPJPCOMMA,
[XT_KEY_00_Language3] = KEY_KATAKANA,
[XT_KEY_00_Language4] = KEY_HIRAGANA,
};
LINUX_KEY_MAP(xtE0) = {
[XT_KEY_E0_LeftGUI] = KEY_LEFTMETA,
[XT_KEY_E0_RightAlt] = KEY_RIGHTALT,
[XT_KEY_E0_RightGUI] = KEY_RIGHTMETA,
[XT_KEY_E0_Context] = KEY_COMPOSE,
[XT_KEY_E0_RightControl] = KEY_RIGHTCTRL,
[XT_KEY_E0_Insert] = KEY_INSERT,
[XT_KEY_E0_Delete] = KEY_DELETE,
[XT_KEY_E0_Home] = KEY_HOME,
[XT_KEY_E0_End] = KEY_END,
[XT_KEY_E0_PageUp] = KEY_PAGEUP,
[XT_KEY_E0_PageDown] = KEY_PAGEDOWN,
[XT_KEY_E0_ArrowUp] = KEY_UP,
[XT_KEY_E0_ArrowLeft] = KEY_LEFT,
[XT_KEY_E0_ArrowDown] = KEY_DOWN,
[XT_KEY_E0_ArrowRight] = KEY_RIGHT,
[XT_KEY_E0_KPEnter] = KEY_KPENTER,
[XT_KEY_E0_KPSlash] = KEY_KPSLASH,
[XT_KEY_E0_Copy] = KEY_COPY,
[XT_KEY_E0_Cut] = KEY_CUT,
[XT_KEY_E0_Paste] = KEY_PASTE,
[XT_KEY_E0_Undo] = KEY_UNDO,
[XT_KEY_E0_Redo] = KEY_REDO,
[XT_KEY_E0_MyComputer] = KEY_COMPUTER,
[XT_KEY_E0_Calculator] = KEY_CALC,
[XT_KEY_E0_Mail] = KEY_MAIL,
[XT_KEY_E0_Mail_X1] = KEY_MAIL,
[XT_KEY_E0_WebHome] = KEY_HOMEPAGE,
[XT_KEY_E0_WebBookmarks] = KEY_BOOKMARKS,
[XT_KEY_E0_WebSearch] = KEY_SEARCH,
[XT_KEY_E0_WebBack] = KEY_BACK,
[XT_KEY_E0_WebForward] = KEY_FORWARD,
[XT_KEY_E0_WebRefresh] = KEY_REFRESH,
[XT_KEY_E0_WebStop] = KEY_STOP,
[XT_KEY_E0_Mute] = KEY_MUTE,
[XT_KEY_E0_VolumeDown] = KEY_VOLUMEDOWN,
[XT_KEY_E0_VolumeUp] = KEY_VOLUMEUP,
[XT_KEY_E0_MediaVideo] = KEY_MEDIA,
[XT_KEY_E0_MediaPlayPause] = KEY_PLAYPAUSE,
[XT_KEY_E0_MediaStop] = KEY_STOPCD,
[XT_KEY_E0_MediaPrevious] = KEY_PREVIOUSSONG,
[XT_KEY_E0_MediaNext] = KEY_NEXTSONG,
[XT_KEY_E0_Power] = KEY_POWER,
[XT_KEY_E0_Sleep] = KEY_SLEEP,
[XT_KEY_E0_Wake] = KEY_WAKEUP,
};
LINUX_KEY_MAP(xtE1) = {
[XT_KEY_E1_Pause] = KEY_PAUSE,
};
LINUX_KEY_MAP(at00) = {
[AT_KEY_00_Escape] = KEY_ESC,
[AT_KEY_00_F1] = KEY_F1,
[AT_KEY_00_F2] = KEY_F2,
[AT_KEY_00_F3] = KEY_F3,
[AT_KEY_00_F4] = KEY_F4,
[AT_KEY_00_F5] = KEY_F5,
[AT_KEY_00_F6] = KEY_F6,
[AT_KEY_00_F7] = KEY_F7,
[AT_KEY_00_F7_X1] = KEY_F7,
[AT_KEY_00_F8] = KEY_F8,
[AT_KEY_00_F9] = KEY_F9,
[AT_KEY_00_F10] = KEY_F10,
[AT_KEY_00_F11] = KEY_F11,
[AT_KEY_00_F12] = KEY_F12,
[AT_KEY_00_SystemRequest] = KEY_SYSRQ,
[AT_KEY_00_ScrollLock] = KEY_SCROLLLOCK,
[AT_KEY_00_F13] = KEY_F13,
[AT_KEY_00_F14] = KEY_F14,
[AT_KEY_00_F15] = KEY_F15,
[AT_KEY_00_F16] = KEY_F16,
[AT_KEY_00_F17] = KEY_F17,
[AT_KEY_00_F18] = KEY_F18,
[AT_KEY_00_F19] = KEY_F19,
[AT_KEY_00_F20] = KEY_F20,
[AT_KEY_00_F21] = KEY_F21,
[AT_KEY_00_F22] = KEY_F22,
[AT_KEY_00_F23] = KEY_F23,
[AT_KEY_00_F24] = KEY_F24,
[AT_KEY_00_Grave] = KEY_GRAVE,
[AT_KEY_00_1] = KEY_1,
[AT_KEY_00_2] = KEY_2,
[AT_KEY_00_3] = KEY_3,
[AT_KEY_00_4] = KEY_4,
[AT_KEY_00_5] = KEY_5,
[AT_KEY_00_6] = KEY_6,
[AT_KEY_00_7] = KEY_7,
[AT_KEY_00_8] = KEY_8,
[AT_KEY_00_9] = KEY_9,
[AT_KEY_00_0] = KEY_0,
[AT_KEY_00_Minus] = KEY_MINUS,
[AT_KEY_00_Equal] = KEY_EQUAL,
[AT_KEY_00_Backspace] = KEY_BACKSPACE,
[AT_KEY_00_Tab] = KEY_TAB,
[AT_KEY_00_Q] = KEY_Q,
[AT_KEY_00_W] = KEY_W,
[AT_KEY_00_E] = KEY_E,
[AT_KEY_00_R] = KEY_R,
[AT_KEY_00_T] = KEY_T,
[AT_KEY_00_Y] = KEY_Y,
[AT_KEY_00_U] = KEY_U,
[AT_KEY_00_I] = KEY_I,
[AT_KEY_00_O] = KEY_O,
[AT_KEY_00_P] = KEY_P,
[AT_KEY_00_LeftBracket] = KEY_LEFTBRACE,
[AT_KEY_00_RightBracket] = KEY_RIGHTBRACE,
[AT_KEY_00_Backslash] = KEY_BACKSLASH,
[AT_KEY_00_CapsLock] = KEY_CAPSLOCK,
[AT_KEY_00_A] = KEY_A,
[AT_KEY_00_S] = KEY_S,
[AT_KEY_00_D] = KEY_D,
[AT_KEY_00_F] = KEY_F,
[AT_KEY_00_G] = KEY_G,
[AT_KEY_00_H] = KEY_H,
[AT_KEY_00_J] = KEY_J,
[AT_KEY_00_K] = KEY_K,
[AT_KEY_00_L] = KEY_L,
[AT_KEY_00_Semicolon] = KEY_SEMICOLON,
[AT_KEY_00_Apostrophe] = KEY_APOSTROPHE,
[AT_KEY_00_Enter] = KEY_ENTER,
[AT_KEY_00_LeftShift] = KEY_LEFTSHIFT,
[AT_KEY_00_Europe2] = KEY_102ND,
[AT_KEY_00_Z] = KEY_Z,
[AT_KEY_00_X] = KEY_X,
[AT_KEY_00_C] = KEY_C,
[AT_KEY_00_V] = KEY_V,
[AT_KEY_00_B] = KEY_B,
[AT_KEY_00_N] = KEY_N,
[AT_KEY_00_M] = KEY_M,
[AT_KEY_00_Comma] = KEY_COMMA,
[AT_KEY_00_Period] = KEY_DOT,
[AT_KEY_00_Slash] = KEY_SLASH,
[AT_KEY_00_RightShift] = KEY_RIGHTSHIFT,
[AT_KEY_00_LeftControl] = KEY_LEFTCTRL,
[AT_KEY_00_LeftAlt] = KEY_LEFTALT,
[AT_KEY_00_Space] = KEY_SPACE,
[AT_KEY_00_NumLock] = KEY_NUMLOCK,
[AT_KEY_00_KPAsterisk] = KEY_KPASTERISK,
[AT_KEY_00_KPMinus] = KEY_KPMINUS,
[AT_KEY_00_KPPlus] = KEY_KPPLUS,
[AT_KEY_00_KPPeriod] = KEY_KPDOT,
[AT_KEY_00_KP0] = KEY_KP0,
[AT_KEY_00_KP1] = KEY_KP1,
[AT_KEY_00_KP2] = KEY_KP2,
[AT_KEY_00_KP3] = KEY_KP3,
[AT_KEY_00_KP4] = KEY_KP4,
[AT_KEY_00_KP5] = KEY_KP5,
[AT_KEY_00_KP6] = KEY_KP6,
[AT_KEY_00_KP7] = KEY_KP7,
[AT_KEY_00_KP8] = KEY_KP8,
[AT_KEY_00_KP9] = KEY_KP9,
[AT_KEY_00_KPComma] = KEY_KPCOMMA,
[AT_KEY_00_KPEqual] = KEY_KPEQUAL,
[AT_KEY_00_International1] = KEY_RO,
[AT_KEY_00_International2] = KEY_KATAKANAHIRAGANA,
[AT_KEY_00_International3] = KEY_YEN,
[AT_KEY_00_International4] = KEY_HENKAN,
[AT_KEY_00_International5] = KEY_MUHENKAN,
[AT_KEY_00_International6] = KEY_KPJPCOMMA,
[AT_KEY_00_Language3] = KEY_KATAKANA,
[AT_KEY_00_Language4] = KEY_HIRAGANA,
};
LINUX_KEY_MAP(atE0) = {
[AT_KEY_E0_LeftGUI] = KEY_LEFTMETA,
[AT_KEY_E0_RightAlt] = KEY_RIGHTALT,
[AT_KEY_E0_RightGUI] = KEY_RIGHTMETA,
[AT_KEY_E0_Context] = KEY_COMPOSE,
[AT_KEY_E0_RightControl] = KEY_RIGHTCTRL,
[AT_KEY_E0_Insert] = KEY_INSERT,
[AT_KEY_E0_Delete] = KEY_DELETE,
[AT_KEY_E0_Home] = KEY_HOME,
[AT_KEY_E0_End] = KEY_END,
[AT_KEY_E0_PageUp] = KEY_PAGEUP,
[AT_KEY_E0_PageDown] = KEY_PAGEDOWN,
[AT_KEY_E0_ArrowUp] = KEY_UP,
[AT_KEY_E0_ArrowLeft] = KEY_LEFT,
[AT_KEY_E0_ArrowDown] = KEY_DOWN,
[AT_KEY_E0_ArrowRight] = KEY_RIGHT,
[AT_KEY_E0_KPEnter] = KEY_KPENTER,
[AT_KEY_E0_KPSlash] = KEY_KPSLASH,
[AT_KEY_E0_Copy] = KEY_COPY,
[AT_KEY_E0_Cut] = KEY_CUT,
[AT_KEY_E0_Paste] = KEY_PASTE,
[AT_KEY_E0_Undo] = KEY_UNDO,
[AT_KEY_E0_Redo] = KEY_REDO,
[AT_KEY_E0_MyComputer] = KEY_COMPUTER,
[AT_KEY_E0_Calculator] = KEY_CALC,
[AT_KEY_E0_Mail] = KEY_MAIL,
[AT_KEY_E0_Mail_X1] = KEY_MAIL,
[AT_KEY_E0_WebHome] = KEY_HOMEPAGE,
[AT_KEY_E0_WebBookmarks] = KEY_BOOKMARKS,
[AT_KEY_E0_WebSearch] = KEY_SEARCH,
[AT_KEY_E0_WebBack] = KEY_BACK,
[AT_KEY_E0_WebForward] = KEY_FORWARD,
[AT_KEY_E0_WebRefresh] = KEY_REFRESH,
[AT_KEY_E0_WebStop] = KEY_STOP,
[AT_KEY_E0_Mute] = KEY_MUTE,
[AT_KEY_E0_VolumeDown] = KEY_VOLUMEDOWN,
[AT_KEY_E0_VolumeUp] = KEY_VOLUMEUP,
[AT_KEY_E0_MediaVideo] = KEY_MEDIA,
[AT_KEY_E0_MediaPlayPause] = KEY_PLAYPAUSE,
[AT_KEY_E0_MediaStop] = KEY_STOPCD,
[AT_KEY_E0_MediaPrevious] = KEY_PREVIOUSSONG,
[AT_KEY_E0_MediaNext] = KEY_NEXTSONG,
[AT_KEY_E0_Power] = KEY_POWER,
[AT_KEY_E0_Sleep] = KEY_SLEEP,
[AT_KEY_E0_Wake] = KEY_WAKEUP,
};
LINUX_KEY_MAP(atE1) = {
[AT_KEY_E1_Pause] = KEY_PAUSE,
};
LINUX_KEY_MAP(ps2) = {
[PS2_KEY_Escape] = KEY_ESC,
[PS2_KEY_F1] = KEY_F1,
[PS2_KEY_F2] = KEY_F2,
[PS2_KEY_F3] = KEY_F3,
[PS2_KEY_F4] = KEY_F4,
[PS2_KEY_F5] = KEY_F5,
[PS2_KEY_F6] = KEY_F6,
[PS2_KEY_F7] = KEY_F7,
[PS2_KEY_F8] = KEY_F8,
[PS2_KEY_F9] = KEY_F9,
[PS2_KEY_F10] = KEY_F10,
[PS2_KEY_F11] = KEY_F11,
[PS2_KEY_F12] = KEY_F12,
[PS2_KEY_Pause] = KEY_PAUSE,
[PS2_KEY_ScrollLock] = KEY_SCROLLLOCK,
[PS2_KEY_Grave] = KEY_GRAVE,
[PS2_KEY_1] = KEY_1,
[PS2_KEY_2] = KEY_2,
[PS2_KEY_3] = KEY_3,
[PS2_KEY_4] = KEY_4,
[PS2_KEY_5] = KEY_5,
[PS2_KEY_6] = KEY_6,
[PS2_KEY_7] = KEY_7,
[PS2_KEY_8] = KEY_8,
[PS2_KEY_9] = KEY_9,
[PS2_KEY_0] = KEY_0,
[PS2_KEY_Minus] = KEY_MINUS,
[PS2_KEY_Equal] = KEY_EQUAL,
[PS2_KEY_Backspace] = KEY_BACKSPACE,
[PS2_KEY_Tab] = KEY_TAB,
[PS2_KEY_Q] = KEY_Q,
[PS2_KEY_W] = KEY_W,
[PS2_KEY_E] = KEY_E,
[PS2_KEY_R] = KEY_R,
[PS2_KEY_T] = KEY_T,
[PS2_KEY_Y] = KEY_Y,
[PS2_KEY_U] = KEY_U,
[PS2_KEY_I] = KEY_I,
[PS2_KEY_O] = KEY_O,
[PS2_KEY_P] = KEY_P,
[PS2_KEY_LeftBracket] = KEY_LEFTBRACE,
[PS2_KEY_RightBracket] = KEY_RIGHTBRACE,
[PS2_KEY_Backslash] = KEY_BACKSLASH,
[PS2_KEY_Europe1] = KEY_BACKSLASH,
[PS2_KEY_CapsLock] = KEY_CAPSLOCK,
[PS2_KEY_A] = KEY_A,
[PS2_KEY_S] = KEY_S,
[PS2_KEY_D] = KEY_D,
[PS2_KEY_F] = KEY_F,
[PS2_KEY_G] = KEY_G,
[PS2_KEY_H] = KEY_H,
[PS2_KEY_J] = KEY_J,
[PS2_KEY_K] = KEY_K,
[PS2_KEY_L] = KEY_L,
[PS2_KEY_Semicolon] = KEY_SEMICOLON,
[PS2_KEY_Apostrophe] = KEY_APOSTROPHE,
[PS2_KEY_Enter] = KEY_ENTER,
[PS2_KEY_LeftShift] = KEY_LEFTSHIFT,
[PS2_KEY_Europe2] = KEY_102ND,
[PS2_KEY_Z] = KEY_Z,
[PS2_KEY_X] = KEY_X,
[PS2_KEY_C] = KEY_C,
[PS2_KEY_V] = KEY_V,
[PS2_KEY_B] = KEY_B,
[PS2_KEY_N] = KEY_N,
[PS2_KEY_M] = KEY_M,
[PS2_KEY_Comma] = KEY_COMMA,
[PS2_KEY_Period] = KEY_DOT,
[PS2_KEY_Slash] = KEY_SLASH,
[PS2_KEY_RightShift] = KEY_RIGHTSHIFT,
[PS2_KEY_LeftControl] = KEY_LEFTCTRL,
[PS2_KEY_LeftAlt] = KEY_LEFTALT,
[PS2_KEY_LeftGUI] = KEY_LEFTMETA,
[PS2_KEY_Space] = KEY_SPACE,
[PS2_KEY_RightAlt] = KEY_RIGHTALT,
[PS2_KEY_RightGUI] = KEY_RIGHTMETA,
[PS2_KEY_Context] = KEY_COMPOSE,
[PS2_KEY_RightControl] = KEY_RIGHTCTRL,
[PS2_KEY_Insert] = KEY_INSERT,
[PS2_KEY_Delete] = KEY_DELETE,
[PS2_KEY_Home] = KEY_HOME,
[PS2_KEY_End] = KEY_END,
[PS2_KEY_PageUp] = KEY_PAGEUP,
[PS2_KEY_PageDown] = KEY_PAGEDOWN,
[PS2_KEY_ArrowUp] = KEY_UP,
[PS2_KEY_ArrowLeft] = KEY_LEFT,
[PS2_KEY_ArrowDown] = KEY_DOWN,
[PS2_KEY_ArrowRight] = KEY_RIGHT,
[PS2_KEY_NumLock] = KEY_NUMLOCK,
[PS2_KEY_KPSlash] = KEY_KPSLASH,
[PS2_KEY_KPAsterisk] = KEY_KPASTERISK,
[PS2_KEY_KPMinus] = KEY_KPMINUS,
[PS2_KEY_KPPlus] = KEY_KPPLUS,
[PS2_KEY_KPEnter] = KEY_KPENTER,
[PS2_KEY_KPPeriod] = KEY_KPDOT,
[PS2_KEY_KP0] = KEY_KP0,
[PS2_KEY_KP1] = KEY_KP1,
[PS2_KEY_KP2] = KEY_KP2,
[PS2_KEY_KP3] = KEY_KP3,
[PS2_KEY_KP4] = KEY_KP4,
[PS2_KEY_KP5] = KEY_KP5,
[PS2_KEY_KP6] = KEY_KP6,
[PS2_KEY_KP7] = KEY_KP7,
[PS2_KEY_KP8] = KEY_KP8,
[PS2_KEY_KP9] = KEY_KP9,
[PS2_KEY_KPComma] = KEY_KPCOMMA,
[PS2_KEY_International1] = KEY_RO,
[PS2_KEY_International2] = KEY_KATAKANAHIRAGANA,
[PS2_KEY_International3] = KEY_YEN,
[PS2_KEY_International4] = KEY_HENKAN,
[PS2_KEY_International5] = KEY_MUHENKAN,
};
LINUX_KEY_MAP(hid) = {
[HID_KEY_Escape] = KEY_ESC,
[HID_KEY_F1] = KEY_F1,
[HID_KEY_F2] = KEY_F2,
[HID_KEY_F3] = KEY_F3,
[HID_KEY_F4] = KEY_F4,
[HID_KEY_F5] = KEY_F5,
[HID_KEY_F6] = KEY_F6,
[HID_KEY_F7] = KEY_F7,
[HID_KEY_F8] = KEY_F8,
[HID_KEY_F9] = KEY_F9,
[HID_KEY_F10] = KEY_F10,
[HID_KEY_F11] = KEY_F11,
[HID_KEY_F12] = KEY_F12,
[HID_KEY_Pause] = KEY_PAUSE,
[HID_KEY_ScrollLock] = KEY_SCROLLLOCK,
[HID_KEY_F13] = KEY_F13,
[HID_KEY_F14] = KEY_F14,
[HID_KEY_F15] = KEY_F15,
[HID_KEY_F16] = KEY_F16,
[HID_KEY_F17] = KEY_F17,
[HID_KEY_F18] = KEY_F18,
[HID_KEY_F19] = KEY_F19,
[HID_KEY_F20] = KEY_F20,
[HID_KEY_F21] = KEY_F21,
[HID_KEY_F22] = KEY_F22,
[HID_KEY_F23] = KEY_F23,
[HID_KEY_F24] = KEY_F24,
[HID_KEY_Grave] = KEY_GRAVE,
[HID_KEY_1] = KEY_1,
[HID_KEY_2] = KEY_2,
[HID_KEY_3] = KEY_3,
[HID_KEY_4] = KEY_4,
[HID_KEY_5] = KEY_5,
[HID_KEY_6] = KEY_6,
[HID_KEY_7] = KEY_7,
[HID_KEY_8] = KEY_8,
[HID_KEY_9] = KEY_9,
[HID_KEY_0] = KEY_0,
[HID_KEY_Minus] = KEY_MINUS,
[HID_KEY_Equal] = KEY_EQUAL,
[HID_KEY_Backspace] = KEY_BACKSPACE,
[HID_KEY_Tab] = KEY_TAB,
[HID_KEY_Q] = KEY_Q,
[HID_KEY_W] = KEY_W,
[HID_KEY_E] = KEY_E,
[HID_KEY_R] = KEY_R,
[HID_KEY_T] = KEY_T,
[HID_KEY_Y] = KEY_Y,
[HID_KEY_U] = KEY_U,
[HID_KEY_I] = KEY_I,
[HID_KEY_O] = KEY_O,
[HID_KEY_P] = KEY_P,
[HID_KEY_LeftBracket] = KEY_LEFTBRACE,
[HID_KEY_RightBracket] = KEY_RIGHTBRACE,
[HID_KEY_Backslash] = KEY_BACKSLASH,
[HID_KEY_Europe1] = KEY_BACKSLASH,
[HID_KEY_CapsLock] = KEY_CAPSLOCK,
[HID_KEY_A] = KEY_A,
[HID_KEY_S] = KEY_S,
[HID_KEY_D] = KEY_D,
[HID_KEY_F] = KEY_F,
[HID_KEY_G] = KEY_G,
[HID_KEY_H] = KEY_H,
[HID_KEY_J] = KEY_J,
[HID_KEY_K] = KEY_K,
[HID_KEY_L] = KEY_L,
[HID_KEY_Semicolon] = KEY_SEMICOLON,
[HID_KEY_Apostrophe] = KEY_APOSTROPHE,
[HID_KEY_Enter] = KEY_ENTER,
[HID_KEY_LeftShift] = KEY_LEFTSHIFT,
[HID_KEY_Europe2] = KEY_102ND,
[HID_KEY_Z] = KEY_Z,
[HID_KEY_X] = KEY_X,
[HID_KEY_C] = KEY_C,
[HID_KEY_V] = KEY_V,
[HID_KEY_B] = KEY_B,
[HID_KEY_N] = KEY_N,
[HID_KEY_M] = KEY_M,
[HID_KEY_Comma] = KEY_COMMA,
[HID_KEY_Period] = KEY_DOT,
[HID_KEY_Slash] = KEY_SLASH,
[HID_KEY_RightShift] = KEY_RIGHTSHIFT,
[HID_KEY_LeftControl] = KEY_LEFTCTRL,
[HID_KEY_LeftAlt] = KEY_LEFTALT,
[HID_KEY_LeftGUI] = KEY_LEFTMETA,
[HID_KEY_Space] = KEY_SPACE,
[HID_KEY_RightAlt] = KEY_RIGHTALT,
[HID_KEY_RightGUI] = KEY_RIGHTMETA,
[HID_KEY_Context] = KEY_COMPOSE,
[HID_KEY_RightControl] = KEY_RIGHTCTRL,
[HID_KEY_Insert] = KEY_INSERT,
[HID_KEY_Delete] = KEY_DELETE,
[HID_KEY_Home] = KEY_HOME,
[HID_KEY_End] = KEY_END,
[HID_KEY_PageUp] = KEY_PAGEUP,
[HID_KEY_PageDown] = KEY_PAGEDOWN,
[HID_KEY_ArrowUp] = KEY_UP,
[HID_KEY_ArrowLeft] = KEY_LEFT,
[HID_KEY_ArrowDown] = KEY_DOWN,
[HID_KEY_ArrowRight] = KEY_RIGHT,
[HID_KEY_NumLock] = KEY_NUMLOCK,
[HID_KEY_KPSlash] = KEY_KPSLASH,
[HID_KEY_KPAsterisk] = KEY_KPASTERISK,
[HID_KEY_KPMinus] = KEY_KPMINUS,
[HID_KEY_KPPlus] = KEY_KPPLUS,
[HID_KEY_KPEnter] = KEY_KPENTER,
[HID_KEY_KPPeriod] = KEY_KPDOT,
[HID_KEY_KP0] = KEY_KP0,
[HID_KEY_KP1] = KEY_KP1,
[HID_KEY_KP2] = KEY_KP2,
[HID_KEY_KP3] = KEY_KP3,
[HID_KEY_KP4] = KEY_KP4,
[HID_KEY_KP5] = KEY_KP5,
[HID_KEY_KP6] = KEY_KP6,
[HID_KEY_KP7] = KEY_KP7,
[HID_KEY_KP8] = KEY_KP8,
[HID_KEY_KP9] = KEY_KP9,
[HID_KEY_KPComma] = KEY_KPCOMMA,
[HID_KEY_KPEqual] = KEY_KPEQUAL,
[HID_KEY_International1] = KEY_RO,
[HID_KEY_International2] = KEY_KATAKANAHIRAGANA,
[HID_KEY_International3] = KEY_YEN,
[HID_KEY_International4] = KEY_HENKAN,
[HID_KEY_International5] = KEY_MUHENKAN,
[HID_KEY_International6] = KEY_KPJPCOMMA,
[HID_KEY_Language3] = KEY_KATAKANA,
[HID_KEY_Language4] = KEY_HIRAGANA,
[HID_KEY_Language5] = KEY_ZENKAKUHANKAKU,
[HID_KEY_Copy] = KEY_COPY,
[HID_KEY_Cut] = KEY_CUT,
[HID_KEY_Paste] = KEY_PASTE,
[HID_KEY_Undo] = KEY_UNDO,
[HID_KEY_Mute] = KEY_MUTE,
[HID_KEY_VolumeDown] = KEY_VOLUMEDOWN,
[HID_KEY_VolumeUp] = KEY_VOLUMEUP,
[HID_KEY_Power] = KEY_POWER,
};
#define LINUX_KEY_MAP_DESCRIPTOR(type) { \
.name = #type, \
.keys = LINUX_KEY_MAP_NAME(type), \
.count = ARRAY_COUNT(LINUX_KEY_MAP_NAME(type)) \
}
const LinuxKeyMapDescriptor linuxKeyMapDescriptors[] = {
LINUX_KEY_MAP_DESCRIPTOR(xt00),
LINUX_KEY_MAP_DESCRIPTOR(xtE0),
LINUX_KEY_MAP_DESCRIPTOR(xtE1),
LINUX_KEY_MAP_DESCRIPTOR(at00),
LINUX_KEY_MAP_DESCRIPTOR(atE0),
LINUX_KEY_MAP_DESCRIPTOR(atE1),
LINUX_KEY_MAP_DESCRIPTOR(ps2),
LINUX_KEY_MAP_DESCRIPTOR(hid)
};
const unsigned char linuxKeyMapCount = ARRAY_COUNT(linuxKeyMapDescriptors);
#endif /* HAVE_LINUX_INPUT_H */
#ifdef HAVE_LINUX_UINPUT_H
#include <linux/uinput.h>
struct UinputObjectStruct {
int fileDescriptor;
BITMASK(pressedKeys, KEY_MAX+1, char);
};
#endif /* HAVE_LINUX_UINPUT_H */
int
installKernelModule (const char *name, unsigned char *status) {
if (status && *status) return *status == 2;
{
const char *command = "modprobe";
char buffer[0X100];
if (status) ++*status;
{
const char *path = "/proc/sys/kernel/modprobe";
FILE *stream = fopen(path, "r");
if (stream) {
char *line = fgets(buffer, sizeof(buffer), stream);
if (line) {
size_t length = strlen(line);
if (length && (line[length-1] == '\n')) line[--length] = 0;
if (length) command = line;
}
fclose(stream);
} else {
logMessage(LOG_WARNING, "cannot open %s: %s", path, strerror(errno));
}
}
{
const char *const arguments[] = {command, "-q", name, NULL};
int ok = executeHostCommand(arguments) == 0;
if (!ok) {
logMessage(LOG_WARNING, "kernel module not installed: %s", name);
return 0;
}
if (status) ++*status;
}
}
return 1;
}
int
installSpeakerModule (void) {
static unsigned char status = 0;
return installKernelModule("pcspkr", &status);
}
int
installUinputModule (void) {
static unsigned char status = 0;
int wait = !status;
int installed = installKernelModule("uinput", &status);
if (!installed) wait = 0;
if (wait) asyncWait(500);
return installed;
}
static int
openDevice (const char *path, int flags, int allowModeSubset) {
int descriptor;
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif /* O_CLOEXEC */
if ((descriptor = open(path, flags)) != -1) goto opened;
if (!allowModeSubset) goto failed;
if ((flags & O_ACCMODE) != O_RDWR) goto failed;
flags &= ~O_ACCMODE;
{
int error = errno;
if (errno == EACCES) goto tryWriteOnly;
if (errno == EROFS) goto tryReadOnly;
goto failed;
tryWriteOnly:
if ((descriptor = open(path, (flags | O_WRONLY))) != -1) goto opened;
tryReadOnly:
if ((descriptor = open(path, (flags | O_RDONLY))) != -1) goto opened;
errno = error;
}
failed:
logMessage(LOG_DEBUG, "cannot open device: %s: %s", path, strerror(errno));
return -1;
opened:
logMessage(LOG_DEBUG, "device opened: %s: fd=%d", path, descriptor);
return descriptor;
}
static int
canContainDevices (const char *directory) {
struct statvfs vfs;
if (statvfs(directory, &vfs) == -1) {
logSystemError("statvfs");
} else if (vfs.f_flag & ST_NODEV) {
logMessage(LOG_WARNING, "cannot contain device files: %s", directory);
errno = EPERM;
} else {
return 1;
}
return 0;
}
static int
canCreateDevice (const char *path) {
int yes = 0;
char *directory;
if ((directory = getPathDirectory(path))) {
if (canContainDevices(directory)) yes = 1;
free(directory);
}
return yes;
}
static int
createCharacterDevice (const char *path, int flags, int major, int minor) {
int descriptor = -1;
if (canCreateDevice(path)) {
descriptor = openDevice(path, flags, 0);
}
if (descriptor == -1) {
if (errno == ENOENT) {
mode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
if (mknod(path, mode, makedev(major, minor)) == -1) {
logMessage(LOG_DEBUG,
"cannot create device: %s: %s", path, strerror(errno)
);
} else {
logMessage(LOG_DEBUG,
"device created: %s mode=%06o major=%d minor=%d",
path, mode, major, minor
);
descriptor = openDevice(path, flags, 0);
}
}
}
return descriptor;
}
int
openCharacterDevice (const char *name, int flags, int major, int minor) {
char *path = getDevicePath(name);
int descriptor;
if (!path) {
descriptor = -1;
} else if ((descriptor = openDevice(path, flags, 1)) == -1) {
if ((errno == ENOENT) || (errno == EACCES)) {
free(path);
if ((path = makeWritablePath(locatePathName(name)))) {
descriptor = createCharacterDevice(path, flags, major, minor);
}
}
}
if (descriptor != -1) {
int ok = 0;
struct stat status;
if (fstat(descriptor, &status) == -1) {
logMessage(LOG_DEBUG, "cannot fstat device: %d [%s]: %s",
descriptor, path, strerror(errno));
} else if (!S_ISCHR(status.st_mode)) {
logMessage(LOG_DEBUG, "not a character device: %s: fd=%d", path, descriptor);
} else {
ok = 1;
}
if (!ok) {
close(descriptor);
logMessage(LOG_DEBUG, "device closed: %s: fd=%d", path, descriptor);
descriptor = -1;
}
}
if (path) free(path);
return descriptor;
}
UinputObject *
newUinputObject (const char *name) {
#ifdef HAVE_LINUX_UINPUT_H
UinputObject *uinput;
if ((uinput = malloc(sizeof(*uinput)))) {
memset(uinput, 0, sizeof(*uinput));
installUinputModule();
const char *device;
{
static const char *const names[] = {"uinput", "input/uinput", NULL};
device = resolveDeviceName(names, 0, "uinput");
}
if (device) {
if ((uinput->fileDescriptor = openCharacterDevice(device, O_RDWR, MISC_MAJOR, 223)) != -1) {
struct uinput_user_dev description;
memset(&description, 0, sizeof(description));
snprintf(description.name, sizeof(description.name),
"%s %s %s",
PACKAGE_NAME, PACKAGE_VERSION, name);
if (write(uinput->fileDescriptor, &description, sizeof(description)) != -1) {
#ifdef UI_SET_PHYS
{
extern const char *__progname;
char topology[0X40];
snprintf(topology, sizeof(topology),
"pid-%"PRIu32"/%s/%d",
(uint32_t)getpid(), __progname, uinput->fileDescriptor);
if (ioctl(uinput->fileDescriptor, UI_SET_PHYS, topology) == -1) {
logSystemError("ioctl[UI_SET_PHYS]");
}
}
#endif /* UI_SET_PHYS */
logMessage(LOG_DEBUG, "uinput opened: %s: %s fd=%d",
device, description.name, uinput->fileDescriptor);
return uinput;
} else {
logSystemError("write(struct uinput_user_dev)");
}
close(uinput->fileDescriptor);
} else {
logMessage(LOG_DEBUG, "cannot open uinput device: %s: %s", device, strerror(errno));
}
}
free(uinput);
uinput = NULL;
} else {
logMallocError();
}
#else /* HAVE_LINUX_UINPUT_H */
logMessage(LOG_WARNING, "uinput support not available");
errno = ENOSYS;
#endif /* HAVE_LINUX_UINPUT_H */
return NULL;
}
void
destroyUinputObject (UinputObject *uinput) {
#ifdef HAVE_LINUX_UINPUT_H
releasePressedKeys(uinput);
close(uinput->fileDescriptor);
free(uinput);
#endif /* HAVE_LINUX_UINPUT_H */
}
int
getUinputFileDescriptor (UinputObject *uinput) {
return uinput->fileDescriptor;
}
int
createUinputDevice (UinputObject *uinput) {
#ifdef HAVE_LINUX_UINPUT_H
if (ioctl(uinput->fileDescriptor, UI_DEV_CREATE) != -1) return 1;
logSystemError("ioctl[UI_DEV_CREATE]");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
int
enableUinputEventType (UinputObject *uinput, int type) {
#ifdef HAVE_LINUX_UINPUT_H
if (ioctl(uinput->fileDescriptor, UI_SET_EVBIT, type) != -1) return 1;
logSystemError("ioctl[UI_SET_EVBIT]");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
int
writeInputEvent (UinputObject *uinput, uint16_t type, uint16_t code, int32_t value) {
#ifdef HAVE_LINUX_UINPUT_H
struct timeval now;
gettimeofday(&now, NULL);
struct input_event event = {
.input_event_sec = now.tv_sec,
.input_event_usec = now.tv_usec,
.type = type,
.code = code,
.value = value,
};
if (write(uinput->fileDescriptor, &event, sizeof(event)) != -1) return 1;
logSystemError("write(struct input_event)");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
static int
writeSynReport (UinputObject *uinput) {
return writeInputEvent(uinput, EV_SYN, SYN_REPORT, 0);
}
int
enableUinputKey (UinputObject *uinput, int key) {
#ifdef HAVE_LINUX_UINPUT_H
if (ioctl(uinput->fileDescriptor, UI_SET_KEYBIT, key) != -1) return 1;
logSystemError("ioctl[UI_SET_KEYBIT]");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
int
writeKeyEvent (UinputObject *uinput, int key, int press) {
#ifdef HAVE_LINUX_UINPUT_H
if (writeInputEvent(uinput, EV_KEY, key, press)) {
if (press) {
BITMASK_SET(uinput->pressedKeys, key);
} else {
BITMASK_CLEAR(uinput->pressedKeys, key);
}
if (writeSynReport(uinput)) {
return 1;
}
}
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
int
releasePressedKeys (UinputObject *uinput) {
#ifdef HAVE_LINUX_UINPUT_H
unsigned int key;
for (key=0; key<=KEY_MAX; key+=1) {
if (BITMASK_TEST(uinput->pressedKeys, key)) {
if (!writeKeyEvent(uinput, key, 0)) return 0;
BITMASK_CLEAR(uinput->pressedKeys, key);
}
}
#endif /* HAVE_LINUX_UINPUT_H */
return 1;
}
int
writeRepeatDelay (UinputObject *uinput, int delay) {
if (writeInputEvent(uinput, EV_REP, REP_DELAY, delay)) {
if (writeSynReport(uinput)) {
return 1;
}
}
return 0;
}
int
writeRepeatPeriod (UinputObject *uinput, int period) {
if (writeInputEvent(uinput, EV_REP, REP_PERIOD, period)) {
if (writeSynReport(uinput)) {
return 1;
}
}
return 0;
}
#ifdef HAVE_LINUX_INPUT_H
static int
enableKeyboardKeys (UinputObject *uinput) {
const LinuxKeyMapDescriptor *map = linuxKeyMapDescriptors;
const LinuxKeyMapDescriptor *end = map + linuxKeyMapCount;
BITMASK(enabledKeys, KEY_MAX+1, char);
if (!enableUinputEventType(uinput, EV_KEY)) return 0;
BITMASK_ZERO(enabledKeys);
while (map < end) {
unsigned int code;
for (code=0; code<map->count; code+=1) {
LinuxKeyCode key = map->keys[code];
if (key) {
if (!BITMASK_TEST(enabledKeys, key)) {
BITMASK_SET(enabledKeys, key);
if (!enableUinputKey(uinput, key)) return 0;
}
}
}
map += 1;
}
return 1;
}
#endif /* HAVE_LINUX_INPUT_H */
int
enableUinputSound (UinputObject *uinput, int sound) {
#ifdef HAVE_LINUX_UINPUT_H
if (ioctl(uinput->fileDescriptor, UI_SET_SNDBIT, sound) != -1) return 1;
logSystemError("ioctl[UI_SET_SNDBIT]");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
int
enableUinputLed (UinputObject *uinput, int led) {
#ifdef HAVE_LINUX_UINPUT_H
if (ioctl(uinput->fileDescriptor, UI_SET_LEDBIT, led) != -1) return 1;
logSystemError("ioctl[UI_SET_LEDBIT]");
#endif /* HAVE_LINUX_UINPUT_H */
return 0;
}
UinputObject *
newUinputKeyboard (const char *name) {
#ifdef HAVE_LINUX_INPUT_H
UinputObject *uinput;
if ((uinput = newUinputObject(name))) {
if (enableKeyboardKeys(uinput)) {
if (enableUinputEventType(uinput, EV_REP)) {
if (createUinputDevice(uinput)) {
return uinput;
}
}
}
destroyUinputObject(uinput);
}
#endif /* HAVE_LINUX_INPUT_H */
return NULL;
}
struct InputEventMonitorStruct {
UinputObject *uinputObject;
int fileDescriptor;
AsyncHandle asyncHandle;
UinputObjectPreparer *prepareUinputObject;
InputEventHandler *handleInputEvent;
};
static void
closeInputEventMonitor (InputEventMonitor *monitor) {
close(monitor->fileDescriptor);
monitor->fileDescriptor = -1;
}
ASYNC_INPUT_CALLBACK(handleInterceptedInputEvent) {
InputEventMonitor *monitor = parameters->data;
static const char label[] = "input event monitor";
if (parameters->error) {
logMessage(LOG_DEBUG, "%s read error: fd=%d: %s",
label, monitor->fileDescriptor, strerror(parameters->error));
closeInputEventMonitor(monitor);
} else if (parameters->end) {
logMessage(LOG_DEBUG, "%s end-of-file: fd=%d",
label, monitor->fileDescriptor);
closeInputEventMonitor(monitor);
} else {
const struct input_event *event = parameters->buffer;
if (parameters->length >= sizeof(*event)) {
monitor->handleInputEvent(event);
return sizeof(*event);
}
}
return 0;
}
InputEventMonitor *
newInputEventMonitor (
const char *name,
UinputObjectPreparer *prepareUinputObject,
InputEventHandler *handleInputEvent
) {
InputEventMonitor *monitor;
if ((monitor = malloc(sizeof(*monitor)))) {
memset(monitor, 0, sizeof(*monitor));
monitor->prepareUinputObject = prepareUinputObject;
monitor->handleInputEvent = handleInputEvent;
if ((monitor->uinputObject = newUinputObject(name))) {
monitor->fileDescriptor = getUinputFileDescriptor(monitor->uinputObject);
if (prepareUinputObject(monitor->uinputObject)) {
if (createUinputDevice(monitor->uinputObject)) {
if (asyncReadFile(&monitor->asyncHandle, monitor->fileDescriptor,
sizeof(struct input_event),
handleInterceptedInputEvent, monitor)) {
logMessage(LOG_DEBUG, "input event monitor opened: fd=%d",
monitor->fileDescriptor);
return monitor;
}
}
}
destroyUinputObject(monitor->uinputObject);
}
free(monitor);
} else {
logMallocError();
}
return NULL;
}
void
destroyInputEventMonitor (InputEventMonitor *monitor) {
asyncCancelRequest(monitor->asyncHandle);
destroyUinputObject(monitor->uinputObject);
free(monitor);
}
void
initializeSystemObject (void) {
}