/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Linux console keyboard module. * * By Marek Habersack, modified by George Foot. * * See readme.txt for copyright information. */ /* Make sure we don't #define our own KEY_ macros as linux has its * own. Instead, we use the __allegro_KEY_ constants in this file. */ #define ALLEGRO_NO_KEY_DEFINES #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintunix.h" #include "linalleg.h" #define PREFIX_I "al-ckey INFO: " #define PREFIX_W "al-ckey WARNING: " #define PREFIX_E "al-ckey ERROR: " static KEYBOARD_DRIVER keydrv_linux_console; /* list the available drivers */ _DRIVER_INFO _linux_keyboard_driver_list[] = { { KEYDRV_LINUX, &keydrv_linux_console, TRUE }, { 0, NULL, 0 } }; static int linux_key_init (void); static void linux_key_exit (void); static void linux_set_leds (int leds); static void linux_set_rate (int delay, int rate); static int linux_scancode_to_ascii (int scancode); static KEYBOARD_DRIVER keydrv_linux_console = { KEYDRV_LINUX, empty_string, empty_string, "Linux console keyboard", FALSE, linux_key_init, linux_key_exit, NULL, linux_set_leds, linux_set_rate, NULL, NULL, linux_scancode_to_ascii, NULL /* scancode_to_name */ }; static int update_keyboard (void); static void resume_keyboard (void); static void suspend_keyboard (void); static STD_DRIVER std_keyboard = { STD_KBD, update_keyboard, resume_keyboard, suspend_keyboard, -1 /* fd -- filled in later */ }; #include #include #include #include #include #include #include #include #include #include #include static int startup_kbmode; static int resume_count; static int main_pid; #define KB_MODIFIERS (__allegro_KB_SHIFT_FLAG | __allegro_KB_CTRL_FLAG | __allegro_KB_ALT_FLAG | \ __allegro_KB_LWIN_FLAG | __allegro_KB_RWIN_FLAG | __allegro_KB_MENU_FLAG) #define KB_LED_FLAGS (__allegro_KB_SCROLOCK_FLAG | __allegro_KB_NUMLOCK_FLAG | __allegro_KB_CAPSLOCK_FLAG) /* lookup table for converting kernel scancodes into Allegro format */ static unsigned char kernel_to_mycode[128] = { /* 0x00 */ 0, __allegro_KEY_ESC, __allegro_KEY_1, __allegro_KEY_2, /* 0x04 */ __allegro_KEY_3, __allegro_KEY_4, __allegro_KEY_5, __allegro_KEY_6, /* 0x08 */ __allegro_KEY_7, __allegro_KEY_8, __allegro_KEY_9, __allegro_KEY_0, /* 0x0C */ __allegro_KEY_MINUS, __allegro_KEY_EQUALS, __allegro_KEY_BACKSPACE, __allegro_KEY_TAB, /* 0x10 */ __allegro_KEY_Q, __allegro_KEY_W, __allegro_KEY_E, __allegro_KEY_R, /* 0x14 */ __allegro_KEY_T, __allegro_KEY_Y, __allegro_KEY_U, __allegro_KEY_I, /* 0x18 */ __allegro_KEY_O, __allegro_KEY_P, __allegro_KEY_OPENBRACE, __allegro_KEY_CLOSEBRACE, /* 0x1C */ __allegro_KEY_ENTER, __allegro_KEY_LCONTROL, __allegro_KEY_A, __allegro_KEY_S, /* 0x20 */ __allegro_KEY_D, __allegro_KEY_F, __allegro_KEY_G, __allegro_KEY_H, /* 0x24 */ __allegro_KEY_J, __allegro_KEY_K, __allegro_KEY_L, __allegro_KEY_COLON, /* 0x28 */ __allegro_KEY_QUOTE, __allegro_KEY_TILDE, __allegro_KEY_LSHIFT, __allegro_KEY_BACKSLASH, /* 0x2C */ __allegro_KEY_Z, __allegro_KEY_X, __allegro_KEY_C, __allegro_KEY_V, /* 0x30 */ __allegro_KEY_B, __allegro_KEY_N, __allegro_KEY_M, __allegro_KEY_COMMA, /* 0x34 */ __allegro_KEY_STOP, __allegro_KEY_SLASH, __allegro_KEY_RSHIFT, __allegro_KEY_ASTERISK, /* 0x38 */ __allegro_KEY_ALT, __allegro_KEY_SPACE, __allegro_KEY_CAPSLOCK, __allegro_KEY_F1, /* 0x3C */ __allegro_KEY_F2, __allegro_KEY_F3, __allegro_KEY_F4, __allegro_KEY_F5, /* 0x40 */ __allegro_KEY_F6, __allegro_KEY_F7, __allegro_KEY_F8, __allegro_KEY_F9, /* 0x44 */ __allegro_KEY_F10, __allegro_KEY_NUMLOCK, __allegro_KEY_SCRLOCK, __allegro_KEY_7_PAD, /* 0x48 */ __allegro_KEY_8_PAD, __allegro_KEY_9_PAD, __allegro_KEY_MINUS_PAD, __allegro_KEY_4_PAD, /* 0x4C */ __allegro_KEY_5_PAD, __allegro_KEY_6_PAD, __allegro_KEY_PLUS_PAD, __allegro_KEY_1_PAD, /* 0x50 */ __allegro_KEY_2_PAD, __allegro_KEY_3_PAD, __allegro_KEY_0_PAD, __allegro_KEY_DEL_PAD, /* 0x54 */ 0, 0, __allegro_KEY_BACKSLASH2, __allegro_KEY_F11, /* 0x58 */ __allegro_KEY_F12, 0, 0, 0, /* 0x5C */ 0, 0, 0, 0, /* 0x60 */ __allegro_KEY_ENTER_PAD, __allegro_KEY_RCONTROL, __allegro_KEY_SLASH_PAD, __allegro_KEY_PRTSCR, /* 0x64 */ __allegro_KEY_ALTGR, 0, __allegro_KEY_HOME, __allegro_KEY_UP, /* 0x68 */ __allegro_KEY_PGUP, __allegro_KEY_LEFT, __allegro_KEY_RIGHT, __allegro_KEY_END, /* 0x6C */ __allegro_KEY_DOWN, __allegro_KEY_PGDN, __allegro_KEY_INSERT, __allegro_KEY_DEL, /* 0x70 */ 0, 0, 0, 0, /* 0x74 */ 0, 0, 0, __allegro_KEY_PAUSE, /* 0x78 */ 0, 0, 0, 0, /* 0x7C */ 0, __allegro_KEY_LWIN, __allegro_KEY_RWIN, __allegro_KEY_MENU }; /* convert Allegro format scancodes into key_shifts flag bits */ static unsigned short modifier_table[__allegro_KEY_MAX - __allegro_KEY_MODIFIERS] = { __allegro_KB_SHIFT_FLAG, __allegro_KB_SHIFT_FLAG, __allegro_KB_CTRL_FLAG, __allegro_KB_CTRL_FLAG, __allegro_KB_ALT_FLAG, __allegro_KB_ALT_FLAG, __allegro_KB_LWIN_FLAG, __allegro_KB_RWIN_FLAG, __allegro_KB_MENU_FLAG, __allegro_KB_SCROLOCK_FLAG, __allegro_KB_NUMLOCK_FLAG, __allegro_KB_CAPSLOCK_FLAG }; #define NUM_PAD_KEYS 17 static char pad_asciis_numlock[NUM_PAD_KEYS] = "0123456789+-*/\r,."; static char pad_asciis_no_numlock[NUM_PAD_KEYS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, '+', '-', '*', '/', '\r', -1, -1 }; static void process_keyboard_data (unsigned char *buf, size_t bytes_read) { unsigned int ch; unsigned int code, mycode, press; int ascii; int map; int val; struct kbentry kbe; ASSERT(buf || bytes_read==0); /* Process all keys read from left to right */ for (ch = 0; ch < bytes_read; ch++) { /* Read kernel's fake scancode */ code = buf[ch] & 0x7f; press = !(buf[ch] & 0x80); /* Convert to Allegro's fake scancode */ mycode = kernel_to_mycode[code]; /* Process modifiers */ if (mycode >= __allegro_KEY_MODIFIERS) { int flag = modifier_table[mycode - __allegro_KEY_MODIFIERS]; if (press) { if (flag & KB_MODIFIERS) _key_shifts |= flag; else if ((flag & KB_LED_FLAGS) && key_led_flag) _key_shifts ^= flag; _handle_key_press (-1, mycode); } else { if (flag & KB_MODIFIERS) _key_shifts &= ~flag; _handle_key_release (mycode); } continue; } /* Releases only need the scancode */ if (!press) { _handle_key_release (mycode); continue; } /* Build kernel keymap number */ map = 0; if (key[__allegro_KEY_LSHIFT] || key[__allegro_KEY_RSHIFT]) map |= 1; if (key[__allegro_KEY_ALTGR]) map |= 2; if (key[__allegro_KEY_LCONTROL] || key[__allegro_KEY_RCONTROL]) map |= 4; if (key[__allegro_KEY_ALT]) map |= 8; /* Map scancode to type and value */ kbe.kb_table = map; kbe.kb_index = code; ioctl (std_keyboard.fd, KDGKBENT, &kbe); /* debugging */ /* TRACE(PREFIX_I "keypress: code=%3d mycode=%3d map=%2d kb_value=%04x\n", code, mycode, map, kbe.kb_value); */ /* Allegro wants Alt+key to return ASCII code zero */ if (key[__allegro_KEY_ALT]) ascii = 0; else /* Otherwise fill it in according to the key pressed */ switch (mycode) { case __allegro_KEY_BACKSPACE: ascii = 8; break; default: /* Most keys come here */ ascii = KVAL(kbe.kb_value); /* Handle invalid keymaps */ /* FIXME: Maybe we should just redo the * ioctl with keymap 0? */ if (kbe.kb_value == K_NOSUCHMAP) ascii = 0; } /* Finally do some things based on key type */ switch (KTYP(kbe.kb_value)) { case KT_CONS: if (!ioctl (__al_linux_console_fd, VT_ACTIVATE, KVAL(kbe.kb_value) + 1)) { _handle_key_press (-1, mycode); continue; } ascii = 0; break; case KT_LETTER: /* Apply capslock translation */ if (_key_shifts & __allegro_KB_CAPSLOCK_FLAG) ascii ^= 0x20; break; case KT_LATIN: case KT_ASCII: break; case KT_PAD: ascii = -1; val = KVAL(kbe.kb_value); if ((val >= 0) && (val < NUM_PAD_KEYS)) { if (_key_shifts & __allegro_KB_NUMLOCK_FLAG) ascii = pad_asciis_numlock[val]; else ascii = pad_asciis_no_numlock[val]; } break; case KT_SPEC: if (mycode == __allegro_KEY_ENTER) { ascii = '\r'; break; } case KT_FN: case KT_CUR: ascii = -1; break; default: /* Don't put anything in the buffer */ _handle_key_press (-1, mycode); } /* Replace -1 with the modifier state */ if (ascii == -1) ascii = _key_shifts & KB_MODIFIERS; /* three-finger salute for killing the program */ if (((mycode == __allegro_KEY_DEL) || (mycode == __allegro_KEY_END)) && (three_finger_flag) && (_key_shifts & __allegro_KB_CTRL_FLAG) && (_key_shifts & __allegro_KB_ALT_FLAG)) { TRACE(PREFIX_W "Three finger combo detected. SIGTERMing " "pid %d\n", main_pid); kill(main_pid, SIGTERM); } _handle_key_press (ascii, mycode); } } /* update_keyboard: * Read keys from keyboard, if there are any, and update all relevant * variables. * * We take full advantage of the kernel keyboard driver while still working * in the MEDIUM_RAW mode. This allows us to both keep track of the up/down * status of any key and return character codes from the kernel keymaps. */ static int update_keyboard (void) { unsigned char buf[128]; int bytes_read; fd_set set; struct timeval tv = { 0, 0 }; if (resume_count <= 0) return 0; FD_ZERO(&set); FD_SET(std_keyboard.fd, &set); if (select (FD_SETSIZE, &set, NULL, NULL, &tv) <= 0) return 0; bytes_read = read(std_keyboard.fd, &buf, sizeof(buf)); if (bytes_read < 1) return 0; process_keyboard_data(buf, bytes_read); return 1; } /* activate_keyboard: * Put keyboard into the mode we want. */ static void activate_keyboard (void) { tcsetattr (std_keyboard.fd, TCSANOW, &__al_linux_work_termio); ioctl (std_keyboard.fd, KDSKBMODE, K_MEDIUMRAW); } /* deactivate_keyboard: * Restore the original keyboard settings. */ static void deactivate_keyboard (void) { tcsetattr (std_keyboard.fd, TCSANOW, &__al_linux_startup_termio); ioctl (std_keyboard.fd, KDSKBMODE, startup_kbmode); } /* release_all_keys: * Sends release messages for all keys that are marked as pressed in the * key array. Maybe this should be a global function in `src/keyboard.c'; * it could be used by other platforms, and could work better with the * polled driver emulation system. */ static void release_all_keys (void) { int i; for (i = 0; i < __allegro_KEY_MAX; i++) if (key[i]) _handle_key_release (i); } /* suspend_keyboard: * Suspend our keyboard handling, restoring the original OS values, so that * the application can take advantage of Linux keyboard handling. */ static void suspend_keyboard (void) { resume_count--; if (resume_count == 0) deactivate_keyboard(); release_all_keys(); } /* resume_keyboard: * Start/resume Allegro keyboard handling. */ static void resume_keyboard(void) { if (resume_count == 0) activate_keyboard(); resume_count++; } static int linux_key_init (void) { std_keyboard.fd = dup (__al_linux_console_fd); /* Set terminal attributes we need */ __al_linux_work_termio.c_iflag = 0; __al_linux_work_termio.c_cflag = CS8; __al_linux_work_termio.c_lflag = 0; /* Prevent `read' from blocking */ __al_linux_work_termio.c_cc[VMIN] = 0; __al_linux_work_termio.c_cc[VTIME] = 0; /* Save previous keyboard mode (probably XLATE) */ ioctl (std_keyboard.fd, KDGKBMODE, &startup_kbmode); /* Keyboard is disabled */ resume_count = 0; /* Get the pid, which we use for the three finger salute */ main_pid = getpid(); /* Register our driver (enables it too) */ __al_linux_add_standard_driver (&std_keyboard); return 0; } static void linux_key_exit (void) { /* Reset LED mode before exiting */ ioctl(std_keyboard.fd, KDSETLED, 8); __al_linux_remove_standard_driver (&std_keyboard); close (std_keyboard.fd); } static void linux_set_leds (int leds) { int val = 0; if (leds & __allegro_KB_SCROLOCK_FLAG) val |= LED_SCR; if (leds & __allegro_KB_NUMLOCK_FLAG) val |= LED_NUM; if (leds & __allegro_KB_CAPSLOCK_FLAG) val |= LED_CAP; ioctl(std_keyboard.fd, KDSETLED, val); } static void linux_set_rate (int delay, int rate) { } static int linux_scancode_to_ascii (int scancode) { int kernel_code; struct kbentry kbe; for (kernel_code = 0; kernel_code < 128; kernel_code++) if (scancode == kernel_to_mycode[kernel_code]) break; if (kernel_code == 128) return 0; kbe.kb_table = 0; kbe.kb_index = kernel_code; ioctl (std_keyboard.fd, KDGKBENT, &kbe); switch (KTYP(kbe.kb_value)) { case KT_LETTER: case KT_LATIN: case KT_ASCII: return KVAL(kbe.kb_value); case KT_SPEC: if (scancode == __allegro_KEY_ENTER) return '\r'; break; } return 0; }