diff --git a/reactos/tools/kbdtool/parser.c b/reactos/tools/kbdtool/parser.c index 4308d25b2f8..c292f45bc8c 100644 --- a/reactos/tools/kbdtool/parser.c +++ b/reactos/tools/kbdtool/parser.c @@ -26,9 +26,36 @@ typedef struct tagSCVK USHORT ScanCode; USHORT VirtualKey; PCHAR Name; - PVOID Reserved; + BOOLEAN Processed; } SCVK, *PSCVK; +typedef struct tagVKNAME +{ + ULONG VirtualKey; + PCHAR Name; +} VKNAME, *PVKNAME; + +typedef struct tagLAYOUTENTRY +{ + USHORT ScanCode; + UCHAR VirtualKey; + UCHAR OriginalVirtualKey; + UCHAR Cap; + ULONG StateCount; + ULONG CharData[8]; + ULONG DeadCharData[8]; + ULONG OtherCharData[8]; + struct LAYOUTENTRY* CapData; + PCHAR Name; + ULONG Processed; + ULONG LineCount; +} LAYOUTENTRY, *PLAYOUTENTRY; + +typedef struct tagLAYOUT +{ + LAYOUTENTRY Entry[110]; +} LAYOUT, *PLAYOUT; + /* GLOBALS ********************************************************************/ #define KEYWORD_COUNT 17 @@ -44,7 +71,7 @@ CHAR gCompany[256]; CHAR gLocaleName[256]; ULONG gID = 0; ULONG gKbdLayoutVersion; -CHAR g_Layout[4096]; +LAYOUT g_Layout; ULONG gLineCount; PCHAR KeyWordList[KEYWORD_COUNT] = { @@ -70,119 +97,160 @@ PCHAR KeyWordList[KEYWORD_COUNT] = /* ISO 110-key Keyboard Scancode to Virtual Key Conversion Table */ SCVK ScVk[] = { - {0x02, '1', NULL, NULL}, - {0x03, '2', NULL, NULL}, - {0x04, '3', NULL, NULL}, - {0x05, '4', NULL, NULL}, - {0x06, '5', NULL, NULL}, - {0x07, '6', NULL, NULL}, - {0x08, '7', NULL, NULL}, - {0x09, '8', NULL, NULL}, - {0x0a, '9', NULL, NULL}, - {0x0b, '0', NULL, NULL}, - {0x0c, 0xbd, NULL, NULL}, - {0x0d, 0xbb, NULL, NULL}, - {0x10, 'Q', NULL, NULL}, - {0x11, 'W', NULL, NULL}, - {0x12, 'E', NULL, NULL}, - {0x13, 'R', NULL, NULL}, - {0x14, 'T', NULL, NULL}, - {0x15, 'Y', NULL, NULL}, - {0x16, 'U', NULL, NULL}, - {0x17, 'I', NULL, NULL}, - {0x18, 'O', NULL, NULL}, - {0x19, 'P', NULL, NULL}, - {0x1a, 0xdb, NULL, NULL}, - {0x1b, 0xdd, NULL, NULL}, - {0x1e, 'A', NULL, NULL}, - {0x1f, 'S', NULL, NULL}, - {0x20, 'D', NULL, NULL}, - {0x21, 'F', NULL, NULL}, - {0x22, 'G', NULL, NULL}, - {0x23, 'H', NULL, NULL}, - {0x24, 'J', NULL, NULL}, - {0x25, 'K', NULL, NULL}, - {0x26, 'L', NULL, NULL}, - {0x27, 0xba, NULL, NULL}, - {0x28, 0xde, NULL, NULL}, - {0x29, 0xc0, NULL, NULL}, - {0x2b, 0xdc, NULL, NULL}, - {0x2c, 'Z', NULL, NULL}, - {0x2d, 'X', NULL, NULL}, - {0x2e, 'C', NULL, NULL}, - {0x2f, 'V', NULL, NULL}, - {0x30, 'B', NULL, NULL}, - {0x31, 'N', NULL, NULL}, - {0x32, 'M', NULL, NULL}, - {0x33, 0xbc, NULL, NULL}, - {0x34, 0xbe, NULL, NULL}, - {0x35, 0xbf, NULL, NULL}, - {0x53, 0x6e, NULL, NULL}, - {0x56, 0xe2, NULL, NULL}, - {0x73, 0xc1, NULL, NULL}, - {0x7e, 0xc2, NULL, NULL}, - {0xe010, 0xb1, "Speedracer: Previous Track", NULL}, - {0xe019, 0xb0, "Speedracer: Next Track", NULL}, - {0xe01d, 0xa3, "RControl", NULL}, - {0xe020, 0xad, "Speedracer: Volume Mute", NULL}, - {0xe021, 0xb7, "Speedracer: Launch App 2", NULL}, - {0xe022, 0xb3, "Speedracer: Media Play/Pause", NULL}, - {0xe024, 0xb2, "Speedracer: Media Stop", NULL}, - {0xe02e, 0xae, "Speedracer: Volume Up", NULL}, - {0xe030, 0xaf, "Speedracer: Volume Down", NULL}, - {0xe032, 0xac, "Speedracer: Browser Home", NULL}, - {0xe035, 0x6f, "Numpad Divide", NULL}, - {0xe037, 0x2c, "Snapshot", NULL}, - {0xe038, 0xa5, "RMenu", NULL}, - {0xe047, 0x24, "Home", NULL}, - {0xe048, 0x26, "Up", NULL}, - {0xe049, 0x21, "Prior", NULL}, - {0xe04b, 0x25, "Left", NULL}, - {0xe04d, 0x27, "Right", NULL}, - {0xe04f, 0x23, "End", NULL}, - {0xe050, 0x28, "Down", NULL}, - {0xe051, 0x22, "Next", NULL}, - {0xe052, 0x2d, "Insert", NULL}, - {0xe053, 0x2e, "Delete", NULL}, - {0xe05b, 0x5b, "Left Win", NULL}, - {0xe05c, 0x5c, "Right Win", NULL}, - {0xe05d, 0x5d, "Application", NULL}, - {0xe05e, 0xff, "Power", NULL}, - {0xe05f, 0x5f, "Speedracer: Sleep", NULL}, - {0xe060, 0xff, "BAD SCANCODE", NULL}, - {0xe061, 0xff, "BAD SCANCODE", NULL}, - {0xe065, 0xaa, "Speedracer: Browser Search", NULL}, - {0xe066, 0xab, "Speedracer: Browser Favorites", NULL}, - {0xe067, 0xa8, "Speedracer: Browser Refresh", NULL}, - {0xe068, 0xa9, "Speedracer: Browser Stop", NULL}, - {0xe069, 0xa7, "Speedracer: Browser Foward", NULL}, - {0xe06a, 0xa6, "Speedracer: Browser Back", NULL}, - {0xe06b, 0xb6, "Speedracer: Launch App 1", NULL}, - {0xe06c, 0xb4, "Speedracer: Launch Mail", NULL}, - {0xe06d, 0xb5, "Speedracer: Launch Media Selector", NULL}, - {0x53, 0x6e, NULL, NULL}, - {0x0e, 0x08, NULL, NULL}, - {0x01, 0x1b, NULL, NULL}, - {0xe01c, 0x0d, "Numpad Enter", NULL}, - {0x1c, 0x0d, NULL, NULL}, - {0x39, 0x20, NULL, NULL}, - {0xe046, 0x03, "Break (Ctrl + Pause)", NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL}, - {0xFFFF, 0x00, NULL, NULL} + {0x02, '1', NULL, FALSE}, + {0x03, '2', NULL, FALSE}, + {0x04, '3', NULL, FALSE}, + {0x05, '4', NULL, FALSE}, + {0x06, '5', NULL, FALSE}, + {0x07, '6', NULL, FALSE}, + {0x08, '7', NULL, FALSE}, + {0x09, '8', NULL, FALSE}, + {0x0a, '9', NULL, FALSE}, + {0x0b, '0', NULL, FALSE}, + {0x0c, 0xbd, NULL, FALSE}, + {0x0d, 0xbb, NULL, FALSE}, + {0x10, 'Q', NULL, FALSE}, + {0x11, 'W', NULL, FALSE}, + {0x12, 'E', NULL, FALSE}, + {0x13, 'R', NULL, FALSE}, + {0x14, 'T', NULL, FALSE}, + {0x15, 'Y', NULL, FALSE}, + {0x16, 'U', NULL, FALSE}, + {0x17, 'I', NULL, FALSE}, + {0x18, 'O', NULL, FALSE}, + {0x19, 'P', NULL, FALSE}, + {0x1a, 0xdb, NULL, FALSE}, + {0x1b, 0xdd, NULL, FALSE}, + {0x1e, 'A', NULL, FALSE}, + {0x1f, 'S', NULL, FALSE}, + {0x20, 'D', NULL, FALSE}, + {0x21, 'F', NULL, FALSE}, + {0x22, 'G', NULL, FALSE}, + {0x23, 'H', NULL, FALSE}, + {0x24, 'J', NULL, FALSE}, + {0x25, 'K', NULL, FALSE}, + {0x26, 'L', NULL, FALSE}, + {0x27, 0xba, NULL, FALSE}, + {0x28, 0xde, NULL, FALSE}, + {0x29, 0xc0, NULL, FALSE}, + {0x2b, 0xdc, NULL, FALSE}, + {0x2c, 'Z', NULL, FALSE}, + {0x2d, 'X', NULL, FALSE}, + {0x2e, 'C', NULL, FALSE}, + {0x2f, 'V', NULL, FALSE}, + {0x30, 'B', NULL, FALSE}, + {0x31, 'N', NULL, FALSE}, + {0x32, 'M', NULL, FALSE}, + {0x33, 0xbc, NULL, FALSE}, + {0x34, 0xbe, NULL, FALSE}, + {0x35, 0xbf, NULL, FALSE}, + {0x53, 0x6e, NULL, FALSE}, + {0x56, 0xe2, NULL, FALSE}, + {0x73, 0xc1, NULL, FALSE}, + {0x7e, 0xc2, NULL, FALSE}, + {0xe010, 0xb1, "Speedracer: Previous Track", FALSE}, + {0xe019, 0xb0, "Speedracer: Next Track", FALSE}, + {0xe01d, 0xa3, "RControl", FALSE}, + {0xe020, 0xad, "Speedracer: Volume Mute", FALSE}, + {0xe021, 0xb7, "Speedracer: Launch App 2", FALSE}, + {0xe022, 0xb3, "Speedracer: Media Play/Pause", FALSE}, + {0xe024, 0xb2, "Speedracer: Media Stop", FALSE}, + {0xe02e, 0xae, "Speedracer: Volume Up", FALSE}, + {0xe030, 0xaf, "Speedracer: Volume Down", FALSE}, + {0xe032, 0xac, "Speedracer: Browser Home", FALSE}, + {0xe035, 0x6f, "Numpad Divide", FALSE}, + {0xe037, 0x2c, "Snapshot", FALSE}, + {0xe038, 0xa5, "RMenu", FALSE}, + {0xe047, 0x24, "Home", FALSE}, + {0xe048, 0x26, "Up", FALSE}, + {0xe049, 0x21, "Prior", FALSE}, + {0xe04b, 0x25, "Left", FALSE}, + {0xe04d, 0x27, "Right", FALSE}, + {0xe04f, 0x23, "End", FALSE}, + {0xe050, 0x28, "Down", FALSE}, + {0xe051, 0x22, "Next", FALSE}, + {0xe052, 0x2d, "Insert", FALSE}, + {0xe053, 0x2e, "Delete", FALSE}, + {0xe05b, 0x5b, "Left Win", FALSE}, + {0xe05c, 0x5c, "Right Win", FALSE}, + {0xe05d, 0x5d, "Application", FALSE}, + {0xe05e, 0xff, "Power", FALSE}, + {0xe05f, 0x5f, "Speedracer: Sleep", FALSE}, + {0xe060, 0xff, "BAD SCANCODE", FALSE}, + {0xe061, 0xff, "BAD SCANCODE", FALSE}, + {0xe065, 0xaa, "Speedracer: Browser Search", FALSE}, + {0xe066, 0xab, "Speedracer: Browser Favorites", FALSE}, + {0xe067, 0xa8, "Speedracer: Browser Refresh", FALSE}, + {0xe068, 0xa9, "Speedracer: Browser Stop", FALSE}, + {0xe069, 0xa7, "Speedracer: Browser Foward", FALSE}, + {0xe06a, 0xa6, "Speedracer: Browser Back", FALSE}, + {0xe06b, 0xb6, "Speedracer: Launch App 1", FALSE}, + {0xe06c, 0xb4, "Speedracer: Launch Mail", FALSE}, + {0xe06d, 0xb5, "Speedracer: Launch Media Selector", FALSE}, + {0x53, 0x6e, NULL, FALSE}, + {0x0e, 0x08, NULL, FALSE}, + {0x01, 0x1b, NULL, FALSE}, + {0xe01c, 0x0d, "Numpad Enter", FALSE}, + {0x1c, 0x0d, NULL, FALSE}, + {0x39, 0x20, NULL, FALSE}, + {0xe046, 0x03, "Break (Ctrl + Pause)", FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE}, + {0xFFFF, 0x00, NULL, FALSE} }; +VKNAME VKName[] = +{ + {0x08, "BACK"}, + {0x03, "CANCEL"}, + {0x1b, "ESCAPE"}, + {0x0d, "RETURN"}, + {0x20, "SPACE"}, + {0x6e, "DECIMAL"}, + {0xba, "OEM_1"}, + {0xbb, "OEM_PLUS"}, + {0xbc, "OEM_COMMA"}, + {0xbd, "OEM_MINUS"}, + {0xbe, "OEM_PERIOD"}, + {0xbf, "OEM_2"}, + {0xc0, "OEM_3"}, + {0xdb, "OEM_4"}, + {0xdc, "OEM_5"}, + {0xdd, "OEM_6"}, + {0xde, "OEM_7"}, + {0xdf, "OEM_8"}, + {0xe2, "OEM_102"}, + {0xc1, "ABNT_C1"}, + {0xc2, "ABNT_C2"}, + {0x10, "SHIFT"}, + {0xa0, "LSHIFT"}, + {0xa1, "RSHIFT"}, + {0x12, "MENU"}, + {0xa4, "LMENU"}, + {0xa5, "RMENU"}, + {0x11, "CONTROL"}, + {0xa2, "LCONTROL"}, + {0xa3, "RCONTROL"}, + {0x6c, "SEPARATOR"}, + {0xe4, "ICO_00"}, + {0x2e, "DELETE"}, + {0x2d, "INSERT"}, + {0xe5, "GROUPSHIFT"}, + {0xe6, "RGROUPSHIFT"} +}; + + /* FUNCTIONS ******************************************************************/ ULONG @@ -197,6 +265,52 @@ isKeyWord(PCHAR p) return i; } +ULONG +getVKNum(IN PCHAR p) +{ + ULONG Length; + ULONG i; + ULONG KeyNumber; + + /* Compute the length of the string */ + Length = strlen(p); + if (!Length) return -1; + + /* Check if this is is a simple key */ + if (Length == 1) + { + /* If it's a number, return it now */ + if ((*p >= '0') && (*p <= '9')) return *p; + + /* Otherwise, convert the letter to upper case */ + *p = toupper(*p); + + /* And make sure it's a valid letter */ + if ((*p >= 'A') && (*p <='Z')) return *p; + + /* Otherwise, fail */ + return -1; + } + + /* Otherwise, scan our virtual key names */ + for (i = 0; i < 36; i++) + { + /* Check if we have a match */ + if (!strcmp(VKName[i].Name, p)) return VKName[i].VirtualKey; + } + + /* Check if this is a hex string */ + if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X'))) + { + /* Get the key number from the hex string */ + *(p + 1) = 'x'; + if (sscanf(p, "0x%x", &KeyNumber) == 1) return KeyNumber; + } + + /* No hope: fail */ + return -1; +} + BOOLEAN NextLine(PCHAR LineBuffer, ULONG BufferSize, @@ -680,11 +794,131 @@ DoDEADKEY(PVOID DeadKeyData) } ULONG -DoLAYOUT(IN PVOID LayoutData, +DoLAYOUT(IN PLAYOUT LayoutData, IN PVOID LigatureData, IN PULONG ShiftStates, IN ULONG StateCount) { + CHAR Token[32]; + CHAR Cap[8]; + ULONG KeyWord; + ULONG ScanCode, CurrentCode; + ULONG TokenCount; + ULONG VirtualKey; + ULONG i; + BOOLEAN FullEntry; + ULONG ScanCodeCount = -1; + PLAYOUTENTRY Entry; + + /* Only attempt this is Verbose is enabled (FOR DEBUGGING ONLY) */ + if (!Verbose) return SkipLines(); + + /* Zero out the layout */ + memset(LayoutData, 0, sizeof(LAYOUT)); + + /* Read each line */ + Entry = &LayoutData->Entry[0]; + while (NextLine(gBuf, 256, gfpInput)) + { + /* Search for token */ + if (sscanf(gBuf, "%s", Token) != 1) continue; + + /* Make sure it's not just a comment */ + if (*Token == ';') continue; + + /* Make sure it's not a keyword */ + KeyWord = isKeyWord(Token); + if (KeyWord < KEYWORD_COUNT) break; + + /* Now read the entry */ + TokenCount = sscanf(gBuf, " %x %s %s", &ScanCode, Token, Cap); + if (TokenCount == 3) + { + /* Full entry with cap */ + FullEntry = TRUE; + } + else if (TokenCount != 2) + { + /* Fail, invalid LAYOUT entry */ + printf("There are not enough columns in the layout list.\n"); + exit(1); + } + else + { + /* Simplified layout with no cap */ + FullEntry = FALSE; + } + + /* One more */ + Entry++; + if (++ScanCodeCount >= 110) + { + /* Too many! */ + printf("ScanCode %02x - too many scancodes here to parse.\n", ScanCode); + exit(1); + } + + /* Fill out this entry */ + Entry->ScanCode = ScanCode; + Entry->LineCount = gLineCount; + + /* Loop scancode table */ + for (i = 0; i < 110; i++) + { + /* Get the current code */ + CurrentCode = ScVk[i].ScanCode; + if (CurrentCode == 0xFFFF) + { + /* New code */ + if (Verbose) printf("A new scancode is being defined: 0x%2X, %s\n", Entry->ScanCode, Token); + + /* Fill out the entry */ + Entry->VirtualKey = getVKNum(Token); + goto FillEntry; + } + + /* If we found it, process it */ + if (ScanCode == CurrentCode) break; + } + + /* Make sure we didn't already process it */ + if (ScVk[i].Processed) + { + /* Fail */ + printf("Scancode %X was previously defined.\n", ScanCode); + exit(1); + } + + /* Check if there is a valid virtual key */ + if (ScVk[i].VirtualKey == 0xFFFF) + { + /* Fail */ + printf("The Scancode you tried to use (%X) is reserved.\n", ScanCode); + exit(1); + } + + /* Fill out the entry */ + Entry->OriginalVirtualKey = ScVk[i].VirtualKey; + Entry->Name = ScVk[i].Name; + +FillEntry: + Entry->Processed = TRUE; + ScVk[i].Processed = TRUE; + + /* Get the virtual key from the entry */ + VirtualKey = getVKNum(Token); + Entry->VirtualKey = VirtualKey; + + /* Make sure it's valid */ + if (VirtualKey == 0xFFFF) + { + /* Warn the user */ + if (Verbose) printf("An invalid Virtual Key '%s' was defined.\n", Token); + continue; + } + } + + /* Skip what's left */ return SkipLines(); }