* Copyright 2004-2011 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Sandor Vroemisse
* Jérôme Duval
* Axel Dörfler, axeld@pinc-software.de.
*/
#include "Keymap.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <ByteOrder.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include <input_globals.h>
static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
| B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
static void
print_key(char* chars, int32 offset, bool last = false)
{
int size = chars[offset++];
switch (size) {
case 0:
fputs("N/A", stdout);
break;
case 1:
fputc(chars[offset], stdout);
break;
default:
{
char* str = new char[size + 1];
strncpy(str, &chars[offset], size);
str[size] = 0;
fputs(str, stdout);
delete[] str;
break;
}
}
if (!last)
fputs("\t", stdout);
}
Keymap::Keymap()
:
fModificationMessage(NULL)
{
}
Keymap::~Keymap()
{
delete fModificationMessage;
}
void
Keymap::SetTarget(BMessenger target, BMessage* modificationMessage)
{
delete fModificationMessage;
fTarget = target;
fModificationMessage = modificationMessage;
}
void
Keymap::SetName(const char* name)
{
strlcpy(fName, name, sizeof(fName));
}
void
Keymap::DumpKeymap()
{
if (fKeys.version != 3)
return;
puts("Key #\tn\ts\tc\to\tos\tC\tCs\tCo\tCos\n");
for (uint8 i = 0; i < 128; i++) {
printf(" 0x%02x\t", i);
print_key(fChars, fKeys.normal_map[i]);
print_key(fChars, fKeys.shift_map[i]);
print_key(fChars, fKeys.control_map[i]);
print_key(fChars, fKeys.option_map[i]);
print_key(fChars, fKeys.option_shift_map[i]);
print_key(fChars, fKeys.caps_map[i]);
print_key(fChars, fKeys.caps_shift_map[i]);
print_key(fChars, fKeys.option_caps_map[i]);
print_key(fChars, fKeys.option_caps_shift_map[i], true);
fputs("\n", stdout);
}
}
status_t
Keymap::Load(const entry_ref& ref)
{
BEntry entry;
status_t status = entry.SetTo(&ref, true);
if (status != B_OK)
return status;
BFile file(&entry, B_READ_ONLY);
status = SetTo(file);
if (status != B_OK)
return status;
ssize_t bytesRead = file.ReadAttr("keymap:name", B_STRING_TYPE, 0, fName,
sizeof(fName));
if (bytesRead > 0)
fName[bytesRead] = '\0';
else
strlcpy(fName, ref.name, sizeof(fName));
return B_OK;
}
status_t
Keymap::Save(const entry_ref& ref)
{
BFile file;
status_t status = file.SetTo(&ref,
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if (status != B_OK) {
printf("error %s\n", strerror(status));
return status;
}
for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
if (bytesWritten < (ssize_t)sizeof(fKeys))
status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
if (status == B_OK) {
fCharsSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
bytesWritten = file.Write(&fCharsSize, sizeof(uint32));
if (bytesWritten < (ssize_t)sizeof(uint32))
status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
}
if (status == B_OK) {
bytesWritten = file.Write(fChars, fCharsSize);
if (bytesWritten < (ssize_t)fCharsSize)
status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
}
if (status == B_OK) {
const BString name(fName);
file.WriteAttrString("keymap:name", &name);
}
return status;
}
status_t
Keymap::SetModifier(uint32 keyCode, uint32 modifier)
{
const uint32 kSingleModifierKeys = B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY
| B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY | B_LEFT_CONTROL_KEY
| B_RIGHT_CONTROL_KEY | B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY;
if ((modifier & kSingleModifierKeys) != 0)
modifier &= kSingleModifierKeys;
else if ((modifier & kModifierKeys) != 0)
modifier &= kModifierKeys;
if (modifier == B_CAPS_LOCK)
fKeys.caps_key = keyCode;
else if (modifier == B_NUM_LOCK)
fKeys.num_key = keyCode;
else if (modifier == B_SCROLL_LOCK)
fKeys.scroll_key = keyCode;
else if (modifier == B_LEFT_SHIFT_KEY)
fKeys.left_shift_key = keyCode;
else if (modifier == B_RIGHT_SHIFT_KEY)
fKeys.right_shift_key = keyCode;
else if (modifier == B_LEFT_COMMAND_KEY)
fKeys.left_command_key = keyCode;
else if (modifier == B_RIGHT_COMMAND_KEY)
fKeys.right_command_key = keyCode;
else if (modifier == B_LEFT_CONTROL_KEY)
fKeys.left_control_key = keyCode;
else if (modifier == B_RIGHT_CONTROL_KEY)
fKeys.right_control_key = keyCode;
else if (modifier == B_LEFT_OPTION_KEY)
fKeys.left_option_key = keyCode;
else if (modifier == B_RIGHT_OPTION_KEY)
fKeys.right_option_key = keyCode;
else if (modifier == B_MENU_KEY)
fKeys.menu_key = keyCode;
else
return B_BAD_VALUE;
if (fModificationMessage != NULL)
fTarget.SendMessage(fModificationMessage);
return B_OK;
}
void
Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled)
{
uint32 tableMask = 0;
int32 offset = Offset(keyCode, modifiers, &tableMask);
uint8 deadKeyIndex = DeadKeyIndex(offset);
if (deadKeyIndex > 0) {
uint32* deadTables[] = {
&fKeys.acute_tables,
&fKeys.grave_tables,
&fKeys.circumflex_tables,
&fKeys.dieresis_tables,
&fKeys.tilde_tables
};
if (enabled)
(*deadTables[deadKeyIndex - 1]) |= tableMask;
else
(*deadTables[deadKeyIndex - 1]) &= ~tableMask;
if (fModificationMessage != NULL)
fTarget.SendMessage(fModificationMessage);
}
}
key with the given index (which is 1..5).
*/
void
Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger)
{
outTrigger = "";
if (deadKeyIndex < 1 || deadKeyIndex > 5)
return;
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]
};
int32 offset = deadOffsets[deadKeyIndex - 1];
if (offset < 0 || offset >= (int32)fCharsSize)
return;
uint32 deadNumBytes = fChars[offset];
if (!deadNumBytes)
return;
outTrigger.SetTo(&fChars[offset + 1], deadNumBytes);
}
with the given index (which is 1..5).
*/
void
Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger)
{
if (deadKeyIndex < 1 || deadKeyIndex > 5)
return;
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]
};
int32 offset = deadOffsets[deadKeyIndex - 1];
if (offset < 0 || offset >= (int32)fCharsSize)
return;
if (_SetChars(offset, trigger.String(), trigger.Length())) {
uint32* deadTables[] = {
&fKeys.acute_tables,
&fKeys.grave_tables,
&fKeys.circumflex_tables,
&fKeys.dieresis_tables,
&fKeys.tilde_tables
};
*deadTables[deadKeyIndex - 1]
= B_NORMAL_TABLE | B_SHIFT_TABLE | B_CONTROL_TABLE | B_OPTION_TABLE
| B_OPTION_SHIFT_TABLE | B_CAPS_TABLE | B_CAPS_SHIFT_TABLE
| B_OPTION_CAPS_TABLE | B_OPTION_CAPS_SHIFT_TABLE;
if (fModificationMessage != NULL)
fTarget.SendMessage(fModificationMessage);
}
}
status_t
Keymap::RestoreSystemDefault()
{
BPath path;
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
if (status != B_OK)
return status;
path.Append("Key_map");
BEntry entry(path.Path());
entry.Remove();
return Use();
}
status_t
Keymap::Use()
{
status_t result = _restore_key_map_();
if (result == B_OK)
set_keyboard_locks(modifiers());
return result;
}
void
Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey,
const char* bytes, int32 numBytes)
{
int32 offset = Offset(keyCode, modifiers);
if (offset < 0)
return;
if (numBytes < 0)
numBytes = strlen(bytes);
if (numBytes > 6)
return;
if (_SetChars(offset, bytes, numBytes)) {
if (fModificationMessage != NULL)
fTarget.SendMessage(fModificationMessage);
}
}
Keymap&
Keymap::operator=(const Keymap& other)
{
if (this == &other)
return *this;
delete[] fChars;
delete fModificationMessage;
fChars = new(std::nothrow) char[other.fCharsSize];
if (fChars != NULL) {
memcpy(fChars, other.fChars, other.fCharsSize);
fCharsSize = other.fCharsSize;
} else
fCharsSize = 0;
memcpy(&fKeys, &other.fKeys, sizeof(key_map));
strlcpy(fName, other.fName, sizeof(fName));
fTarget = other.fTarget;
if (other.fModificationMessage != NULL)
fModificationMessage = new BMessage(*other.fModificationMessage);
return *this;
}
bool
Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes)
{
int32 oldNumBytes = fChars[offset];
if (oldNumBytes == numBytes
&& !memcmp(&fChars[offset + 1], bytes, numBytes)) {
return false;
}
int32 diff = numBytes - oldNumBytes;
if (diff != 0) {
fCharsSize += diff;
if (diff > 0) {
char* chars = new(std::nothrow) char[fCharsSize];
if (chars != NULL) {
memcpy(chars, fChars, offset + oldNumBytes + 1);
memcpy(&chars[offset + 1 + numBytes],
&fChars[offset + 1 + oldNumBytes],
fCharsSize - 2 - offset - diff);
delete[] fChars;
fChars = chars;
} else
return false;
} else if (diff < 0) {
memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes],
fCharsSize - offset - 2 - diff);
}
int32* data = fKeys.control_map;
int32 size = sizeof(fKeys.control_map) / 4 * 9
+ sizeof(fKeys.acute_dead_key) / 4 * 5;
for (int32 i = 0; i < size; i++) {
if (data[i] > offset)
data[i] += diff;
}
}
memcpy(&fChars[offset + 1], bytes, numBytes);
fChars[offset] = numBytes;
return true;
}