/* Copyright (c) 2003 by Juliusz Chroboczek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define NEED_EVENTS #include "kdrive.h" #include "kkeymap.h" #include #include #include #include #include extern int LinuxConsoleFd; #define NEVENT 16 static int LinuxEventType[NEVENT]; static int eventFd[NEVENT]; static int touching[NEVENT]; static int numFd = 0; #define countof(a) (sizeof(a)/sizeof((a)[0])) /* Copied from keyboard.c */ static const KeySym alpha_to_x[] = { NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_BackSpace, XK_Tab, XK_Linefeed, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_Escape, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_space, XK_exclam, XK_quotedbl, XK_numbersign, XK_dollar, XK_percent, XK_ampersand, XK_apostrophe, XK_parenleft, XK_parenright, XK_asterisk, XK_plus, XK_comma, XK_minus, XK_period, XK_slash, XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, XK_colon, XK_semicolon, XK_less, XK_equal, XK_greater, XK_question, XK_at, XK_A, XK_B, XK_C, XK_D, XK_E, XK_F, XK_G, XK_H, XK_I, XK_J, XK_K, XK_L, XK_M, XK_N, XK_O, XK_P, XK_Q, XK_R, XK_S, XK_T, XK_U, XK_V, XK_W, XK_X, XK_Y, XK_Z, XK_bracketleft, XK_backslash, XK_bracketright, XK_asciicircum, XK_underscore, XK_grave, XK_a, XK_b, XK_c, XK_d, XK_e, XK_f, XK_g, XK_h, XK_i, XK_j, XK_k, XK_l, XK_m, XK_n, XK_o, XK_p, XK_q, XK_r, XK_s, XK_t, XK_u, XK_v, XK_w, XK_x, XK_y, XK_z, XK_braceleft, XK_bar, XK_braceright, XK_asciitilde, XK_BackSpace, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_nobreakspace,XK_exclamdown, XK_cent, XK_sterling, XK_currency, XK_yen, XK_brokenbar, XK_section, XK_diaeresis, XK_copyright, XK_ordfeminine, XK_guillemotleft, XK_notsign, XK_hyphen, XK_registered, XK_macron, XK_degree, XK_plusminus, XK_twosuperior, XK_threesuperior, XK_acute, XK_mu, XK_paragraph, XK_periodcentered, XK_cedilla, XK_onesuperior, XK_masculine, XK_guillemotright, XK_onequarter, XK_onehalf, XK_threequarters,XK_questiondown, XK_Agrave, XK_Aacute, XK_Acircumflex, XK_Atilde, XK_Adiaeresis, XK_Aring, XK_AE, XK_Ccedilla, XK_Egrave, XK_Eacute, XK_Ecircumflex, XK_Ediaeresis, XK_Igrave, XK_Iacute, XK_Icircumflex, XK_Idiaeresis, XK_ETH, XK_Ntilde, XK_Ograve, XK_Oacute, XK_Ocircumflex, XK_Otilde, XK_Odiaeresis, XK_multiply, XK_Ooblique, XK_Ugrave, XK_Uacute, XK_Ucircumflex, XK_Udiaeresis, XK_Yacute, XK_THORN, XK_ssharp, XK_agrave, XK_aacute, XK_acircumflex, XK_atilde, XK_adiaeresis, XK_aring, XK_ae, XK_ccedilla, XK_egrave, XK_eacute, XK_ecircumflex, XK_ediaeresis, XK_igrave, XK_iacute, XK_icircumflex, XK_idiaeresis, XK_eth, XK_ntilde, XK_ograve, XK_oacute, XK_ocircumflex, XK_otilde, XK_odiaeresis, XK_division, XK_oslash, XK_ugrave, XK_uacute, XK_ucircumflex, XK_udiaeresis, XK_yacute, XK_thorn, XK_ydiaeresis }; static const KeySym spec_to_x[] = { NoSymbol, XK_Return, NoSymbol, NoSymbol, NoSymbol, XK_Break, NoSymbol, XK_Caps_Lock, XK_Num_Lock, XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_Multi_key, }; static const KeySym shift_to_x[] = { XK_Shift_L, XK_Mode_switch, XK_Control_L, XK_Alt_L, XK_Shift_L, XK_Shift_R, XK_Control_L, XK_Control_R, }; static const KeySym dead_to_x[] = { XK_dead_grave, XK_dead_acute, XK_dead_circumflex, XK_dead_tilde, XK_dead_diaeresis, XK_dead_cedilla, }; static const KeySym cur_to_x[] = { XK_Down, XK_Left, XK_Right, XK_Up, }; static const KeySym kpad_to_x[] = { XK_KP_0, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_7, XK_KP_8, XK_KP_9, XK_KP_Add, XK_KP_Subtract, XK_KP_Multiply, XK_KP_Divide, XK_KP_Enter, XK_KP_Decimal, XK_KP_Decimal, NoSymbol, XK_KP_Add, XK_KP_Subtract, XK_plusminus, XK_parenleft, XK_parenright, }; static const KeySym fn_to_x[] = { XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, XK_F9, XK_F10, XK_F11, XK_F12, XK_F13, XK_F14, XK_F15, XK_F16, XK_F17, XK_F18, XK_F19, XK_F20, XK_Home, XK_Insert, XK_Delete, XK_End, XK_Prior, XK_Next, XK_Menu, XK_Help, XK_Execute, XK_Pause, XK_F21, XK_F22, XK_F23, XK_F24, XK_F25, XK_F26, XK_F27, XK_F28, XK_F29, XK_F30, }; static unsigned char modifiers[KD_MAX_WIDTH] = { 0, 1 << KG_SHIFT, 1 << KG_ALTGR, (1 << KG_ALTGR) | (1 << KG_SHIFT) }; static void readKernelMapping() { KeySym *k; struct kbentry kbe; int min, max, rc, i, j; min = NR_KEYS; max = 0; k = kdKeymap; for(i = 0; i < NR_KEYS; i++) { if(max - min + 1 >= KD_MAX_LENGTH) break; kbe.kb_index = i; for(j = 0; j < KD_MAX_WIDTH; j++) { unsigned short kval; k[j] = NoSymbol; kbe.kb_table = modifiers[j]; rc = ioctl(LinuxConsoleFd, KDGKBENT, &kbe); if(rc < 0) continue; kval = KVAL(kbe.kb_value); switch(KTYP(kbe.kb_value)) { case KT_LATIN: case KT_LETTER: if(kval < countof(alpha_to_x)) k[j] = alpha_to_x[kval]; break; case KT_FN: if(kval < countof(fn_to_x)) k[j] = fn_to_x[kval]; break; case KT_SPEC: if(kval < countof(spec_to_x)) k[j] = spec_to_x[kval]; break; case KT_SHIFT: if(kval < countof(spec_to_x)) k[j] = shift_to_x[kval]; break; case KT_DEAD: if(kval < countof(dead_to_x)) k[j] = dead_to_x[kval]; break; case KT_CUR: if(kval < countof(spec_to_x)) k[j] = cur_to_x[kval]; break; case KT_PAD: if(kval < countof(kpad_to_x)) k[j] = kpad_to_x[kval]; break; default: break; } } if(k[3] == k[2]) k[3] = NoSymbol; if(k[2] == k[1]) k[2] = NoSymbol; if(k[1] == k[0]) k[1] = NoSymbol; k += KD_MAX_WIDTH; if(i < min) min = i; if(i > max) max = i; } kdMinScanCode = min; kdMaxScanCode = max; } void LinuxEventLoad(void) { readKernelMapping(); } static int mouseState = 0; static int lastX = -1, lastY = -1; void LinuxEventRead (int fd, void *closure) { struct input_event event; int n, i, flags, rx, ry; n = read(fd, &event, sizeof(event)); if(n != sizeof(event)) return; switch(event.type) { case EV_KEY: if(event.code < BTN_MISC) { KdEnqueueKeyboardEvent(event.code, event.value == 0); } else if(event.code >= BTN_MISC) { int button = -1; switch(event.code) { case BTN_LEFT: button = 0; break; case BTN_MIDDLE: button = 1; break; case BTN_RIGHT: button = 2; break; case BTN_SIDE: button = 3; break; case BTN_EXTRA: button = 4; break; case BTN_0: button = 3; break; case BTN_3: button = 4; break; } if(button >= 0 || button <= 4) { if(event.value) mouseState |= (1 << button); else mouseState &= ~(1 << button); KdEnqueueMouseEvent(kdMouseInfo, KD_MOUSE_DELTA | mouseState, 0, 0); } } break; #define COMBINE(a, b) (((a) << 16) | (b)) case EV_REL: case EV_ABS: switch(COMBINE(event.type, event.code)) { case COMBINE(EV_ABS, ABS_X): if(lastX < 0) lastX = event.value; rx = (event.value - lastX + 4) / 8; ry = 0; lastX = event.value; break; case COMBINE(EV_ABS, ABS_Y): if(lastY < 0) lastY = event.value; rx = 0; ry = (event.value - lastY + 4) / 8; lastY = event.value; break; case COMBINE(EV_ABS, ABS_PRESSURE): if(event.value >= 30) { if(touching[fd] <= 0) touching[fd] = 1; } else if(event.value < 25) { touching[fd] = 0; lastX = lastY = -1; } rx = ry = 0; break; case COMBINE(EV_REL, REL_X): rx = event.value; ry = 0; break; case COMBINE(EV_REL, REL_Y): rx = 0; ry = event.value; break; default: rx = ry = 0; break; } if(rx != 0 || ry != 0) { if(touching[fd] >= 1 && touching[fd] < 3) touching[fd]++; else if(touching[fd] < 0 || touching[fd] >= 3) KdEnqueueMouseEvent(kdMouseInfo, KD_MOUSE_DELTA | mouseState, rx, ry); } } } int LinuxEventInit(void) { int i, n, fd, type; char buf[32]; for(i = 0; i < NEVENT; i++) { if(numFd > NEVENT) break; n = snprintf(buf, 32, "/dev/input/event%d", i); if(n < 0 || n >= 32) continue; fd = open(buf, O_RDONLY); if(fd < 0) continue; LinuxEventType[numFd] = KdAllocInputType(); n = KdRegisterFd(LinuxEventType[numFd], fd, LinuxEventRead, 0); eventFd[numFd] = fd; touching[numFd] = -1; numFd ++; } if(numFd == 0) FatalError("Couldn't open any input devices.\n"); return 1; } void LinuxEventFini(void) { int i; for(i = 0; i < numFd; i++) { KdUnregisterFds(LinuxEventType[i], TRUE); } numFd = 0; } void LinuxEventLeds(int leds) { ioctl(LinuxConsoleFd, KDSETLED, leds & 7); } void LinuxEventBell(int volume, int pitch, int duration) { if (volume && pitch) { ioctl(LinuxConsoleFd, KDMKTONE, ((1193190 / pitch) & 0xffff) | (((unsigned long)duration * volume / 50) << 16)); } } KdKeyboardFuncs LinuxKeyboardFuncs = { LinuxEventLoad, LinuxEventInit, LinuxEventLeds, LinuxEventBell, LinuxEventFini, 3, }; int DummyInit(void) {} void DummyFini(void) {} KdMouseFuncs LinuxMouseFuncs = { DummyInit, DummyFini, };