f7db27344f
Pointed by clang. Change-Id: I3aaad5b1e03385358ccb729251fa31d35108f389 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2392 Reviewed-by: John Scipione <jscipione@gmail.com> Reviewed-by: Alex von Gluck IV <kallisti5@unixzen.com>
612 lines
13 KiB
C++
612 lines
13 KiB
C++
/*
|
|
* 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 <Keymap.h>
|
|
|
|
#include <new>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <ByteOrder.h>
|
|
#include <File.h>
|
|
|
|
#include <input_globals.h>
|
|
|
|
|
|
#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<const char>* _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;
|
|
}
|