void CKeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group, KeyModifierMask targetRequired, KeyModifierMask targetSensitive, KeyID sourceID, KeyModifierMask sourceRequired, KeyModifierMask sourceSensitive) { // if we can already generate the target as desired then we're done. if (findCompatibleKey(targetID, group, targetRequired, targetSensitive) != NULL) { return; } // find a compatible source, preferably in the same group for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) { SInt32 eg = getEffectiveGroup(group, gd); const KeyItemList* sourceEntry = findCompatibleKey(sourceID, eg, sourceRequired, sourceSensitive); if (sourceEntry != NULL && sourceEntry->size() == 1) { CKeyMap::KeyItem targetItem = sourceEntry->back(); targetItem.m_id = targetID; targetItem.m_group = eg; addKeyEntry(targetItem); break; } } }
void CMSWindowsKeyState::getKeyMap(CKeyMap& keyMap) { // update keyboard groups if (getGroups(m_groups)) { m_groupMap.clear(); SInt32 numGroups = (SInt32)m_groups.size(); for (SInt32 g = 0; g < numGroups; ++g) { m_groupMap[m_groups[g]] = g; } } HKL activeLayout = GetKeyboardLayout(0); // clear table memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton)); m_keyToVKMap.clear(); CKeyMap::KeyItem item; SInt32 numGroups = (SInt32)m_groups.size(); for (SInt32 g = 0; g < numGroups; ++g) { item.m_group = g; ActivateKeyboardLayout(m_groups[g], 0); // clear tables memset(m_buttonToVK, 0, sizeof(m_buttonToVK)); memset(m_buttonToNumpadVK, 0, sizeof(m_buttonToNumpadVK)); // map buttons (scancodes) to virtual keys for (KeyButton i = 1; i < 256; ++i) { UINT vk = MapVirtualKey(i, 1); if (vk == 0) { // unmapped continue; } // deal with certain virtual keys specially switch (vk) { case VK_SHIFT: vk = VK_LSHIFT; break; case VK_CONTROL: vk = VK_LCONTROL; break; case VK_MENU: vk = VK_LMENU; break; case VK_NUMLOCK: vk = VK_PAUSE; break; case VK_NUMPAD0: case VK_NUMPAD1: case VK_NUMPAD2: case VK_NUMPAD3: case VK_NUMPAD4: case VK_NUMPAD5: case VK_NUMPAD6: case VK_NUMPAD7: case VK_NUMPAD8: case VK_NUMPAD9: case VK_DECIMAL: // numpad keys are saved in their own table m_buttonToNumpadVK[i] = vk; continue; case VK_LWIN: case VK_RWIN: // add extended key only for these on 95 family if (m_is95Family) { m_buttonToVK[i | 0x100u] = vk; continue; } break; case VK_RETURN: case VK_PRIOR: case VK_NEXT: case VK_END: case VK_HOME: case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: case VK_INSERT: case VK_DELETE: // also add extended key for these m_buttonToVK[i | 0x100u] = vk; break; } if (m_buttonToVK[i] == 0) { m_buttonToVK[i] = vk; } } // now map virtual keys to buttons. multiple virtual keys may map // to a single button. if the virtual key matches the one in // m_buttonToVK then we use the button as is. if not then it's // either a numpad key and we use the button as is or it's an // extended button. for (UINT i = 1; i < 255; ++i) { // skip virtual keys we don't want switch (i) { case VK_LBUTTON: case VK_RBUTTON: case VK_MBUTTON: case VK_XBUTTON1: case VK_XBUTTON2: case VK_SHIFT: case VK_CONTROL: case VK_MENU: continue; } // get the button KeyButton button = static_cast<KeyButton>(MapVirtualKey(i, 0)); if (button == 0) { continue; } // deal with certain virtual keys specially switch (i) { case VK_NUMPAD0: case VK_NUMPAD1: case VK_NUMPAD2: case VK_NUMPAD3: case VK_NUMPAD4: case VK_NUMPAD5: case VK_NUMPAD6: case VK_NUMPAD7: case VK_NUMPAD8: case VK_NUMPAD9: case VK_DECIMAL: m_buttonToNumpadVK[button] = i; break; default: // add extended key if virtual keys don't match if (m_buttonToVK[button] != i) { m_buttonToVK[button | 0x100u] = i; } break; } } // add alt+printscreen if (m_buttonToVK[0x54u] == 0) { m_buttonToVK[0x54u] = VK_SNAPSHOT; } // set virtual key to button table if (GetKeyboardLayout(0) == m_groups[g]) { for (KeyButton i = 0; i < 512; ++i) { if (m_buttonToVK[i] != 0) { if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) { m_virtualKeyToButton[m_buttonToVK[i]] = i; } } if (m_buttonToNumpadVK[i] != 0) { if (m_virtualKeyToButton[m_buttonToNumpadVK[i]] == 0) { m_virtualKeyToButton[m_buttonToNumpadVK[i]] = i; } } } } // add numpad keys for (KeyButton i = 0; i < 512; ++i) { if (m_buttonToNumpadVK[i] != 0) { item.m_id = getKeyID(m_buttonToNumpadVK[i], i); item.m_button = i; item.m_required = KeyModifierNumLock; item.m_sensitive = KeyModifierNumLock | KeyModifierShift; item.m_generates = 0; item.m_client = m_buttonToNumpadVK[i]; addKeyEntry(keyMap, item); } } // add other keys BYTE keys[256]; memset(keys, 0, sizeof(keys)); for (KeyButton i = 0; i < 512; ++i) { if (m_buttonToVK[i] != 0) { // initialize item item.m_id = getKeyID(m_buttonToVK[i], i); item.m_button = i; item.m_required = 0; item.m_sensitive = 0; item.m_client = m_buttonToVK[i]; // get flags for modifier keys CKeyMap::initModifierKey(item); if (item.m_id == 0) { // translate virtual key to a character with and without // shift, caps lock, and AltGr. struct Modifier { UINT m_vk1; UINT m_vk2; BYTE m_state; KeyModifierMask m_mask; }; static const Modifier modifiers[] = { { VK_SHIFT, VK_SHIFT, 0x80u, KeyModifierShift }, { VK_CAPITAL, VK_CAPITAL, 0x01u, KeyModifierCapsLock }, { VK_CONTROL, VK_MENU, 0x80u, KeyModifierControl | KeyModifierAlt } }; static const size_t s_numModifiers = sizeof(modifiers) / sizeof(modifiers[0]); static const size_t s_numCombinations = 1 << s_numModifiers; KeyID id[s_numCombinations]; bool anyFound = false; KeyButton button = static_cast<KeyButton>(i & 0xffu); for (size_t j = 0; j < s_numCombinations; ++j) { for (size_t k = 0; k < s_numModifiers; ++k) { //if ((j & (1 << k)) != 0) { // http://msdn.microsoft.com/en-us/library/ke55d167.aspx if ((j & (1i64 << k)) != 0) { keys[modifiers[k].m_vk1] = modifiers[k].m_state; keys[modifiers[k].m_vk2] = modifiers[k].m_state; } else { keys[modifiers[k].m_vk1] = 0; keys[modifiers[k].m_vk2] = 0; } } id[j] = getIDForKey(item, button, m_buttonToVK[i], keys, m_groups[g]); if (id[j] != 0) { anyFound = true; } } if (anyFound) { // determine what modifiers we're sensitive to. // we're sensitive if the KeyID changes when the // modifier does. item.m_sensitive = 0; for (size_t k = 0; k < s_numModifiers; ++k) { for (size_t j = 0; j < s_numCombinations; ++j) { //if (id[j] != id[j ^ (1u << k)]) { // http://msdn.microsoft.com/en-us/library/ke55d167.aspx if (id[j] != id[j ^ (1ui64 << k)]) { item.m_sensitive |= modifiers[k].m_mask; break; } } } // save each key. the map will automatically discard // duplicates, like an unshift and shifted version of // a key that's insensitive to shift. for (size_t j = 0; j < s_numCombinations; ++j) { item.m_id = id[j]; item.m_required = 0; for (size_t k = 0; k < s_numModifiers; ++k) { if ((j & (1i64 << k)) != 0) { item.m_required |= modifiers[k].m_mask; } } addKeyEntry(keyMap, item); } } } else { // found in table switch (m_buttonToVK[i]) { case VK_TAB: // add kKeyLeftTab, too item.m_id = kKeyLeftTab; item.m_required |= KeyModifierShift; item.m_sensitive |= KeyModifierShift; addKeyEntry(keyMap, item); item.m_id = kKeyTab; item.m_required &= ~KeyModifierShift; break; case VK_CANCEL: item.m_required |= KeyModifierControl; item.m_sensitive |= KeyModifierControl; break; case VK_SNAPSHOT: item.m_sensitive |= KeyModifierAlt; if ((i & 0x100u) == 0) { // non-extended snapshot key requires alt item.m_required |= KeyModifierAlt; } break; } addKeyEntry(keyMap, item); } } } } // restore keyboard layout ActivateKeyboardLayout(activeLayout, 0); }
void ConfusabledataBuilder::build(const char * confusables, int32_t confusablesLen, UErrorCode &status) { // Convert the user input data from UTF-8 to UChar (UTF-16) int32_t inputLen = 0; if (U_FAILURE(status)) { return; } u_strFromUTF8(NULL, 0, &inputLen, confusables, confusablesLen, &status); if (status != U_BUFFER_OVERFLOW_ERROR) { return; } status = U_ZERO_ERROR; fInput = static_cast<UChar *>(uprv_malloc((inputLen+1) * sizeof(UChar))); if (fInput == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } u_strFromUTF8(fInput, inputLen+1, NULL, confusables, confusablesLen, &status); // Regular Expression to parse a line from Confusables.txt. The expression will match // any line. What was matched is determined by examining which capture groups have a match. // Capture Group 1: the source char // Capture Group 2: the replacement chars // Capture Group 3-6 the table type, SL, SA, ML, or MA // Capture Group 7: A blank or comment only line. // Capture Group 8: A syntactically invalid line. Anything that didn't match before. // Example Line from the confusables.txt source file: // "1D702 ; 006E 0329 ; SL # MATHEMATICAL ITALIC SMALL ETA ... " fParseLine = uregex_openC( "(?m)^[ \\t]*([0-9A-Fa-f]+)[ \\t]+;" // Match the source char "[ \\t]*([0-9A-Fa-f]+" // Match the replacement char(s) "(?:[ \\t]+[0-9A-Fa-f]+)*)[ \\t]*;" // (continued) "\\s*(?:(SL)|(SA)|(ML)|(MA))" // Match the table type "[ \\t]*(?:#.*?)?$" // Match any trailing #comment "|^([ \\t]*(?:#.*?)?)$" // OR match empty lines or lines with only a #comment "|^(.*?)$", // OR match any line, which catches illegal lines. 0, NULL, &status); // Regular expression for parsing a hex number out of a space-separated list of them. // Capture group 1 gets the number, with spaces removed. fParseHexNum = uregex_openC("\\s*([0-9A-F]+)", 0, NULL, &status); // Zap any Byte Order Mark at the start of input. Changing it to a space is benign // given the syntax of the input. if (*fInput == 0xfeff) { *fInput = 0x20; } // Parse the input, one line per iteration of this loop. uregex_setText(fParseLine, fInput, inputLen, &status); while (uregex_findNext(fParseLine, &status)) { fLineNum++; if (uregex_start(fParseLine, 7, &status) >= 0) { // this was a blank or comment line. continue; } if (uregex_start(fParseLine, 8, &status) >= 0) { // input file syntax error. status = U_PARSE_ERROR; return; } // We have a good input line. Extract the key character and mapping string, and // put them into the appropriate mapping table. UChar32 keyChar = SpoofImpl::ScanHex(fInput, uregex_start(fParseLine, 1, &status), uregex_end(fParseLine, 1, &status), status); int32_t mapStringStart = uregex_start(fParseLine, 2, &status); int32_t mapStringLength = uregex_end(fParseLine, 2, &status) - mapStringStart; uregex_setText(fParseHexNum, &fInput[mapStringStart], mapStringLength, &status); UnicodeString *mapString = new UnicodeString(); if (mapString == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return; } while (uregex_findNext(fParseHexNum, &status)) { UChar32 c = SpoofImpl::ScanHex(&fInput[mapStringStart], uregex_start(fParseHexNum, 1, &status), uregex_end(fParseHexNum, 1, &status), status); mapString->append(c); } U_ASSERT(mapString->length() >= 1); // Put the map (value) string into the string pool // This a little like a Java intern() - any duplicates will be eliminated. SPUString *smapString = stringPool->addString(mapString, status); // Add the UChar32 -> string mapping to the appropriate table. UHashtable *table = uregex_start(fParseLine, 3, &status) >= 0 ? fSLTable : uregex_start(fParseLine, 4, &status) >= 0 ? fSATable : uregex_start(fParseLine, 5, &status) >= 0 ? fMLTable : uregex_start(fParseLine, 6, &status) >= 0 ? fMATable : NULL; U_ASSERT(table != NULL); uhash_iput(table, keyChar, smapString, &status); fKeySet->add(keyChar); if (U_FAILURE(status)) { return; } } // Input data is now all parsed and collected. // Now create the run-time binary form of the data. // // This is done in two steps. First the data is assembled into vectors and strings, // for ease of construction, then the contents of these collections are dumped // into the actual raw-bytes data storage. // Build up the string array, and record the index of each string therein // in the (build time only) string pool. // Strings of length one are not entered into the strings array. // At the same time, build up the string lengths table, which records the // position in the string table of the first string of each length >= 4. // (Strings in the table are sorted by length) stringPool->sort(status); fStringTable = new UnicodeString(); fStringLengthsTable = new UVector(status); int32_t previousStringLength = 0; int32_t previousStringIndex = 0; int32_t poolSize = stringPool->size(); int32_t i; for (i=0; i<poolSize; i++) { SPUString *s = stringPool->getByIndex(i); int32_t strLen = s->fStr->length(); int32_t strIndex = fStringTable->length(); U_ASSERT(strLen >= previousStringLength); if (strLen == 1) { // strings of length one do not get an entry in the string table. // Keep the single string character itself here, which is the same // convention that is used in the final run-time string table index. s->fStrTableIndex = s->fStr->charAt(0); } else { if ((strLen > previousStringLength) && (previousStringLength >= 4)) { fStringLengthsTable->addElement(previousStringIndex, status); fStringLengthsTable->addElement(previousStringLength, status); } s->fStrTableIndex = strIndex; fStringTable->append(*(s->fStr)); } previousStringLength = strLen; previousStringIndex = strIndex; } // Make the final entry to the string lengths table. // (it holds an entry for the _last_ string of each length, so adding the // final one doesn't happen in the main loop because no longer string was encountered.) if (previousStringLength >= 4) { fStringLengthsTable->addElement(previousStringIndex, status); fStringLengthsTable->addElement(previousStringLength, status); } // Construct the compile-time Key and Value tables // // For each key code point, check which mapping tables it applies to, // and create the final data for the key & value structures. // // The four logical mapping tables are conflated into one combined table. // If multiple logical tables have the same mapping for some key, they // share a single entry in the combined table. // If more than one mapping exists for the same key code point, multiple // entries will be created in the table for (int32_t range=0; range<fKeySet->getRangeCount(); range++) { // It is an oddity of the UnicodeSet API that simply enumerating the contained // code points requires a nested loop. for (UChar32 keyChar=fKeySet->getRangeStart(range); keyChar <= fKeySet->getRangeEnd(range); keyChar++) { addKeyEntry(keyChar, fSLTable, USPOOF_SL_TABLE_FLAG, status); addKeyEntry(keyChar, fSATable, USPOOF_SA_TABLE_FLAG, status); addKeyEntry(keyChar, fMLTable, USPOOF_ML_TABLE_FLAG, status); addKeyEntry(keyChar, fMATable, USPOOF_MA_TABLE_FLAG, status); } } // Put the assembled data into the flat runtime array outputData(status); // All of the intermediate allocated data belongs to the ConfusabledataBuilder // object (this), and is deleted in the destructor. return; }