2 * Copyright (c) 2000 Greg Haerr <greg@censoft.com>
4 * Microwindows /dev/tty console scancode keyboard driver for Linux
10 #include <sys/types.h>
14 #include <sys/ioctl.h>
15 #include <linux/keyboard.h>
21 #define KEYBOARD "/dev/tty" /* console kbd to open*/
23 static int TTY_Open(KBDDEVICE *pkd);
24 static void TTY_Close(void);
25 static void TTY_GetModifierInfo(MWKEYMOD *modifiers, MWKEYMOD *curmodifiers);
26 static int TTY_Read(MWKEY *kbuf, MWKEYMOD *modifiers, MWSCANCODE *scancode);
39 static int fd; /* file descriptor for keyboard */
40 static struct termios old; /* original terminal modes */
41 static int old_kbd_mode;
42 static unsigned char key_state[MWKEY_LAST]; /* FIXME - make sparse array */
43 static MWKEYMOD key_modstate;
45 /* kernel unicode tables per shiftstate and scancode*/
46 #define NUM_VGAKEYMAPS (1<<KG_CAPSSHIFT) /* kernel key maps*/
47 static unsigned short os_keymap[NUM_VGAKEYMAPS][NR_KEYS];
49 /* PC scancode -> Microwindows key value mapping for non-Linux kernel values*/
50 static MWKEY keymap[128] = {
51 MWKEY_UNKNOWN, MWKEY_ESCAPE, '1', '2', '3', /* 0*/
52 '4', '5', '6', '7', '8', /* 5*/
53 '9', '0', '-', '=', MWKEY_BACKSPACE, /* 10*/
54 MWKEY_TAB, 'q', 'w', 'e', 'r', /* 15*/
55 't', 'y', 'u', 'i', 'o', /* 20*/
56 'o', '[', ']', MWKEY_ENTER, MWKEY_LCTRL, /* 25*/
57 'a', 's', 'd', 'f', 'g', /* 30*/
58 'h', 'j', 'k', 'l', ';', /* 35*/
59 '\'', '`', MWKEY_LSHIFT, '\\', 'z', /* 40*/
60 'x', 'c', 'v', 'b', 'n', /* 45*/
61 'm', ',', '.', '/', MWKEY_RSHIFT, /* 50*/
62 MWKEY_KP_MULTIPLY, MWKEY_LALT, ' ', MWKEY_CAPSLOCK, MWKEY_F1, /* 55*/
63 MWKEY_F2, MWKEY_F3, MWKEY_F4, MWKEY_F5, MWKEY_F6, /* 60*/
64 MWKEY_F7, MWKEY_F8, MWKEY_F9, MWKEY_F10, MWKEY_NUMLOCK, /* 65*/
65 MWKEY_SCROLLOCK, MWKEY_KP7, MWKEY_KP8, MWKEY_KP9, MWKEY_KP_MINUS, /* 70*/
66 MWKEY_KP4, MWKEY_KP5, MWKEY_KP6, MWKEY_KP_PLUS, MWKEY_KP1, /* 75*/
67 MWKEY_KP2, MWKEY_KP3, MWKEY_KP0, MWKEY_KP_PERIOD, MWKEY_UNKNOWN, /* 80*/
68 MWKEY_UNKNOWN, MWKEY_UNKNOWN, MWKEY_F11, MWKEY_F12, MWKEY_UNKNOWN, /* 85*/
69 MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN, /* 90*/
70 MWKEY_UNKNOWN, MWKEY_KP_ENTER, MWKEY_RCTRL, MWKEY_KP_DIVIDE,MWKEY_PRINT,/* 95*/
71 MWKEY_RALT, MWKEY_BREAK, MWKEY_HOME, MWKEY_UP, MWKEY_PAGEUP, /* 100*/
72 MWKEY_LEFT, MWKEY_RIGHT, MWKEY_END, MWKEY_DOWN, MWKEY_PAGEDOWN, /* 105*/
73 MWKEY_INSERT, MWKEY_DELETE, MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN, /* 110*/
74 MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_PAUSE, /* 115*/
75 MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN,MWKEY_UNKNOWN, /* 120*/
76 MWKEY_LMETA, MWKEY_RMETA, MWKEY_MENU /* 125*/
79 static MWBOOL UpdateKeyState(int pressed, MWKEY mwkey);
80 static void UpdateLEDState(MWKEYMOD modstate);
81 static MWKEY TranslateScancode(int scancode, MWKEYMOD modstate);
82 static void LoadKernelKeymaps(int fd);
83 static MWBOOL switch_vt(unsigned short which);
87 * This is real simple, we just use a special file handle
88 * that allows non-blocking I/O, and put the terminal into
92 TTY_Open(KBDDEVICE *pkd)
100 /* Open "CONSOLE" or /dev/tty device*/
101 if(!(env = getenv("CONSOLE")))
102 fd = open(KEYBOARD, O_NONBLOCK);
104 fd = open(env, O_NONBLOCK);
108 /* Save previous settings*/
109 if (ioctl(fd, KDGKBMODE, &old_kbd_mode) < 0) {
113 if (tcgetattr(fd, &old) < 0)
116 /* Set medium-raw keyboard mode */
118 /* ISIG and BRKINT must be set otherwise '2' is ^C (scancode 3)!!*/
119 new.c_lflag &= ~(ICANON | ECHO | ISIG);
120 new.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON
125 if (tcsetattr(fd, TCSAFLUSH, &new) < 0) {
129 if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) < 0) {
134 /* Load OS keymappings*/
135 LoadKernelKeymaps(fd);
137 /* Initialize keyboard state*/
138 key_modstate = MWKMOD_NONE;
139 for (i=0; i<MWKEY_LAST; ++i)
140 key_state[i] = RELEASED;
142 /* preset CAPSLOCK and NUMLOCK from startup LED state*/
143 if (ioctl(fd, KDGETLED, &ledstate) == 0) {
144 if (ledstate & LED_CAP) {
145 key_modstate |= MWKMOD_CAPS;
146 key_state[MWKEY_CAPSLOCK] = PRESSED;
148 if (ledstate & LED_NUM) {
149 key_modstate |= MWKMOD_NUM;
150 key_state[MWKEY_NUMLOCK] = PRESSED;
153 UpdateLEDState(key_modstate);
164 * Close the keyboard.
165 * This resets the terminal modes.
170 int ledstate = 0x80000000L;
173 /* revert LEDs to follow key modifiers*/
174 if (ioctl(fd, KDSETLED, ledstate) < 0)
177 /* reset terminal mode*/
178 if (ioctl(fd, KDSKBMODE, old_kbd_mode) < 0)
180 tcsetattr(fd, TCSAFLUSH, &old);
188 * Return the possible modifiers and current modifiers for the keyboard.
191 TTY_GetModifierInfo(MWKEYMOD *modifiers, MWKEYMOD *curmodifiers)
194 *modifiers = MWKMOD_CTRL | MWKMOD_SHIFT | MWKMOD_ALT |
195 MWKMOD_META | MWKMOD_ALTGR | MWKMOD_CAPS | MWKMOD_NUM;
197 *curmodifiers = key_modstate;
201 * This reads one keystroke from the keyboard, and the current state of
202 * the modifier keys (ALT, SHIFT, etc). Returns -1 on error, 0 if no data
203 * is ready, 1 on a keypress, and 2 on keyrelease.
204 * This is a non-blocking call.
207 TTY_Read(MWKEY *kbuf, MWKEYMOD *modifiers, MWSCANCODE *pscancode)
209 int cc; /* characters read */
213 unsigned char buf[128];
215 cc = read(fd, buf, 1);
217 pressed = (*buf & 0x80) ? RELEASED: PRESSED;
218 scancode = *buf & 0x7f;
219 mwkey = keymap[scancode];
222 printf("scan %02x really: %08x\n", *buf&0x7F, *buf);
223 printf("mwkey: %02x (%c)\n", mwkey, mwkey);
226 /* Handle Alt-FN for vt switch */
240 if (key_modstate & MWKMOD_ALT) {
241 if (switch_vt(mwkey-MWKEY_F1+1)) {
242 mwkey = MWKEY_REDRAW;
246 /* Fall through to normal processing */
248 /* update internal key states*/
249 if (!UpdateKeyState(pressed, mwkey))
252 /* mwkey is 0 if only a modifier is hit */
253 if(mwkey != MWKEY_LCTRL &&
254 mwkey != MWKEY_RCTRL &&
255 mwkey != MWKEY_LALT &&
256 mwkey != MWKEY_RALT &&
257 mwkey != MWKEY_RSHIFT &&
258 mwkey != MWKEY_LSHIFT) {
259 /* translate scancode to key value*/
260 mwkey = TranslateScancode(scancode, key_modstate);
262 //printf("Modifier only\n");
266 /* XXX Hack to get scancodes to come out the same as
272 case 0x2 ... 0xe: /* 1 - BackSpace */
274 case 0xf ... 0x1b: /* TAB - ] */
277 case 0x3a: /* Caps-Lock */
278 case 0x1e ... 0x28: /* a - ' */
279 case 0x1c: /* Enter */
281 case 0x2a: /* LShift */
282 case 0x2c ... 0x35: /* z - / */
283 case 0x36: /* RShift */
285 case 0x1d: /* LCtrl */
286 //case 0x7d: /* LWin */
287 case 0x38: /* LAlt */
288 case 0x39: /* Space */
289 //case 0x64: /* RAlt */
290 //case 0x7e: /* RWin */
291 //case 0x7f: /* Win-PopupMenu */
292 //case 0x61: /* RCtrl */
294 //case 0x63: /* SysReq */
295 //case 0x46: /* Scroll Lock */
296 //case 0x77: /* Pause/Break */
300 case 0x6e: /* Insert */
303 case 0x66: /* Home */
304 case 0x68: /* Page-Up */
308 case 0x6f: /* Delete */
310 case 0x6d: /* Page-Down */
314 case 0x67: /* Up arrow */
315 case 0x69: /* Left arrow */
319 case 0x6a: /* Right arrow */
320 case 0x6c: /* Down arrow */
330 *modifiers = key_modstate;
331 *pscancode = scancode;
334 printf("Returning: mwkey: 0x%04x, mods: 0x%x,
335 sc:0x%04x\n\n", *kbuf, *modifiers, *pscancode);
337 return pressed ? 1 : 2;
340 if ((cc < 0) && (errno != EINTR) && (errno != EAGAIN))
345 /* Update the internal keyboard state, return TRUE if changed*/
347 UpdateKeyState(int pressed, MWKEY mwkey)
349 MWKEYMOD modstate = key_modstate;
351 //printf("UpdateKeyState %02x %02x\n", pressed, mwkey);
352 if (pressed == PRESSED) {
356 /* change state on release because of auto-repeat*/
359 modstate |= MWKMOD_LCTRL;
362 modstate |= MWKMOD_RCTRL;
365 modstate |= MWKMOD_LSHIFT;
368 modstate |= MWKMOD_RSHIFT;
371 modstate |= MWKMOD_LALT;
374 modstate |= MWKMOD_RALT;
377 modstate |= MWKMOD_LMETA;
380 modstate |= MWKMOD_RMETA;
383 modstate |= MWKMOD_ALTGR;
391 key_modstate ^= MWKMOD_NUM;
392 key_state[MWKEY_NUMLOCK] ^= PRESSED;
393 UpdateLEDState(key_modstate);
396 key_modstate ^= MWKMOD_CAPS;
397 key_state[MWKEY_CAPSLOCK] ^= PRESSED;
398 UpdateLEDState(key_modstate);
401 modstate &= ~MWKMOD_LCTRL;
404 modstate &= ~MWKMOD_RCTRL;
407 modstate &= ~MWKMOD_LSHIFT;
410 modstate &= ~MWKMOD_RSHIFT;
413 modstate &= ~MWKMOD_LALT;
416 modstate &= ~MWKMOD_RALT;
419 modstate &= ~MWKMOD_LMETA;
422 modstate &= ~MWKMOD_RMETA;
425 modstate &= ~MWKMOD_ALTGR;
433 /* Drop events that don't change state */
434 if (key_state[mwkey] == pressed)
437 /* Update internal keyboard state */
438 key_state[mwkey] = (unsigned char)pressed;
439 key_modstate = modstate;
444 UpdateLEDState(MWKEYMOD modstate)
448 if (modstate & MWKMOD_CAPS)
450 if (modstate & MWKMOD_NUM)
452 ioctl(fd, KDSETLED, ledstate);
455 /* translate a scancode and modifier state to an MWKEY*/
457 TranslateScancode(int scancode, MWKEYMOD modstate)
459 unsigned short mwkey = 0;
462 //printf("Translate: 0x%04x\n", scancode);
464 /* determine appropriate kernel table*/
465 if (modstate & MWKMOD_SHIFT)
466 map |= (1<<KG_SHIFT);
467 if (modstate & MWKMOD_CTRL)
469 if (modstate & MWKMOD_ALT)
471 if (modstate & MWKMOD_ALTGR)
472 map |= (1<<KG_ALTGR);
473 if (KTYP(os_keymap[map][scancode]) == KT_LETTER) {
474 if (modstate & MWKMOD_CAPS)
475 map |= (1<<KG_SHIFT);
477 if (KTYP(os_keymap[map][scancode]) == KT_PAD) {
478 if (modstate & MWKMOD_NUM) {
479 switch (keymap[scancode]) {
490 mwkey = keymap[scancode] - MWKEY_KP0 + '0';
492 case MWKEY_KP_PERIOD:
495 case MWKEY_KP_DIVIDE:
498 case MWKEY_KP_MULTIPLY:
510 case MWKEY_KP_EQUALS:
516 mwkey = KVAL(os_keymap[map][scancode]);
519 mwkey = keymap[scancode];
521 /* perform additional translations*/
524 mwkey = MWKEY_BACKSPACE;
530 case 0x1c: /* kernel maps print key to ctrl-\ */
536 /* printf("TranslateScancode %02x to mwkey %d\n", scancode, mwkey); */
540 /* load Linux keyboard mappings, used as first try for scancode conversion*/
542 LoadKernelKeymaps(int fd)
545 struct kbentry entry;
547 /* Load all the keysym mappings */
548 for (map=0; map<NUM_VGAKEYMAPS; ++map) {
549 memset(os_keymap[map], 0, NR_KEYS*sizeof(unsigned short));
550 for (i=0; i<NR_KEYS; ++i) {
551 entry.kb_table = map;
553 if (ioctl(fd, KDGKBENT, &entry) == 0) {
554 /* change K_ENTER to \r*/
555 if (entry.kb_value == K_ENTER)
556 entry.kb_value = K(KT_ASCII,13);
558 if ((KTYP(entry.kb_value) == KT_LATIN) ||
559 (KTYP(entry.kb_value) == KT_ASCII) ||
560 (KTYP(entry.kb_value) == KT_PAD) ||
561 (KTYP(entry.kb_value) == KT_LETTER)
563 os_keymap[map][i] = entry.kb_value;
569 /* Handle switching to another VC, returns when our VC is back */
571 switch_vt(unsigned short which)
573 struct vt_stat vtstate;
574 unsigned short current;
575 static unsigned short r[16], g[16], b[16];
577 /* Figure out whether or not we're switching to a new console */
578 if ((ioctl(fd, VT_GETSTATE, &vtstate) < 0) ||
579 (which == vtstate.v_active)) {
582 current = vtstate.v_active;
584 /* save palette, goto text mode*/
585 ioctl_getpalette(0, 16, r, g, b);
586 ioctl(fd, KDSETMODE, KD_TEXT);
588 /* New console, switch to it */
589 if (ioctl(fd, VT_ACTIVATE, which) == 0) {
590 /* Wait for our console to be activated again */
591 ioctl(fd, VT_WAITACTIVE, which);
592 while (ioctl(fd, VT_WAITACTIVE, current) < 0) {
593 if ((errno != EINTR) && (errno != EAGAIN)) {
594 /* Unknown VT error, cancel*/
601 /* Restore graphics mode and the contents of the screen */
602 ioctl(fd, KDSETMODE, KD_GRAPHICS);
603 ioctl_setpalette(0, 16, r, g, b);