#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include "kdrive.h"
#include <linux/keyboard.h>
#include <linux/kd.h>
#define XK_PUBLISHING
#include <X11/keysym.h>
#include <termios.h>
#include <sys/ioctl.h>
extern int LinuxConsoleFd;
static const KeySym linux_to_x[256] = {
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
};
#ifdef XKB
static unsigned char at2lnx[] =
{
0x0,
0x01, 0x02,
0x03, 0x04,
0x05, 0x06,
0x07, 0x08,
0x09, 0x0a,
0x0b, 0x0c,
0x0d, 0x0e,
0x0f, 0x10,
0x11, 0x12,
0x13, 0x14,
0x15, 0x16,
0x17, 0x18,
0x19, 0x1a,
0x1b, 0x1c,
0x1d, 0x1e,
0x1f, 0x20,
0x21, 0x22,
0x23, 0x24,
0x25, 0x26,
0x27, 0x28,
0x29, 0x2a,
0x2b, 0x2c,
0x2d, 0x2e,
0x2f, 0x30,
0x31, 0x32,
0x33, 0x34,
0x35, 0x36,
0x37, 0x38,
0x39, 0x3a,
0x3b, 0x3c,
0x3d, 0x3e,
0x3f, 0x40,
0x41, 0x42,
0x43, 0x44,
0x45, 0x46,
0x47, 0x48,
0x49, 0x4a,
0x4b, 0x4c,
0x4d, 0x4e,
0x4f, 0x50,
0x51, 0x52,
0x53, 0x54,
0x00, 0x56,
0x57, 0x58,
0x66, 0x67,
0x68, 0x69,
0x5d, 0x6a,
0x6b, 0x6c,
0x6d, 0x6e,
0x6f, 0x60,
0x61, 0x77,
0x63, 0x62,
0x64, 0x65,
0x00, 0x00,
0x7A, 0x00,
0x7B, 0x00,
0x00, 0x00,
0x59, 0x78,
0x00, 0x00,
0x5A, 0x5B,
0x5C, 0x5D,
0x5E, 0x5F,
0x7C, 0x79,
0x00,
};
#define NUM_AT_KEYS (sizeof(at2lnx)/sizeof(at2lnx[0]))
#define LNX_KEY_INDEX(n) n < NUM_AT_KEYS ? at2lnx[n] : 0
#else
#define LNX_KEY_INDEX(n) n
#endif
static unsigned char tbl[KD_MAX_WIDTH] =
{
0,
1 << KG_SHIFT,
(1 << KG_ALTGR),
(1 << KG_ALTGR) | (1 << KG_SHIFT)
};
static void
readKernelMapping(KdKeyboardInfo *ki)
{
KeySym *k;
int i, j;
struct kbentry kbe;
int minKeyCode, maxKeyCode;
int row;
int fd;
if (!ki)
return;
fd = LinuxConsoleFd;
minKeyCode = NR_KEYS;
maxKeyCode = 0;
row = 0;
ki->keySyms.mapWidth = KD_MAX_WIDTH;
for (i = 0; i < NR_KEYS && row < KD_MAX_LENGTH; ++i)
{
kbe.kb_index = LNX_KEY_INDEX(i);
k = ki->keySyms.map + row * ki->keySyms.mapWidth;
for (j = 0; j < ki->keySyms.mapWidth; ++j)
{
unsigned short kval;
k[j] = NoSymbol;
kbe.kb_table = tbl[j];
kbe.kb_value = 0;
if (ioctl(fd, KDGKBENT, &kbe))
continue;
kval = KVAL(kbe.kb_value);
switch (KTYP(kbe.kb_value))
{
case KT_LATIN:
case KT_LETTER:
k[j] = linux_to_x[kval];
break;
case KT_FN:
if (kval <= 19)
k[j] = XK_F1 + kval;
else switch (kbe.kb_value)
{
case K_FIND:
k[j] = XK_Home;
break;
case K_INSERT:
k[j] = XK_Insert;
break;
case K_REMOVE:
k[j] = XK_Delete;
break;
case K_SELECT:
k[j] = XK_End;
break;
case K_PGUP:
k[j] = XK_Prior;
break;
case K_PGDN:
k[j] = XK_Next;
break;
case K_HELP:
k[j] = XK_Help;
break;
case K_DO:
k[j] = XK_Execute;
break;
case K_PAUSE:
k[j] = XK_Pause;
break;
case K_MACRO:
k[j] = XK_Menu;
break;
default:
break;
}
break;
case KT_SPEC:
switch (kbe.kb_value)
{
case K_ENTER:
k[j] = XK_Return;
break;
case K_BREAK:
k[j] = XK_Break;
break;
case K_CAPS:
k[j] = XK_Caps_Lock;
break;
case K_NUM:
k[j] = XK_Num_Lock;
break;
case K_HOLD:
k[j] = XK_Scroll_Lock;
break;
case K_COMPOSE:
k[j] = XK_Multi_key;
break;
default:
break;
}
break;
case KT_PAD:
switch (kbe.kb_value)
{
case K_PPLUS:
k[j] = XK_KP_Add;
break;
case K_PMINUS:
k[j] = XK_KP_Subtract;
break;
case K_PSTAR:
k[j] = XK_KP_Multiply;
break;
case K_PSLASH:
k[j] = XK_KP_Divide;
break;
case K_PENTER:
k[j] = XK_KP_Enter;
break;
case K_PCOMMA:
k[j] = XK_KP_Separator;
break;
case K_PDOT:
k[j] = XK_KP_Decimal;
break;
case K_PPLUSMINUS:
k[j] = XK_KP_Subtract;
break;
default:
if (kval <= 9)
k[j] = XK_KP_0 + kval;
break;
}
break;
case KT_DEAD:
switch (kbe.kb_value)
{
case K_DGRAVE:
k[j] = XK_dead_grave;
break;
case K_DACUTE:
k[j] = XK_dead_acute;
break;
case K_DCIRCM:
k[j] = XK_dead_circumflex;
break;
case K_DTILDE:
k[j] = XK_dead_tilde;
break;
case K_DDIERE:
k[j] = XK_dead_diaeresis;
break;
}
break;
case KT_CUR:
switch (kbe.kb_value)
{
case K_DOWN:
k[j] = XK_Down;
break;
case K_LEFT:
k[j] = XK_Left;
break;
case K_RIGHT:
k[j] = XK_Right;
break;
case K_UP:
k[j] = XK_Up;
break;
}
break;
case KT_SHIFT:
switch (kbe.kb_value)
{
case K_ALTGR:
k[j] = XK_Mode_switch;
break;
case K_ALT:
k[j] = (kbe.kb_index == 0x64 ?
XK_Alt_R : XK_Alt_L);
break;
case K_CTRL:
k[j] = (kbe.kb_index == 0x61 ?
XK_Control_R : XK_Control_L);
break;
case K_CTRLL:
k[j] = XK_Control_L;
break;
case K_CTRLR:
k[j] = XK_Control_R;
break;
case K_SHIFT:
k[j] = (kbe.kb_index == 0x36 ?
XK_Shift_R : XK_Shift_L);
break;
case K_SHIFTL:
k[j] = XK_Shift_L;
break;
case K_SHIFTR:
k[j] = XK_Shift_R;
break;
default:
break;
}
break;
case KT_ASCII:
break;
case KT_LOCK:
if (kbe.kb_value == K_SHIFTLOCK)
k[j] = XK_Shift_Lock;
break;
#ifdef KT_X
case KT_X:
if(kbe.kb_value == K_XMENU) k[j] = XK_Menu;
if(kbe.kb_value == K_XTELEPHONE) k[j] = XK_telephone;
break;
#endif
#ifdef KT_XF
case KT_XF:
k[j] = (kbe.kb_value & 0xFF) + 0x1008FF00;
break;
#endif
default:
break;
}
if (i < minKeyCode)
minKeyCode = i;
if (i > maxKeyCode)
maxKeyCode = i;
}
if (minKeyCode == NR_KEYS)
continue;
if (k[3] == k[2]) k[3] = NoSymbol;
if (k[2] == k[1]) k[2] = NoSymbol;
if (k[1] == k[0]) k[1] = NoSymbol;
if (k[0] == k[2] && k[1] == k[3]) k[2] = k[3] = NoSymbol;
if (k[3] == k[0] && k[2] == k[1] && k[2] == NoSymbol) k[3] =NoSymbol;
row++;
}
ki->minScanCode = minKeyCode;
ki->maxScanCode = maxKeyCode;
}
#ifdef XKB
#define KEY_Prefix0 96
#define KEY_Prefix1 97
#define KEY_Enter 28
#define KEY_LCtrl 29
#define KEY_Slash 53
#define KEY_KP_Multiply 55
#define KEY_Alt 56
#define KEY_F3 61
#define KEY_F4 62
#define KEY_F5 63
#define KEY_F6 64
#define KEY_F7 65
#define KEY_ScrollLock 70
#define KEY_KP_7 71
#define KEY_KP_8 72
#define KEY_KP_9 73
#define KEY_KP_Minus 74
#define KEY_KP_4 75
#define KEY_KP_5 76
#define KEY_KP_6 77
#define KEY_KP_Plus 78
#define KEY_KP_1 79
#define KEY_KP_2 80
#define KEY_KP_3 81
#define KEY_KP_0 82
#define KEY_KP_Decimal 83
#define KEY_Home 89
#define KEY_Up 90
#define KEY_PgUp 91
#define KEY_Left 92
#define KEY_Begin 93
#define KEY_Right 94
#define KEY_End 95
#define KEY_Down 96
#define KEY_PgDown 97
#define KEY_Insert 98
#define KEY_Delete 99
#define KEY_KP_Enter 100
#define KEY_RCtrl 101
#define KEY_Pause 102
#define KEY_Print 103
#define KEY_KP_Divide 104
#define KEY_AltLang 105
#define KEY_Break 106
#define KEY_LMeta 107
#define KEY_RMeta 108
#define KEY_Menu 109
#define KEY_F13 110
#define KEY_F14 111
#define KEY_F15 112
#define KEY_F16 113
#define KEY_F17 114
#define KEY_KP_DEC 115
#endif
static void
LinuxKeyboardRead (int fd, void *closure)
{
unsigned char buf[256], *b;
int n;
unsigned char prefix = 0, scancode = 0;
while ((n = read (fd, buf, sizeof (buf))) > 0) {
b = buf;
while (n--) {
#ifdef XKB
if (!noXkbExtension) {
if (!prefix && ((b[0] & 0x7f) == KEY_Prefix0))
{
prefix = KEY_Prefix0;
#ifdef DEBUG
ErrorF("Prefix0");
#endif
b++;
continue;
}
else if (!prefix && ((b[0] & 0x7f) == KEY_Prefix1))
{
prefix = KEY_Prefix1;
ErrorF("Prefix1");
b++;
continue;
}
scancode = b[0] & 0x7f;
switch (prefix) {
case KEY_Prefix0:
{
#ifdef DEBUG
ErrorF("Prefix0 scancode: 0x%02x\n", scancode);
#endif
switch (scancode) {
case KEY_KP_7:
scancode = KEY_Home; break;
case KEY_KP_8:
scancode = KEY_Up; break;
case KEY_KP_9:
scancode = KEY_PgUp; break;
case KEY_KP_4:
scancode = KEY_Left; break;
case KEY_KP_5:
scancode = KEY_Begin; break;
case KEY_KP_6:
scancode = KEY_Right; break;
case KEY_KP_1:
scancode = KEY_End; break;
case KEY_KP_2:
scancode = KEY_Down; break;
case KEY_KP_3:
scancode = KEY_PgDown; break;
case KEY_KP_0:
scancode = KEY_Insert; break;
case KEY_KP_Decimal:
scancode = KEY_Delete; break;
case KEY_Enter:
scancode = KEY_KP_Enter; break;
case KEY_LCtrl:
scancode = KEY_RCtrl; break;
case KEY_KP_Multiply:
scancode = KEY_Print; break;
case KEY_Slash:
scancode = KEY_KP_Divide; break;
case KEY_Alt:
scancode = KEY_AltLang; break;
case KEY_ScrollLock:
scancode = KEY_Break; break;
case 0x5b:
scancode = KEY_LMeta; break;
case 0x5c:
scancode = KEY_RMeta; break;
case 0x5d:
scancode = KEY_Menu; break;
case KEY_F3:
scancode = KEY_F13; break;
case KEY_F4:
scancode = KEY_F14; break;
case KEY_F5:
scancode = KEY_F15; break;
case KEY_F6:
scancode = KEY_F16; break;
case KEY_F7:
scancode = KEY_F17; break;
case KEY_KP_Plus:
scancode = KEY_KP_DEC; break;
case 0x2A:
case 0x36:
b++;
prefix = 0;
continue;
default:
#ifdef DEBUG
ErrorF("Unreported Prefix0 scancode: 0x%02x\n",
scancode);
#endif
scancode += 0x78;
}
break;
}
case KEY_Prefix1:
{
#ifdef DEBUG
ErrorF("Prefix1 scancode: 0x%02x\n", scancode);
#endif
b++;
prefix = 0;
continue;
}
default:
case 0:
#ifdef DEBUG
ErrorF("Plain scancode: 0x%02x\n", scancode);
#endif
;
}
prefix = 0;
}
else
#endif
scancode = b[0] & 0x7f;
KdEnqueueKeyboardEvent (closure, scancode, b[0] & 0x80);
b++;
}
}
}
static int LinuxKbdTrans;
static struct termios LinuxTermios;
static Status
LinuxKeyboardEnable (KdKeyboardInfo *ki)
{
struct termios nTty;
unsigned char buf[256];
int n;
int fd;
if (!ki)
return !Success;
fd = LinuxConsoleFd;
ki->driverPrivate = (void *) fd;
ioctl (fd, KDGKBMODE, &LinuxKbdTrans);
tcgetattr (fd, &LinuxTermios);
#ifdef XKB
if (!noXkbExtension)
ioctl(fd, KDSKBMODE, K_RAW);
else
#else
ioctl(fd, KDSKBMODE, K_MEDIUMRAW);
#endif
nTty = LinuxTermios;
nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
nTty.c_oflag = 0;
nTty.c_cflag = CREAD | CS8;
nTty.c_lflag = 0;
nTty.c_cc[VTIME]=0;
nTty.c_cc[VMIN]=1;
cfsetispeed(&nTty, 9600);
cfsetospeed(&nTty, 9600);
tcsetattr(fd, TCSANOW, &nTty);
while ((n = read (fd, buf, sizeof (buf))) > 0)
;
KdRegisterFd (fd, LinuxKeyboardRead, ki);
return Success;
}
static void
LinuxKeyboardDisable (KdKeyboardInfo *ki)
{
int fd;
if (!ki)
return;
fd = (int) ki->driverPrivate;
KdUnregisterFd(ki, fd, FALSE);
ioctl(fd, KDSKBMODE, LinuxKbdTrans);
tcsetattr(fd, TCSANOW, &LinuxTermios);
}
static Status
LinuxKeyboardInit (KdKeyboardInfo *ki)
{
if (!ki)
return !Success;
if (ki->path)
xfree(ki->path);
ki->path = KdSaveString("console");
if (ki->name)
xfree(ki->name);
ki->name = KdSaveString("Linux console keyboard");
readKernelMapping (ki);
return Success;
}
static void
LinuxKeyboardLeds (KdKeyboardInfo *ki, int leds)
{
if (!ki)
return;
ioctl ((int)ki->driverPrivate, KDSETLED, leds & 7);
}
KdKeyboardDriver LinuxKeyboardDriver = {
"keyboard",
.Init = LinuxKeyboardInit,
.Enable = LinuxKeyboardEnable,
.Leds = LinuxKeyboardLeds,
.Disable = LinuxKeyboardDisable,
};