/* * Copyright 2004-2012, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Jérôme Duval * Axel Dörfler, axeld@pinc-software.de. * John Scipione, jscipione@gmail.com. */ #include #include #include #include #include #include #include #include #ifdef HAIKU_TARGET_PLATFORM_HAIKU # include "SystemKeymap.h" // generated by the build system #endif // Private only at this point, as we might want to improve the dead key // implementation in the future enum dead_key_index { kDeadKeyAcute = 1, kDeadKeyGrave, kDeadKeyCircumflex, kDeadKeyDiaeresis, kDeadKeyTilde }; static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY | B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY; BKeymap::BKeymap() : fChars(NULL), fCharsSize(0) { Unset(); } BKeymap::~BKeymap() { delete[] fChars; } /*! Load a map from a file. File format in big endian: struct key_map uint32 size of following charset charset (offsets go into this with size of character followed by character) */ status_t BKeymap::SetTo(const char* path) { BFile file; status_t status = file.SetTo(path, B_READ_ONLY); if (status != B_OK) return status; return SetTo(file); } status_t BKeymap::SetTo(BDataIO& stream) { if (stream.Read(&fKeys, sizeof(fKeys)) < 1) return B_IO_ERROR; // convert from big-endian for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) { ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]); } if (fKeys.version != 3) return B_BAD_DATA; if (stream.Read(&fCharsSize, sizeof(uint32)) < 1) return B_IO_ERROR; fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize); if (fCharsSize > 16 * 1024) { Unset(); return B_BAD_DATA; } delete[] fChars; fChars = new char[fCharsSize]; if (stream.Read(fChars, fCharsSize) != (ssize_t)fCharsSize) { Unset(); return B_IO_ERROR; } return B_OK; } status_t BKeymap::SetToCurrent() { #ifdef HAIKU_TARGET_PLATFORM_HAIKU key_map* keys = NULL; ssize_t charsSize; delete[] fChars; _get_key_map(&keys, &fChars, &charsSize); if (!keys) return B_ERROR; memcpy(&fKeys, keys, sizeof(fKeys)); free(keys); fCharsSize = (uint32)charsSize; return B_OK; #else // ! __BEOS__ fprintf(stderr, "Unsupported operation on this platform!\n"); exit(1); #endif // ! __BEOS__ } status_t BKeymap::SetToDefault() { #ifdef HAIKU_TARGET_PLATFORM_HAIKU fKeys = kSystemKeymap; fCharsSize = kSystemKeyCharsSize; delete[] fChars; fChars = new (std::nothrow) char[fCharsSize]; if (fChars == NULL) { Unset(); return B_NO_MEMORY; } memcpy(fChars, kSystemKeyChars, fCharsSize); return B_OK; #else // ! __BEOS__ fprintf(stderr, "Unsupported operation on this platform!\n"); exit(1); #endif // ! __BEOS__ } void BKeymap::Unset() { delete[] fChars; fChars = NULL; fCharsSize = 0; memset(&fKeys, 0, sizeof(fKeys)); } /*! We need to know if a key is a modifier key to choose a valid key when several are pressed together */ bool BKeymap::IsModifierKey(uint32 keyCode) const { return keyCode == fKeys.caps_key || keyCode == fKeys.num_key || keyCode == fKeys.scroll_key || keyCode == fKeys.left_shift_key || keyCode == fKeys.right_shift_key || keyCode == fKeys.left_command_key || keyCode == fKeys.right_command_key || keyCode == fKeys.left_control_key || keyCode == fKeys.right_control_key || keyCode == fKeys.left_option_key || keyCode == fKeys.right_option_key || keyCode == fKeys.menu_key; } //! We need to know a modifier for a key uint32 BKeymap::Modifier(uint32 keyCode) const { if (keyCode == fKeys.caps_key) return B_CAPS_LOCK; if (keyCode == fKeys.num_key) return B_NUM_LOCK; if (keyCode == fKeys.scroll_key) return B_SCROLL_LOCK; if (keyCode == fKeys.left_shift_key) return B_LEFT_SHIFT_KEY | B_SHIFT_KEY; if (keyCode == fKeys.right_shift_key) return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY; if (keyCode == fKeys.left_command_key) return B_LEFT_COMMAND_KEY | B_COMMAND_KEY; if (keyCode == fKeys.right_command_key) return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY; if (keyCode == fKeys.left_control_key) return B_LEFT_CONTROL_KEY | B_CONTROL_KEY; if (keyCode == fKeys.right_control_key) return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY; if (keyCode == fKeys.left_option_key) return B_LEFT_OPTION_KEY | B_OPTION_KEY; if (keyCode == fKeys.right_option_key) return B_RIGHT_OPTION_KEY | B_OPTION_KEY; if (keyCode == fKeys.menu_key) return B_MENU_KEY; return 0; } uint32 BKeymap::KeyForModifier(uint32 modifier) const { if (modifier == B_CAPS_LOCK) return fKeys.caps_key; if (modifier == B_NUM_LOCK) return fKeys.num_key; if (modifier == B_SCROLL_LOCK) return fKeys.scroll_key; if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY) return fKeys.left_shift_key; if (modifier == B_RIGHT_SHIFT_KEY) return fKeys.right_shift_key; if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY) return fKeys.left_command_key; if (modifier == B_RIGHT_COMMAND_KEY) return fKeys.right_command_key; if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY) return fKeys.left_control_key; if (modifier == B_RIGHT_CONTROL_KEY) return fKeys.right_control_key; if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY) return fKeys.left_option_key; if (modifier == B_RIGHT_OPTION_KEY) return fKeys.right_option_key; if (modifier == B_MENU_KEY) return fKeys.menu_key; return 0; } /*! Checks whether a key is an active dead key. */ uint8 BKeymap::ActiveDeadKey(uint32 keyCode, uint32 modifiers) const { bool enabled; uint8 deadKey = DeadKey(keyCode, modifiers, &enabled); if (deadKey == 0 || !enabled) return 0; return deadKey; } /*! Checks whether a key is a dead key. If it is, the enabled/disabled state of that dead key will be passed out via isEnabled (isEnabled is not touched for non-dead keys). */ uint8 BKeymap::DeadKey(uint32 keyCode, uint32 modifiers, bool* _isEnabled) const { uint32 tableMask = 0; int32 offset = Offset(keyCode, modifiers, &tableMask); uint8 deadKeyIndex = DeadKeyIndex(offset); if (deadKeyIndex > 0 && _isEnabled != NULL) { uint32 deadTables[] = { fKeys.acute_tables, fKeys.grave_tables, fKeys.circumflex_tables, fKeys.dieresis_tables, fKeys.tilde_tables }; *_isEnabled = (deadTables[deadKeyIndex - 1] & tableMask) != 0; } return deadKeyIndex; } //! Tell if a key is a dead second key. bool BKeymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey) const { if (!activeDeadKey) return false; int32 offset = Offset(keyCode, modifiers); if (offset < 0) return false; uint32 numBytes = fChars[offset]; if (!numBytes) return false; const int32* deadOffsets[] = { fKeys.acute_dead_key, fKeys.grave_dead_key, fKeys.circumflex_dead_key, fKeys.dieresis_dead_key, fKeys.tilde_dead_key }; const int32* deadOffset = deadOffsets[activeDeadKey - 1]; for (int32 i = 0; i < 32; i++) { if (offset == deadOffset[i]) return true; uint32 deadNumBytes = fChars[deadOffset[i]]; if (!deadNumBytes) continue; if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1], deadNumBytes) == 0) return true; i++; } return false; } //! Get the char for a key given modifiers and active dead key void BKeymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey, char** chars, int32* numBytes) const { *numBytes = 0; *chars = NULL; if (keyCode > 128 || fChars == NULL) return; // here we take NUMLOCK into account if ((modifiers & B_NUM_LOCK) != 0) { switch (keyCode) { case 0x37: case 0x38: case 0x39: case 0x48: case 0x49: case 0x4a: case 0x58: case 0x59: case 0x5a: case 0x64: case 0x65: modifiers ^= B_SHIFT_KEY; } } int32 offset = Offset(keyCode, modifiers); if (offset < 0) return; // here we get the char size *numBytes = fChars[offset]; if (*numBytes <= 0) { // if key is not mapped in the option table, fall-through. if ((modifiers & B_OPTION_KEY) != 0) { offset = Offset(keyCode, modifiers & ~B_OPTION_KEY); if (offset < 0) return; // get the char size again *numBytes = fChars[offset]; if (*numBytes <= 0) return; } else return; } // here we take an potential active dead key const int32* deadKey; switch (activeDeadKey) { case kDeadKeyAcute: deadKey = fKeys.acute_dead_key; break; case kDeadKeyGrave: deadKey = fKeys.grave_dead_key; break; case kDeadKeyCircumflex: deadKey = fKeys.circumflex_dead_key; break; case kDeadKeyDiaeresis: deadKey = fKeys.dieresis_dead_key; break; case kDeadKeyTilde: deadKey = fKeys.tilde_dead_key; break; default: { // if not dead, we copy and return the char char* str = *chars = new char[*numBytes + 1]; strncpy(str, &fChars[offset + 1], *numBytes); str[*numBytes] = 0; return; } } // if dead key, we search for our current offset char in the dead key // offset table string comparison is needed for (int32 i = 0; i < 32; i += 2) { if (strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes) == 0) { *numBytes = fChars[deadKey[i + 1]]; switch (*numBytes) { case 0: // Not mapped *chars = NULL; break; default: { // 1-, 2-, 3-, or 4-byte UTF-8 character char *str = *chars = new char[*numBytes + 1]; strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes); str[*numBytes] = 0; break; } } return; } } // if not found we return the current char mapped *chars = new char[*numBytes + 1]; strncpy(*chars, &fChars[offset + 1], *numBytes); (*chars)[*numBytes] = 0; } /*! Get a list of characters translated from a given character and set of modifiers to another set of modifiers. */ status_t BKeymap::GetModifiedCharacters(const char* in, int32 inModifiers, int32 outModifiers, BObjectList* _outList) { if (in == NULL || *in == '\0' || _outList == NULL) return B_BAD_VALUE; for(uint32 i = 0; i < 128; i++) { int32 inOffset = Offset(i, inModifiers); size_t sizeIn = fChars[inOffset++]; if (sizeIn == 0 || memcmp(in, fChars + inOffset, sizeIn) != 0) { // this character isn't mapped or doesn't match continue; } int32 outOffset = Offset(i, outModifiers); size_t sizeOut = fChars[outOffset++]; char* out = (char*)malloc(sizeOut + 1); if (out == NULL) return B_NO_MEMORY; memcpy(out, fChars + outOffset, sizeOut); out[sizeOut] = '\0'; _outList->AddItem((const char*)out); } return B_OK; } bool BKeymap::operator==(const BKeymap& other) const { return fCharsSize == other.fCharsSize && !memcmp(&fKeys, &other.fKeys, sizeof(fKeys)) && !memcmp(fChars, other.fChars, fCharsSize); } bool BKeymap::operator!=(const BKeymap& other) const { return !(*this == other); } BKeymap& BKeymap::operator=(const BKeymap& other) { Unset(); fCharsSize = other.fCharsSize; fChars = new char[fCharsSize]; memcpy(fChars, other.fChars, fCharsSize); memcpy(&fKeys, &other.fKeys, sizeof(fKeys)); return *this; } int32 BKeymap::Offset(uint32 keyCode, uint32 modifiers, uint32* _table) const { int32 offset; uint32 table; if (keyCode >= 128) return -1; switch (modifiers & kModifierKeys) { case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; table = B_SHIFT_TABLE; break; case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; table = B_CAPS_TABLE; break; case B_CAPS_LOCK | B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; table = B_CAPS_SHIFT_TABLE; break; case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; table = B_CONTROL_TABLE; break; case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; table = B_OPTION_TABLE; break; case B_OPTION_KEY | B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; table = B_OPTION_SHIFT_TABLE; break; case B_OPTION_KEY | B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; table = B_OPTION_CAPS_TABLE; break; case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; table = B_OPTION_CAPS_SHIFT_TABLE; break; default: offset = fKeys.normal_map[keyCode]; table = B_NORMAL_TABLE; break; } if (_table != NULL) *_table = table; if (offset >= (int32)fCharsSize) return -1; return offset; } uint8 BKeymap::DeadKeyIndex(int32 offset) const { if (fChars == NULL || offset <= 0) return 0; uint32 numBytes = fChars[offset]; if (!numBytes || numBytes > 4) return 0; char chars[5]; strncpy(chars, &fChars[offset + 1], numBytes); chars[numBytes] = 0; const int32 deadOffsets[] = { fKeys.acute_dead_key[1], fKeys.grave_dead_key[1], fKeys.circumflex_dead_key[1], fKeys.dieresis_dead_key[1], fKeys.tilde_dead_key[1] }; uint8 result = 0; for (int32 i = 0; i < 5; i++) { if (offset == deadOffsets[i]) return i + 1; uint32 deadNumBytes = fChars[deadOffsets[i]]; if (!deadNumBytes) continue; if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0) return i + 1; } return result; }