俺と Win32 コンソール(3):オレオレ getch その2
もうすこし真面目に作ってみて放置しておいたのを、ふと思い出したので捨ててみたり。
/* getch_msvc.c getch/getwch replacement for msvc/mingw32 (to replace enhenced key prefix 0xE0 with 0x00) Licence: いわゆる Public Domain ってことで (You can use/modify/redistibute it freely BUT NO WARRANTY.) */ #include <windows.h> #ifdef BUILD_FOR_WCHAR #include <wchar.h> #endif #include <conio.h> #include <stdio.h> /* header */ int getch_replacement_for_msvc(void); int getche_replacement_for_msvc(void); #ifdef BUILD_FOR_WCHAR int getwch_replacement_for_msvc(void); int getwche_replacement_for_msvc(void); #endif #define MY_GETCH_WITH_ECHO 1 #define MY_GETCH_ENAHNCED_PREFIX_E0 2 int getch_replacement_with_flags_for_msvc(int flags); int getwch_replacement_with_flags_for_msvc(int flags); /* header end */ #define ENTER_THREAD_ATOMIC(semaphore) #define LEAVE_THREAD_ATOMIC(semaphore) #define SCAN_ENH 0x00010000 #define SCAN_CODE_MASK 0x0000ffff #define EXTRA_00 0x01000000 #define EXTRA_E0 0x02000000 #define IN_NUMLOCKED 0x04000000 #define EXTRA_FLAGS_MASK (EXTRA_00|EXTRA_E0) #define STRIP_EXTRA_FLAGS (~(EXTRA_FLAGS_MASK|IN_NUMLOCKED)) struct vkmap { int vk; int scan; int key; int key_shift; int key_ctrl; int key_alt; int key_ctrl_alt; }; static struct vkmap extra_keymap_msvc[] = { /* { vkey, scan, key, key_shift, key_ctrl, key_alt, key_ctrl_alt } */ /* special case */ { VK_BACK, 0x0e, 0x08, 0x08, 0x7f, 0x08, 0x0e|EXTRA_00 }, /* IBM_15 */ { VK_TAB, 0x0f, 0x09, 0x09, 0x94|EXTRA_00 , 0x94, 0x0f|EXTRA_00 }, /* IBM_16 */ { VK_RETURN, 0x1c, 0x0d, 0x0d, 0x0a, 0x0a, 0x1c|EXTRA_00 }, /* IBM_43 */ { VK_RETURN, 0x1c|SCAN_ENH, 0x0d, 0x0d, 0x0a, 0x0a, 0xa6|EXTRA_00 }, /* IBM_108 (numpad) */ /* enhanced keys */ { VK_INSERT, 0x52|SCAN_ENH, 0x52|EXTRA_E0, 0x52|EXTRA_E0, 0x52|EXTRA_E0, 0xa2|EXTRA_00, 0xa2|EXTRA_00 }, /* IBM_75 */ { VK_DELETE, 0x53|SCAN_ENH, 0x53|EXTRA_E0, 0x53|EXTRA_E0, 0x93|EXTRA_E0, 0xa3|EXTRA_00, 0xa3|EXTRA_00 }, /* IBM_76 */ { VK_LEFT, 0x4b|SCAN_ENH, 0x4b|EXTRA_E0, 0x4b|EXTRA_E0, 0x4b|EXTRA_E0, 0x73|EXTRA_E0, 0x9b|EXTRA_00 }, /* IBM_79 */ { VK_HOME, 0x47|SCAN_ENH, 0x47|EXTRA_E0, 0x47|EXTRA_E0, 0x47|EXTRA_E0, 0x77|EXTRA_E0, 0x97|EXTRA_00 }, /* IBM_80 */ { VK_END, 0x4f|SCAN_ENH, 0x4f|EXTRA_E0, 0x4f|EXTRA_E0, 0x4f|EXTRA_E0, 0x75|EXTRA_E0, 0x9f|EXTRA_00 }, /* IBM_81 */ { VK_UP, 0x48|SCAN_ENH, 0x48|EXTRA_E0, 0x48|EXTRA_E0, 0x48|EXTRA_E0, 0x8d|EXTRA_E0, 0x98|EXTRA_00 }, /* IBM_83 */ { VK_DOWN, 0x50|SCAN_ENH, 0x50|EXTRA_E0, 0x50|EXTRA_E0, 0x50|EXTRA_E0, 0x91|EXTRA_E0, 0xa0|EXTRA_00 }, /* IBM_84 */ { VK_PRIOR, 0x49|SCAN_ENH, 0x49|EXTRA_E0, 0x49|EXTRA_E0, 0x49|EXTRA_E0, 0x86|EXTRA_E0, 0x99|EXTRA_00 }, /* IBM_85 */ { VK_NEXT, 0x51|SCAN_ENH, 0x51|EXTRA_E0, 0x51|EXTRA_E0, 0x51|EXTRA_E0, 0x76|EXTRA_E0, 0xa1|EXTRA_00 }, /* IBM_86 */ { VK_RIGHT, 0x4d|SCAN_ENH, 0x4d|EXTRA_E0, 0x4d|EXTRA_E0, 0x4d|EXTRA_E0, 0x74|EXTRA_E0, 0x9d|EXTRA_00 }, /* IBM_89 */ /* numpad */ { VK_HOME, 0x47, 0x47|EXTRA_00, 0x47|EXTRA_00|IN_NUMLOCKED, 0x77|EXTRA_00, 0x00, 0x00 }, /* IBM_91 */ { VK_LEFT, 0x4b, 0x4b|EXTRA_00, 0x4b|EXTRA_00|IN_NUMLOCKED, 0x73|EXTRA_00, 0x00, 0x00 }, /* IBM_92 */ { VK_END, 0x4f, 0x4f|EXTRA_00, 0x4f|EXTRA_00|IN_NUMLOCKED, 0x75|EXTRA_00, 0x00, 0x00 }, /* IBM_93 */ { VK_UP, 0x48, 0x48|EXTRA_00, 0x48|EXTRA_00|IN_NUMLOCKED, 0x8d|EXTRA_00, 0x00, 0x00 }, /* IBM_96 */ { VK_CLEAR, 0x4c, 0x00 /* 0x4c|EXTRA_00 */, 0x00 /* 0x4c|EXTRA_00|IN_NUMLOCKED */, 0x00 /* 0x4c|EXTRA_00 */, 0x00, 0x00 }, /* IBM_97 */ { VK_DOWN, 0x50, 0x50|EXTRA_00, 0x50|EXTRA_00|IN_NUMLOCKED, 0x91|EXTRA_00, 0x00, 0x00 }, /* IBM_98 */ { VK_INSERT, 0x52, 0x52|EXTRA_00, 0x52|EXTRA_00|IN_NUMLOCKED, 0x92|EXTRA_00, 0x00, 0x00 }, /* IBM_99 */ { VK_PRIOR, 0x49, 0x49|EXTRA_00, 0x49|EXTRA_00|IN_NUMLOCKED, 0x84|EXTRA_00, 0x00, 0x00 }, /* IBM_101 */ { VK_RIGHT, 0x4d, 0x4d|EXTRA_00, 0x4d|EXTRA_00|IN_NUMLOCKED, 0x74|EXTRA_00, 0x00, 0x00 }, /* IBM_102 */ { VK_NEXT, 0x51, 0x51|EXTRA_00, 0x51|EXTRA_00|IN_NUMLOCKED, 0x76|EXTRA_00, 0x00, 0x00 }, /* IBM_103 */ { VK_DELETE, 0x53, 0x53|EXTRA_00, 0x53|EXTRA_00|IN_NUMLOCKED, 0x93|EXTRA_00, 0x00, 0x00 }, /* IBM_104 */ /* function keys */ { VK_F1, 0x3b, 0x3b|EXTRA_00, 0x54|EXTRA_00, 0x5e|EXTRA_00, 0x68|EXTRA_00, 0x68|EXTRA_00 }, /* IBM_112 */ { VK_F2, 0x3c, 0x3c|EXTRA_00, 0x55|EXTRA_00, 0x5f|EXTRA_00, 0x69|EXTRA_00, 0x69|EXTRA_00 }, /* IBM_113 */ { VK_F3, 0x3d, 0x3d|EXTRA_00, 0x56|EXTRA_00, 0x60|EXTRA_00, 0x6a|EXTRA_00, 0x6a|EXTRA_00 }, /* IBM_114 */ { VK_F4, 0x3e, 0x3e|EXTRA_00, 0x57|EXTRA_00, 0x61|EXTRA_00, 0x6b|EXTRA_00, 0x6b|EXTRA_00 }, /* IBM_115 */ { VK_F5, 0x3f, 0x3f|EXTRA_00, 0x58|EXTRA_00, 0x62|EXTRA_00, 0x6c|EXTRA_00, 0x6c|EXTRA_00 }, /* IBM_116 */ { VK_F6, 0x40, 0x40|EXTRA_00, 0x59|EXTRA_00, 0x63|EXTRA_00, 0x6d|EXTRA_00, 0x6d|EXTRA_00 }, /* IBM_117 */ { VK_F7, 0x41, 0x41|EXTRA_00, 0x5a|EXTRA_00, 0x64|EXTRA_00, 0x6e|EXTRA_00, 0x6e|EXTRA_00 }, /* IBM_118 */ { VK_F8, 0x42, 0x42|EXTRA_00, 0x5b|EXTRA_00, 0x65|EXTRA_00, 0x6f|EXTRA_00, 0x6f|EXTRA_00 }, /* IBM_119 */ { VK_F9, 0x43, 0x43|EXTRA_00, 0x5c|EXTRA_00, 0x66|EXTRA_00, 0x70|EXTRA_00, 0x70|EXTRA_00 }, /* IBM_120 */ { VK_F10, 0x44, 0x44|EXTRA_00, 0x5d|EXTRA_00, 0x67|EXTRA_00, 0x71|EXTRA_00, 0x71|EXTRA_00 }, /* IBM_121 */ { VK_F11, 0x57, 0x85|EXTRA_E0, 0x87|EXTRA_E0, 0x89|EXTRA_E0, 0x8b|EXTRA_E0, 0x8b|EXTRA_E0 }, /* IBM_122 */ { VK_F12, 0x58, 0x86|EXTRA_E0, 0x88|EXTRA_E0, 0x8a|EXTRA_E0, 0x8c|EXTRA_E0, 0x8c|EXTRA_E0 }, /* IBM_123 */ /* terminator */ { -1 , -1, 0, 0, 0, 0, 0 } }; static int lookup_keycode(int vkey, int scan, int shift, int ctrl, int alt, int enhkey) { int ch; const struct vkmap *vk; for(ch=0, vk=&extra_keymap_msvc[0]; vk->vk != -1; ++vk) { int match_vkey = vk->vk && vk->vk == vkey; int match_scan = vk->scan && vk->scan == (scan & SCAN_CODE_MASK); int match_enhstate = (enhkey && (vk->scan & SCAN_ENH)) || !enhkey; if ( (match_vkey || match_scan) && match_enhstate) { ch = alt ? ctrl ? vk->key_ctrl_alt : vk->key_alt : ctrl ? vk->key_ctrl : shift ? vk->key_shift : vk->key; break; } } return ch; } #ifdef BUILD_FOR_WCHAR static LONG succ_wkey_value = 0; # define succ_key_value succ_wkey_value # define gettch_replacement_with_flags_for_msvc getwch_replacement_with_flags_for_msvc #else static LONG succ_key_value = 0; # define gettch_replacement_with_flags_for_msvc getch_replacement_with_flags_for_msvc #endif int #ifdef BUILD_FOR_WCHAR getwch_replacement_with_flags_for_msvc (int flags) #else getch_replacement_with_flags_for_msvc (int flags) #endif { HANDLE hConin; DWORD dwPrevMode; BOOL bKey; int ch; if (flags & MY_GETCH_WITH_ECHO) { ch = gettch_replacement_with_flags_for_msvc(flags & ~MY_GETCH_WITH_ECHO); if (ch != -1) #ifdef BUILD_FOR_WCHAR _putwch(ch); #else _putch(ch); #endif return ch; } ch = InterlockedExchange(&succ_key_value, 0); if (ch != 0) return ch; ENTER_THREAD_ATOMIC() bKey = FALSE; /* check whether stdin is console handle */ if ((hConin = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE || !GetConsoleMode(hConin, &dwPrevMode)) { ch = -1; bKey = TRUE; } while(!bKey) { DWORD dwRead, ks; INPUT_RECORD ir; int vk; BOOL b; SetConsoleMode(hConin, 0); /* set raw mode */ for(;;) { #ifdef BUILD_FOR_WCHAR b = ReadConsoleInputW(hConin, &ir, 1, &dwRead); #else b = ReadConsoleInputA(hConin, &ir, 1, &dwRead); #endif if (b && dwRead > 0) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) break; } } SetConsoleMode(hConin, dwPrevMode); if (!b) { ch = -1; break; } #ifdef BUILD_FOR_WCHAR ch = ir.Event.KeyEvent.uChar.UnicodeChar; #else ch = (unsigned char)(ir.Event.KeyEvent.uChar.AsciiChar); #endif vk = (int)(unsigned)(ir.Event.KeyEvent.wVirtualKeyCode); ks = ir.Event.KeyEvent.dwControlKeyState; if (ch == 0 || vk == VK_RETURN || vk == VK_BACK || vk == VK_TAB) { ch = lookup_keycode(vk, 0 /* ir.Event.KeyEvent.wVirtualScanCode */, ks & SHIFT_PRESSED, ks & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED), ks & (LEFT_ALT_PRESSED|RIGHT_CTRL_PRESSED), ks & ENHANCED_KEY); if ((ch & IN_NUMLOCKED) && !(ks & NUMLOCK_ON)) { ch = 0; } if (ch & EXTRA_FLAGS_MASK) { if ((ch & EXTRA_E0) && (flags & MY_GETCH_ENAHNCED_PREFIX_E0)) { succ_key_value = ch & STRIP_EXTRA_FLAGS; bKey = TRUE; ch = 0xe0; break; } else if ((ch & (EXTRA_E0|EXTRA_00))) { succ_key_value = ch & STRIP_EXTRA_FLAGS; bKey = TRUE; ch = 0x00; break; } } ch &= STRIP_EXTRA_FLAGS; } if (ch != 0) bKey = TRUE; } LEAVE_THREAD_ATOMIC() return ch; } #ifdef BUILD_FOR_WCHAR int getwch_replacement_for_msvc(void) { return getwch_replacement_with_flags_for_msvc(0); } int getwche_replacement_for_msvc(void) { return getwch_replacement_with_flags_for_msvc(MY_GETCH_WITH_ECHO); } #else int getch_replacement_for_msvc(void) { return getch_replacement_with_flags_for_msvc(0); } int getche_replacement_for_msvc(void) { return getch_replacement_with_flags_for_msvc(MY_GETCH_WITH_ECHO); } #endif #ifdef TEST int main(int argc, char **argv) { int k; for(;;) { #ifdef BUILD_FOR_WCHAR k = getwch_replacement_for_msvc(); printf("0x%x\n", k); #else k = getch_replacement_for_msvc(); printf("0x%x", k); if (k >=20 && k <= 0x7e) printf(" (%c)", k); printf("\n"); #endif if (k == -1 || k == 0x1b) break; } return 0; } #endif
返されるキーコードが、すべてのキーで _getch() と同じかどうか確認したわけではないですが、まあ extra_keymap_msvc に書き並べたものについては確認しました。ctrl+altの状態がある点が生 dos と違います。(あ、102 キー独自の部分は調べてねえや)
つーか _getche ってほんとに _getch の内容をエコーしてるだけなんだな…カーソルキーとか入力されたら全然わやくちゃになるという。
(2013-02-18 補足)Windows8 では getch のほうだと日本語(DBCS)がまともに取れないようです(2バイト文字の先頭しか取れない)。たぶんコンソール API のバグだと思う。対策としては、
といった感じでしょうか。何というかめんどくさいです…