Start of a virtual keyboard input_server add-on
Done during GCI 2014. Some files copied from the Keymap preferences.
This commit is contained in:
parent
694be58ab1
commit
857688724e
|
@ -0,0 +1,26 @@
|
|||
SubDir HAIKU_TOP src apps virtualkeyboard ;
|
||||
|
||||
UsePrivateHeaders interface shared ;
|
||||
|
||||
Application VirtualKeyboard :
|
||||
KeyboardLayout.cpp
|
||||
KeyboardLayoutView.cpp
|
||||
Keymap.cpp
|
||||
KeymapListItem.cpp
|
||||
VirtualKeyboardApp.cpp
|
||||
VirtualKeyboardWindow.cpp
|
||||
|
||||
: be tracker localestub libshared.a $(TARGET_LIBSTDC++)
|
||||
: VirtualKeyboard.rdef
|
||||
;
|
||||
|
||||
DoCatalogs Sudoku :
|
||||
x-vnd.Haiku-Sudoku
|
||||
:
|
||||
KeyboardLayout.cpp
|
||||
KeyboardLayoutView.cpp
|
||||
Keymap.cpp
|
||||
KeymapListItem.cpp
|
||||
VirtualKeyboardApp.cpp
|
||||
VirtualKeyboardWindow.cpp
|
||||
;
|
|
@ -0,0 +1,883 @@
|
|||
/*
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
#include <File.h>
|
||||
#include <InterfaceDefs.h>
|
||||
|
||||
|
||||
#undef TRACE
|
||||
|
||||
//#define TRACE_LAYOUT
|
||||
#ifdef TRACE_LAYOUT
|
||||
# define TRACE(x...) printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
KeyboardLayout::KeyboardLayout()
|
||||
:
|
||||
fKeys(NULL),
|
||||
fKeyCount(0),
|
||||
fKeyCapacity(0),
|
||||
fIndicators(5, true),
|
||||
fIsDefault(true)
|
||||
{
|
||||
SetDefault();
|
||||
}
|
||||
|
||||
|
||||
KeyboardLayout::~KeyboardLayout()
|
||||
{
|
||||
free(fKeys);
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
KeyboardLayout::Name()
|
||||
{
|
||||
return fName.String();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
KeyboardLayout::CountKeys()
|
||||
{
|
||||
return fKeyCount;
|
||||
}
|
||||
|
||||
|
||||
Key*
|
||||
KeyboardLayout::KeyAt(int32 index)
|
||||
{
|
||||
if (index < 0 || index >= fKeyCount)
|
||||
return NULL;
|
||||
|
||||
return &fKeys[index];
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
KeyboardLayout::CountIndicators()
|
||||
{
|
||||
return fIndicators.CountItems();
|
||||
}
|
||||
|
||||
|
||||
Indicator*
|
||||
KeyboardLayout::IndicatorAt(int32 index)
|
||||
{
|
||||
return fIndicators.ItemAt(index);
|
||||
}
|
||||
|
||||
|
||||
BRect
|
||||
KeyboardLayout::Bounds()
|
||||
{
|
||||
return fBounds;
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
KeyboardLayout::DefaultKeySize()
|
||||
{
|
||||
return fDefaultKeySize;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
KeyboardLayout::IndexForModifier(int32 modifier)
|
||||
{
|
||||
switch(modifier) {
|
||||
case B_CAPS_LOCK:
|
||||
return 58;
|
||||
case B_NUM_LOCK:
|
||||
return 33;
|
||||
case B_SCROLL_LOCK:
|
||||
return 14;
|
||||
case B_LEFT_SHIFT_KEY:
|
||||
return 74;
|
||||
case B_RIGHT_SHIFT_KEY:
|
||||
return 85;
|
||||
case B_LEFT_CONTROL_KEY:
|
||||
return 91;
|
||||
case B_RIGHT_CONTROL_KEY:
|
||||
return 98;
|
||||
case B_LEFT_OPTION_KEY:
|
||||
return 92;
|
||||
case B_RIGHT_OPTION_KEY:
|
||||
return 96;
|
||||
case B_LEFT_COMMAND_KEY:
|
||||
return 93;
|
||||
case B_RIGHT_COMMAND_KEY:
|
||||
return 95;
|
||||
case B_MENU_KEY:
|
||||
return 97;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
KeyboardLayout::Load(const char* path)
|
||||
{
|
||||
entry_ref ref;
|
||||
get_ref_for_path(path, &ref);
|
||||
|
||||
return Load(ref);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
KeyboardLayout::Load(entry_ref& ref)
|
||||
{
|
||||
BFile file;
|
||||
status_t status = file.SetTo(&ref, B_READ_ONLY);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
off_t size;
|
||||
status = file.GetSize(&size);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (size > 65536) {
|
||||
// We don't accept files larger than this
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
char* data = (char*)malloc(size + 1);
|
||||
if (data == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
ssize_t bytesRead = file.Read(data, size);
|
||||
if (bytesRead != size) {
|
||||
free(data);
|
||||
|
||||
if (bytesRead < 0)
|
||||
return bytesRead;
|
||||
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
data[size] = '\0';
|
||||
|
||||
status = _InitFrom(data);
|
||||
if (status == B_OK)
|
||||
fIsDefault = false;
|
||||
|
||||
free(data);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::SetDefault()
|
||||
{
|
||||
// The keyboard layout description language defines the position,
|
||||
// size, shape, and scancodes of the keys on the keyboard, row by
|
||||
// row.
|
||||
// You can define variables that are substituted in the row
|
||||
// descriptions. Variables are always prefixed with a '$' symbol.
|
||||
|
||||
// On top level, only two different terms are accepted: value pairs,
|
||||
// and row descriptions.
|
||||
// Value pairs are in the form "<name> = <value>". There are only two
|
||||
// predefined names at this moment "name" that specifies the name of
|
||||
// a layout, and "default-size" that specifies the size of keys when
|
||||
// no specific size is given. Also, variables can be specified this
|
||||
// way.
|
||||
|
||||
// Row descriptions are embedded in the '[' and ']' brackets.
|
||||
// The first term within a row is the position of the row, written as
|
||||
// "<x>,<y>;" - the delimiter between terms/keys is usually the
|
||||
// semi-colon. After the initial position, key descriptions are expected
|
||||
// until the row is closed with a ']'.
|
||||
|
||||
// A key description is of the form "<shape>:<scancodes>", where <shape>
|
||||
// is combined of the shape of the character, and its size, written as
|
||||
// "<kind><width>,<height>[,<second-row-width>]".
|
||||
// The kind can either be 'r' (the default, can also be omitted) for
|
||||
// rectangular keys, or 'l' for the enter key. Additionally, you can use
|
||||
// the 'd' character to mark dark keys. The size can be omitted completely,
|
||||
// in which case the defined "default-size" is used. The default size is
|
||||
// also used to determine the height of the first row of the enter key;
|
||||
// the "<second-row-width>" specifier is only valid for enter keys, too.
|
||||
|
||||
// The scancodes can be written in different ways:
|
||||
// 1) "+<count>": adds <count> keys, the scancodes will be used relative
|
||||
// to the previous keys.
|
||||
// 2) "<first>-<last>": keys are added for scancode <first> to scancode
|
||||
// <last>.
|
||||
// 3) "<first>+<count>": one key with the <first> scancode is added, plus
|
||||
// <count> ones with relative scancode from that one.
|
||||
|
||||
// Finally, you can also define LED indicator. Those can be made instead
|
||||
// of a key, it accepts a size, but no shape modifiers. Also, instead of
|
||||
// a scancode, the name of the modifer is given, currently only the
|
||||
// following are allowed: "led-num", "led-caps", and "led-scroll".
|
||||
|
||||
// See for examples in the default layout below.
|
||||
#if 1
|
||||
static const char* kDefaultLayout105 =
|
||||
"name = Generic 105-key International\n"
|
||||
// Size shortcuts
|
||||
"default-size = 10,10\n"
|
||||
"$b = 5,10\n"
|
||||
"$c = 20,10\n"
|
||||
"$d = 15,10\n"
|
||||
"$e = l15,20,9\n"
|
||||
"$f = 10,20\n"
|
||||
"$g = 13,10\n"
|
||||
// Key rows
|
||||
"[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; "
|
||||
"$g:led-num; $g:led-caps; $g:led-scroll ]\n"
|
||||
"[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n"
|
||||
"[ 0,30; d$d:0x26; :+12; d$e:0x47; $b:-; d:0x34-0x36; $b:-; :+3; "
|
||||
"d$f:+1 ]\n"
|
||||
"[ 0,40; d19,10:0x3b; :+11; :0x33; 51,10:-; :0x48-0x4a ]\n"
|
||||
"[ 0,50; d$g:0x4b; :0x69; :0x4c+9; d27,10:+1; 15,10:-; d:+1; "
|
||||
" 15,10:-; :+3; d$f:+1 ]\n"
|
||||
"[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; "
|
||||
"d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n";
|
||||
|
||||
_InitFrom(kDefaultLayout105);
|
||||
#endif
|
||||
#if 0
|
||||
static const char* kDefaultLayout104 = "name = Generic 104-key\n"
|
||||
// Size shortcuts
|
||||
"default-size = 10,10\n"
|
||||
"$b = 5,10\n"
|
||||
"$c = 20,10\n"
|
||||
"$d = 15,10\n"
|
||||
"$enter = d20,10\n"
|
||||
"$f = 10,20\n"
|
||||
"$g = 13,10\n"
|
||||
// Key rows
|
||||
"[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; "
|
||||
"$g:led-num; $g:led-caps; $g:led-scroll ]\n"
|
||||
"[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n"
|
||||
"[ 0,30; d$d:0x26; :+12; $d:+1; $b:-; d:+3; $b:-; :+3; "
|
||||
"d$f:+1 ]\n"
|
||||
"[ 0,40; d$c:0x3b; :+11; $enter:+1; 40,10:-; :+3 ]\n"
|
||||
"[ 0,50; d24,10:0x4b; :+10; d26,10:+1; 15,10:-; d:+1; "
|
||||
" 15,10:-; :+3; d$f:+1 ]\n"
|
||||
"[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; "
|
||||
"d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n";
|
||||
|
||||
_InitFrom(kDefaultLayout104);
|
||||
#endif
|
||||
#if 0
|
||||
static const char* kIBMLaptop = "name = IBM Laptop International\n"
|
||||
// Size shortcuts
|
||||
"default-size = 18,18\n"
|
||||
"$s = 17,10\n"
|
||||
"$gap = 6,10\n"
|
||||
"$sgap = 5,10\n"
|
||||
"$backspace = 38,18\n"
|
||||
"$tab = 28,18\n"
|
||||
"$caps = 32,18\n"
|
||||
"$enter = l28,36,22\n"
|
||||
"$l-shift-ctrl = 23,18\n"
|
||||
"$r-shift = 51,18\n"
|
||||
"$option = 13,18\n"
|
||||
"$space = 95,18\n"
|
||||
// Key rows
|
||||
"[ 0,0; $s:0x01; 148,10:-; $s:0x0e+2; $sgap:-; $s:0x1f+2; ]\n"
|
||||
"[ 0,10; $s:0x02+3; $gap:-; $s:+4; $gap:-; $s:+4; $sgap:-; "
|
||||
"$s:0x34+2; ]\n"
|
||||
"[ 0,20; :0x11+12; $backspace:+1 ]\n"
|
||||
"[ 0,38; $tab:0x26; :+12; $enter:0x47; ]\n"
|
||||
"[ 0,56; $caps:0x3b; :+11; :0x33 ]\n"
|
||||
"[ 0,74; $l-shift-ctrl:0x4b; :0x69; :0x4c+9; $r-shift:+1 ]\n"
|
||||
"[ 0,92; :0x99; $l-shift-ctrl:0x5c; $option:0x66; :0x5d; $space:+1; "
|
||||
":+1; :0x68; :0x60; $s:0x9a; $s:0x57; $s:0x9b ]\n"
|
||||
"[ 221,102; $s:0x61+2; ]\n";
|
||||
|
||||
_InitFrom(kIBMLaptop);
|
||||
#endif
|
||||
fIsDefault = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::_FreeKeys()
|
||||
{
|
||||
free(fKeys);
|
||||
fKeys = NULL;
|
||||
fKeyCount = 0;
|
||||
fKeyCapacity = 0;
|
||||
fBounds = BRect();
|
||||
|
||||
fIndicators.MakeEmpty();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::_Error(const parse_state& state, const char* reason, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, reason);
|
||||
|
||||
fprintf(stderr, "Syntax error in line %" B_PRId32 ": ", state.line);
|
||||
vfprintf(stderr, reason, args);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::_AddAlternateKeyCode(Key* key, int32 modifier, int32 code)
|
||||
{
|
||||
if (key == NULL)
|
||||
return;
|
||||
|
||||
// TODO: search for free spot
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_AddKey(const Key& key)
|
||||
{
|
||||
TRACE(" add %ld (%g,%g)\n", key.code, key.frame.left, key.frame.top);
|
||||
if (fKeyCount + 1 > fKeyCapacity) {
|
||||
// enlarge array
|
||||
int32 newCapacity = fKeyCapacity + 32;
|
||||
Key* newKeys = (Key*)realloc(fKeys, newCapacity * sizeof(Key));
|
||||
if (newKeys == NULL)
|
||||
return false;
|
||||
|
||||
fKeys = newKeys;
|
||||
fKeyCapacity = newCapacity;
|
||||
}
|
||||
|
||||
fKeys[fKeyCount] = key;
|
||||
fKeyCount++;
|
||||
|
||||
fBounds = key.frame | fBounds;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::_SkipCommentsAndSpace(parse_state& state, const char*& data)
|
||||
{
|
||||
while (data[0] != '\0') {
|
||||
while (isspace(data[0])) {
|
||||
if (data[0] == '\n')
|
||||
state.line++;
|
||||
data++;
|
||||
}
|
||||
|
||||
if (data[0] == '#') {
|
||||
// skip comment
|
||||
while (data[0] != '\0' && data[0] != '\n') {
|
||||
data++;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
KeyboardLayout::_Trim(BString& string, bool stripComments)
|
||||
{
|
||||
// Strip leading spaces
|
||||
int32 i = 0;
|
||||
while (isspace(string[i])) {
|
||||
i++;
|
||||
}
|
||||
if (i > 0)
|
||||
string.Remove(0, i);
|
||||
|
||||
// Remove comments
|
||||
if (stripComments) {
|
||||
i = string.FindFirst('#');
|
||||
if (i >= 0)
|
||||
string.Truncate(i);
|
||||
}
|
||||
|
||||
// Strip trailing spaces
|
||||
i = string.Length() - 1;
|
||||
while (i > 0 && isspace(string[i])) {
|
||||
i--;
|
||||
}
|
||||
string.Truncate(i + 1);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_GetPair(const parse_state& state, const char*& data,
|
||||
BString& name, BString& value)
|
||||
{
|
||||
// Get name
|
||||
name = "";
|
||||
while (data[0] != '\0' && data[0] != '=') {
|
||||
name += data[0];
|
||||
data++;
|
||||
}
|
||||
|
||||
if (data[0] != '=') {
|
||||
_Error(state, "no valid pair");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip sign
|
||||
data++;
|
||||
|
||||
// Get value
|
||||
value = "";
|
||||
while (data[0] != '\0' && data[0] != '\n') {
|
||||
value += data[0];
|
||||
data++;
|
||||
}
|
||||
|
||||
_Trim(name, false);
|
||||
_Trim(value, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_AddKeyCodes(const parse_state& state, BPoint& rowLeftTop,
|
||||
Key& key, const char* data, int32& lastCount)
|
||||
{
|
||||
if (data[0] == '-') {
|
||||
// no key, just free space
|
||||
int32 num = strtoul(data + 1, NULL, 0);
|
||||
if (num < 1)
|
||||
num = 1;
|
||||
else if (num > 32) {
|
||||
_Error(state, "empty key count too large");
|
||||
return false;
|
||||
}
|
||||
|
||||
key.frame.OffsetTo(rowLeftTop);
|
||||
rowLeftTop.x = key.frame.left + key.frame.Width() * num;
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 modifier = 0;
|
||||
|
||||
if (isalpha(data[0])) {
|
||||
bool led = false;
|
||||
if (!strcmp("led-caps", data)) {
|
||||
modifier = B_CAPS_LOCK;
|
||||
led = true;
|
||||
} else if (!strcmp("led-num", data)) {
|
||||
modifier = B_NUM_LOCK;
|
||||
led = true;
|
||||
} else if (!strcmp("led-scroll", data)) {
|
||||
modifier = B_SCROLL_LOCK;
|
||||
led = true;
|
||||
} else {
|
||||
// TODO: get modifier (ie. "num")
|
||||
}
|
||||
|
||||
if (led) {
|
||||
key.frame.OffsetTo(rowLeftTop);
|
||||
rowLeftTop.x = key.frame.right;
|
||||
fBounds = key.frame | fBounds;
|
||||
|
||||
Indicator* indicator = new(std::nothrow) Indicator;
|
||||
if (indicator != NULL) {
|
||||
indicator->modifier = modifier;
|
||||
indicator->frame = key.frame;
|
||||
|
||||
fIndicators.AddItem(indicator);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int32 first;
|
||||
int32 last;
|
||||
int32 num = 1;
|
||||
|
||||
if (data[0] == '+') {
|
||||
num = strtoul(data + 1, NULL, 0);
|
||||
if (num < 1)
|
||||
num = 1;
|
||||
else if (num > 32) {
|
||||
_Error(state, "key count too large");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fKeyCount > 0)
|
||||
first = fKeys[fKeyCount - 1].code + 1;
|
||||
else
|
||||
first = 1;
|
||||
|
||||
last = first + num - 1;
|
||||
} else {
|
||||
char* end;
|
||||
first = strtoul(data, &end, 0);
|
||||
last = first;
|
||||
|
||||
if (end[0] == '-') {
|
||||
last = strtoul(end + 1, NULL, 0);
|
||||
if (first > last) {
|
||||
_Error(state, "invalid key code specifier");
|
||||
return false;
|
||||
}
|
||||
|
||||
num = last - first;
|
||||
} else if (end[0] == '+') {
|
||||
num = strtoul(end + 1, NULL, 0) + 1;
|
||||
last = first + num - 1;
|
||||
} else if (end[0] != '\0') {
|
||||
_Error(state, "invalid key range");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastCount != 0) {
|
||||
// update existing keys
|
||||
if (lastCount != num) {
|
||||
_Error(state, "modifier key mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = fKeyCount - num; i < fKeyCount; i++, first++) {
|
||||
Key* key = KeyAt(i);
|
||||
|
||||
_AddAlternateKeyCode(key, modifier, first);
|
||||
}
|
||||
} else {
|
||||
// add new keys
|
||||
for (int32 i = first; i <= last; i++) {
|
||||
key.code = i;
|
||||
|
||||
// "layout"
|
||||
key.frame.OffsetTo(rowLeftTop);
|
||||
rowLeftTop.x = key.frame.right;
|
||||
|
||||
_AddKey(key);
|
||||
}
|
||||
|
||||
lastCount = num;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_GetSize(const parse_state& state, const char* data,
|
||||
float& x, float& y, float* _secondRow)
|
||||
{
|
||||
if (data[0] == '\0') {
|
||||
// default size
|
||||
x = fDefaultKeySize.width;
|
||||
y = fDefaultKeySize.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
float secondRow = 0;
|
||||
int num = sscanf(data, "%g,%g,%g", &x, &y, &secondRow);
|
||||
if (num < 2) {
|
||||
_Error(state, "invalid size");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_secondRow != NULL)
|
||||
*_secondRow = secondRow;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_GetShape(const parse_state& state, const char* data, Key& key)
|
||||
{
|
||||
// the default
|
||||
key.shape = kRectangleKeyShape;
|
||||
key.dark = false;
|
||||
|
||||
while (isalpha(data[0])) {
|
||||
switch (tolower(data[0])) {
|
||||
case 'r':
|
||||
key.shape = kRectangleKeyShape;
|
||||
break;
|
||||
case 'c':
|
||||
key.shape = kCircleKeyShape;
|
||||
break;
|
||||
case 'l':
|
||||
key.shape = kEnterKeyShape;
|
||||
break;
|
||||
case 'd':
|
||||
key.dark = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
_Error(state, "unknown shape specifier '%c'", data[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
data++;
|
||||
}
|
||||
|
||||
float width, height;
|
||||
if (!_GetSize(state, data, width, height, &key.second_row))
|
||||
return false;
|
||||
|
||||
// don't accept second row with anything but kEnterKeyShape
|
||||
if ((key.shape != kEnterKeyShape && key.second_row != 0)
|
||||
|| (key.shape == kEnterKeyShape && key.second_row == 0)) {
|
||||
_Error(state, "shape size mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
key.frame.left = 0;
|
||||
key.frame.top = 0;
|
||||
key.frame.right = width;
|
||||
key.frame.bottom = height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the term delimiter expected in a certain parse mode. */
|
||||
const char*
|
||||
KeyboardLayout::_Delimiter(parse_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
default:
|
||||
case kSize:
|
||||
return "";
|
||||
case kRowStart:
|
||||
return ";";
|
||||
|
||||
case kKeyShape:
|
||||
return ":";
|
||||
case kKeyCodes:
|
||||
return ";:";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_GetTerm(const char*& data, const char* delimiter,
|
||||
BString& term, bool closingBracketAllowed)
|
||||
{
|
||||
// Get term
|
||||
term = "";
|
||||
while (data[0] != '\0' && strchr(delimiter, data[0]) == NULL
|
||||
&& data[0] != '\n' && data[0] != '#'
|
||||
&& (!closingBracketAllowed || data[0] != ']')) {
|
||||
term += data[0];
|
||||
data++;
|
||||
}
|
||||
|
||||
if (data[0] == '\0' && delimiter[0])
|
||||
return false;
|
||||
|
||||
_Trim(term, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_SubstituteVariables(BString& term, VariableMap& variables,
|
||||
BString& unknown)
|
||||
{
|
||||
while (true) {
|
||||
int32 index = term.FindFirst('$');
|
||||
if (index < 0)
|
||||
break;
|
||||
|
||||
// find variable name
|
||||
|
||||
VariableMap::iterator iterator = variables.begin();
|
||||
VariableMap::iterator best = variables.end();
|
||||
int32 bestLength = 0;
|
||||
|
||||
for (; iterator != variables.end(); iterator++) {
|
||||
const BString& name = iterator->first;
|
||||
if (!name.Compare(&term[index], name.Length())
|
||||
&& name.Length() > bestLength) {
|
||||
best = iterator;
|
||||
bestLength = name.Length();
|
||||
}
|
||||
}
|
||||
|
||||
if (best != variables.end()) {
|
||||
// got one, replace it
|
||||
term.Remove(index, bestLength);
|
||||
term.Insert(best->second.String(), index);
|
||||
} else {
|
||||
// variable has not been found
|
||||
unknown = &term[index];
|
||||
int32 length = 1;
|
||||
while (isalpha(unknown[length])) {
|
||||
length++;
|
||||
}
|
||||
unknown.Truncate(length);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
KeyboardLayout::_ParseTerm(const parse_state& state, const char*& data,
|
||||
BString& term, VariableMap& variables)
|
||||
{
|
||||
if (!_GetTerm(data, _Delimiter(state.mode), term,
|
||||
state.mode == kKeyCodes)) {
|
||||
_Error(state, state.mode == kRowStart
|
||||
? "no valid row start" : "invalid term");
|
||||
return false;
|
||||
}
|
||||
|
||||
BString unknown;
|
||||
if (!_SubstituteVariables(term, variables, unknown)) {
|
||||
_Error(state, "Unknown variable \"%s\"", unknown.String());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*! Initializes the keyboard layout from the data given.
|
||||
The string has to be a valid keyboard layout description, otherwise
|
||||
an error is returned.
|
||||
*/
|
||||
status_t
|
||||
KeyboardLayout::_InitFrom(const char* data)
|
||||
{
|
||||
_FreeKeys();
|
||||
|
||||
VariableMap variables;
|
||||
BPoint rowLeftTop;
|
||||
int32 lastKeyCount = 0;
|
||||
Key key;
|
||||
|
||||
parse_state state = {kPairs, 1};
|
||||
|
||||
while (data[0] != '\0') {
|
||||
_SkipCommentsAndSpace(state, data);
|
||||
|
||||
if (data[0] == '[') {
|
||||
state.mode = kRowStart;
|
||||
|
||||
rowLeftTop = BPoint(0, 0);
|
||||
data++;
|
||||
continue;
|
||||
} else if (data[0] == '\0')
|
||||
break;
|
||||
|
||||
switch (state.mode) {
|
||||
case kPairs:
|
||||
{
|
||||
BString name;
|
||||
BString value;
|
||||
if (!_GetPair(state, data, name, value))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE("<%s> = <%s>\n", name.String(), value.String());
|
||||
if (name == "name")
|
||||
fName = value;
|
||||
else if (name == "default-size") {
|
||||
const char* valueString = value.String();
|
||||
parse_state tempState = {kSize, state.line};
|
||||
BString term;
|
||||
if (!_ParseTerm(tempState, valueString, term, variables))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE(" size = %s\n", term.String());
|
||||
if (!_GetSize(state, term.String(), fDefaultKeySize.width,
|
||||
fDefaultKeySize.height))
|
||||
return B_BAD_VALUE;
|
||||
} else if (name[0] == '$')
|
||||
variables[name] = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case kRowStart:
|
||||
case kKeyShape:
|
||||
case kKeyCodes:
|
||||
{
|
||||
if (data[0] == ']') {
|
||||
if (state.mode == kKeyShape) {
|
||||
state.mode = kPairs;
|
||||
data++;
|
||||
continue;
|
||||
}
|
||||
_Error(state, "unexpected row closing bracket");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
BString term;
|
||||
if (!_ParseTerm(state, data, term, variables))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
switch (state.mode) {
|
||||
case kRowStart:
|
||||
if (!_GetSize(state, term.String(), rowLeftTop.x,
|
||||
rowLeftTop.y))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE("row: %s (%g:%g)\n", term.String(), rowLeftTop.x,
|
||||
rowLeftTop.y);
|
||||
|
||||
state.mode = kKeyShape;
|
||||
break;
|
||||
case kKeyShape:
|
||||
memset(&key, 0, sizeof(Key));
|
||||
if (!_GetShape(state, term.String(), key))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE(" shape: %s (%g:%g:%g)\n", term.String(),
|
||||
key.frame.Width(), key.frame.Height(),
|
||||
key.second_row);
|
||||
|
||||
lastKeyCount = 0;
|
||||
state.mode = kKeyCodes;
|
||||
break;
|
||||
case kKeyCodes:
|
||||
TRACE(" raw key: %s\n", term.String());
|
||||
|
||||
if (!_AddKeyCodes(state, rowLeftTop, key, term.String(),
|
||||
lastKeyCount))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (data[0] != ':')
|
||||
state.mode = kKeyShape;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (data[0] != ']' && data[0] != '\0')
|
||||
data++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KEYBOARD_LAYOUT_H
|
||||
#define KEYBOARD_LAYOUT_H
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <ObjectList.h>
|
||||
#include <Point.h>
|
||||
#include <Rect.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
enum key_shape {
|
||||
kRectangleKeyShape,
|
||||
kCircleKeyShape,
|
||||
kEnterKeyShape
|
||||
};
|
||||
|
||||
struct Key {
|
||||
int32 code;
|
||||
int32 alternate_code[3];
|
||||
int32 alternate_modifier[3];
|
||||
key_shape shape;
|
||||
BRect frame;
|
||||
float second_row;
|
||||
// this is the width of the second row of a kEnterKeyShape key
|
||||
bool dark;
|
||||
};
|
||||
|
||||
struct Indicator {
|
||||
int32 modifier;
|
||||
BRect frame;
|
||||
};
|
||||
|
||||
typedef std::map<BString, BString> VariableMap;
|
||||
|
||||
class KeyboardLayout {
|
||||
public:
|
||||
KeyboardLayout();
|
||||
~KeyboardLayout();
|
||||
|
||||
const char* Name();
|
||||
|
||||
int32 CountKeys();
|
||||
Key* KeyAt(int32 index);
|
||||
|
||||
int32 CountIndicators();
|
||||
Indicator* IndicatorAt(int32 index);
|
||||
|
||||
BRect Bounds();
|
||||
BSize DefaultKeySize();
|
||||
int32 IndexForModifier(int32 modifier);
|
||||
|
||||
status_t Load(const char* path);
|
||||
status_t Load(entry_ref& ref);
|
||||
|
||||
void SetDefault();
|
||||
bool IsDefault() const { return fIsDefault; }
|
||||
|
||||
private:
|
||||
enum parse_mode {
|
||||
kPairs,
|
||||
kSize,
|
||||
kRowStart,
|
||||
kKeyShape,
|
||||
kKeyCodes
|
||||
};
|
||||
struct parse_state {
|
||||
parse_mode mode;
|
||||
int32 line;
|
||||
};
|
||||
|
||||
void _FreeKeys();
|
||||
void _Error(const parse_state& state,
|
||||
const char* reason, ...);
|
||||
void _AddAlternateKeyCode(Key* key, int32 modifier,
|
||||
int32 code);
|
||||
bool _AddKey(const Key& key);
|
||||
void _SkipCommentsAndSpace(parse_state& state,
|
||||
const char*& data);
|
||||
void _Trim(BString& string, bool stripComments);
|
||||
bool _GetPair(const parse_state& state,
|
||||
const char*& data, BString& name,
|
||||
BString& value);
|
||||
bool _AddKeyCodes(const parse_state& state,
|
||||
BPoint& rowLeftTop, Key& key, const char* data,
|
||||
int32& lastCount);
|
||||
bool _GetSize(const parse_state& state, const char* data,
|
||||
float& x, float& y, float* _secondRow = NULL);
|
||||
bool _GetShape(const parse_state& state,
|
||||
const char* data, Key& key);
|
||||
const char* _Delimiter(parse_mode mode);
|
||||
bool _GetTerm(const char*& data, const char* delimiter,
|
||||
BString& term, bool closingBracketAllowed);
|
||||
bool _SubstituteVariables(BString& term,
|
||||
VariableMap& variables, BString& unknown);
|
||||
bool _ParseTerm(const parse_state& state,
|
||||
const char*& data, BString& term,
|
||||
VariableMap& variables);
|
||||
|
||||
status_t _InitFrom(const char* data);
|
||||
|
||||
BString fName;
|
||||
Key* fKeys;
|
||||
int32 fKeyCount;
|
||||
int32 fKeyCapacity;
|
||||
BRect fBounds;
|
||||
BSize fDefaultKeySize;
|
||||
int32 fAlternateIndex[3];
|
||||
BObjectList<Indicator> fIndicators;
|
||||
bool fIsDefault;
|
||||
};
|
||||
|
||||
#endif // KEYBOARD_LAYOUT_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KEYBOARD_LAYOUT_VIEW_H
|
||||
#define KEYBOARD_LAYOUT_VIEW_H
|
||||
|
||||
|
||||
#include <Messenger.h>
|
||||
#include <View.h>
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
|
||||
class Keymap;
|
||||
|
||||
|
||||
class KeyboardLayoutView : public BView {
|
||||
public:
|
||||
KeyboardLayoutView(const char* name);
|
||||
~KeyboardLayoutView();
|
||||
|
||||
void SetKeyboardLayout(KeyboardLayout* layout);
|
||||
void SetKeymap(Keymap* keymap);
|
||||
void SetTarget(BMessenger target);
|
||||
|
||||
KeyboardLayout* GetKeyboardLayout() { return fLayout; }
|
||||
|
||||
void SetBaseFont(const BFont& font);
|
||||
|
||||
void SetEditable(bool editable);
|
||||
|
||||
protected:
|
||||
virtual void AttachedToWindow();
|
||||
virtual void FrameResized(float width, float height);
|
||||
virtual BSize MinSize();
|
||||
|
||||
virtual void KeyDown(const char* bytes, int32 numBytes);
|
||||
virtual void KeyUp(const char* bytes, int32 numBytes);
|
||||
virtual void MouseDown(BPoint point);
|
||||
virtual void MouseUp(BPoint point);
|
||||
virtual void MouseMoved(BPoint point, uint32 transit,
|
||||
const BMessage* dragMessage);
|
||||
|
||||
virtual void Draw(BRect updateRect);
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
virtual void WindowActivated(bool active);
|
||||
|
||||
private:
|
||||
enum key_kind {
|
||||
kNormalKey,
|
||||
kSpecialKey,
|
||||
kSymbolKey,
|
||||
kIndicator
|
||||
};
|
||||
|
||||
void _InitOffscreen();
|
||||
void _LayoutKeyboard();
|
||||
void _DrawKeyButton(BView* view, BRect& rect,
|
||||
BRect updateRect, rgb_color base,
|
||||
rgb_color background, bool pressed);
|
||||
void _DrawKey(BView* view, BRect updateRect,
|
||||
const Key* key, BRect frame, bool pressed);
|
||||
void _DrawIndicator(BView* view, BRect updateRect,
|
||||
const Indicator* indicator, BRect rect,
|
||||
bool lit);
|
||||
const char* _SpecialKeyLabel(const key_map& map,
|
||||
uint32 code, bool abbreviated = false);
|
||||
const char* _SpecialMappedKeySymbol(const char* bytes,
|
||||
size_t numBytes);
|
||||
const char* _SpecialMappedKeyLabel(const char* bytes,
|
||||
size_t numBytes, bool abbreviated = false);
|
||||
bool _FunctionKeyLabel(uint32 code, char* text,
|
||||
size_t textSize);
|
||||
void _GetAbbreviatedKeyLabelIfNeeded(BView* view,
|
||||
BRect rect, const Key* key, char* text,
|
||||
size_t textSize);
|
||||
void _GetKeyLabel(const Key* key, char* text,
|
||||
size_t textSize, key_kind& keyKind);
|
||||
bool _IsKeyPressed(uint32 code);
|
||||
bool _KeyState(uint32 code) const;
|
||||
void _SetKeyState(uint32 code, bool pressed);
|
||||
Key* _KeyForCode(uint32 code);
|
||||
void _InvalidateKey(uint32 code);
|
||||
void _InvalidateKey(const Key* key);
|
||||
bool _HandleDeadKey(uint32 key, int32 modifiers);
|
||||
void _KeyChanged(const BMessage* message);
|
||||
Key* _KeyAt(BPoint point);
|
||||
BRect _FrameFor(BRect keyFrame);
|
||||
BRect _FrameFor(const Key* key);
|
||||
void _SetFontSize(BView* view, key_kind keyKind);
|
||||
void _EvaluateDropTarget(BPoint point);
|
||||
void _SendFakeKeyDown(const Key* key);
|
||||
|
||||
BBitmap* fOffscreenBitmap;
|
||||
BView* fOffscreenView;
|
||||
|
||||
KeyboardLayout* fLayout;
|
||||
Keymap* fKeymap;
|
||||
BMessenger fTarget;
|
||||
bool fEditable;
|
||||
|
||||
uint8 fKeyState[16];
|
||||
int32 fModifiers;
|
||||
int32 fDeadKey;
|
||||
int32 fButtons;
|
||||
|
||||
BPoint fClickPoint;
|
||||
Key* fDragKey;
|
||||
int32 fDragModifiers;
|
||||
Key* fDropTarget;
|
||||
BPoint fDropPoint;
|
||||
|
||||
BSize fOldSize;
|
||||
BFont fBaseFont;
|
||||
BFont fSpecialFont;
|
||||
float fBaseFontHeight;
|
||||
float fBaseFontSize;
|
||||
BPoint fOffset;
|
||||
float fFactor;
|
||||
float fGap;
|
||||
};
|
||||
|
||||
|
||||
#endif // KEYBOARD_LAYOUT_VIEW_H
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* 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:
|
||||
// Not mapped
|
||||
fputs("N/A", stdout);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// single-byte UTF-8/ASCII character
|
||||
fputc(chars[offset], stdout);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// 2-, 3-, or 4-byte UTF-8 character
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// Print a chart of the normal, shift, control, option, option+shift,
|
||||
// Caps, Caps+shift, Caps+option, and Caps+option+shift keys.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//! Load a map from a file
|
||||
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;
|
||||
|
||||
// fetch name from attribute and fall back to filename
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//! We save a map to a file
|
||||
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) {
|
||||
file.WriteAttr("keymap:name", B_STRING_TYPE, 0, fName, strlen(fName));
|
||||
// Failing would be non-fatal
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//! Enables/disables the "deadness" of the given keycode/modifier combo.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the trigger character string that is currently set for the dead
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*! Sets the trigger character string that shall be used for the dead key
|
||||
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())) {
|
||||
// reset modifier table such that new dead key is enabled wherever
|
||||
// it is available
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
//! We make our input server use the map in /boot/home/config/settings/Keymap
|
||||
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 == -1)
|
||||
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)) {
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 diff = numBytes - oldNumBytes;
|
||||
if (diff != 0) {
|
||||
fCharsSize += diff;
|
||||
|
||||
if (diff > 0) {
|
||||
// make space for the new data
|
||||
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) {
|
||||
// shrink table
|
||||
memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes],
|
||||
fCharsSize - offset - 2 - diff);
|
||||
}
|
||||
|
||||
// update offsets
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2004-2011 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.
|
||||
*/
|
||||
#ifndef KEYMAP_H
|
||||
#define KEYMAP_H
|
||||
|
||||
|
||||
#include <Keymap.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <Messenger.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
enum dead_key_index {
|
||||
kDeadKeyAcute = 1,
|
||||
kDeadKeyGrave,
|
||||
kDeadKeyCircumflex,
|
||||
kDeadKeyDiaeresis,
|
||||
kDeadKeyTilde
|
||||
};
|
||||
|
||||
|
||||
class Keymap : public BKeymap {
|
||||
public:
|
||||
Keymap();
|
||||
~Keymap();
|
||||
|
||||
void SetTarget(BMessenger target,
|
||||
BMessage* modificationMessage);
|
||||
|
||||
status_t Load(const entry_ref& ref);
|
||||
status_t Save(const entry_ref& ref);
|
||||
|
||||
void DumpKeymap();
|
||||
|
||||
status_t SetModifier(uint32 keyCode, uint32 modifier);
|
||||
|
||||
void SetDeadKeyEnabled(uint32 keyCode,
|
||||
uint32 modifiers, bool enabled);
|
||||
void GetDeadKeyTrigger(dead_key_index deadKeyIndex,
|
||||
BString& outTrigger);
|
||||
void SetDeadKeyTrigger(dead_key_index deadKeyIndex,
|
||||
const BString& trigger);
|
||||
|
||||
status_t RestoreSystemDefault();
|
||||
status_t Use();
|
||||
|
||||
void SetKey(uint32 keyCode, uint32 modifiers,
|
||||
int8 deadKey, const char* bytes,
|
||||
int32 numBytes = -1);
|
||||
|
||||
void SetName(const char* name);
|
||||
|
||||
const key_map& Map() const { return fKeys; }
|
||||
key_map& Map() { return fKeys; }
|
||||
|
||||
Keymap& operator=(const Keymap& other);
|
||||
|
||||
private:
|
||||
bool _SetChars(int32 offset, const char* bytes,
|
||||
int32 numBytes);
|
||||
|
||||
private:
|
||||
char fName[B_FILE_NAME_LENGTH];
|
||||
|
||||
BMessenger fTarget;
|
||||
BMessage* fModificationMessage;
|
||||
};
|
||||
|
||||
|
||||
#endif // KEYMAP_H
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2004-2006 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Sandor Vroemisse
|
||||
* Jérôme Duval
|
||||
*/
|
||||
|
||||
/*
|
||||
* A BStringItem modified such that it holds
|
||||
* the BEntry object it corresponds with
|
||||
*/
|
||||
|
||||
#include "KeymapListItem.h"
|
||||
|
||||
|
||||
KeymapListItem::KeymapListItem(entry_ref& keymap, const char* name)
|
||||
:
|
||||
BStringItem(name ? name : keymap.name),
|
||||
fKeymap(keymap)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2004-2009 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Sandor Vroemisse
|
||||
* Jérôme Duval
|
||||
*/
|
||||
#ifndef KEYMAP_LIST_ITEM_H
|
||||
#define KEYMAP_LIST_ITEM_H
|
||||
|
||||
|
||||
#include <ListItem.h>
|
||||
#include <Entry.h>
|
||||
|
||||
|
||||
/*! A BStringItem modified such that it holds
|
||||
the BEntry object it corresponds with
|
||||
*/
|
||||
class KeymapListItem : public BStringItem {
|
||||
public:
|
||||
KeymapListItem(entry_ref& keymap,
|
||||
const char* name = NULL);
|
||||
|
||||
entry_ref& EntryRef() { return fKeymap; };
|
||||
|
||||
protected:
|
||||
entry_ref fKeymap;
|
||||
};
|
||||
|
||||
#endif // KEYMAP_LIST_ITEM_H
|
|
@ -0,0 +1,38 @@
|
|||
resource app_signature "application/x-vnd.VirtualKeyboard";
|
||||
|
||||
resource app_name_catalog_entry "x-vnd.VirtualKeyboard:System name:VirtualKeyboard";
|
||||
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
|
||||
variety = 1,
|
||||
internal = 0,
|
||||
|
||||
short_info = "VirtualKeybaord",
|
||||
long_info = "An on-screen keyboard for Haiku"
|
||||
};
|
||||
|
||||
resource vector_icon {
|
||||
$"6E63696605020016023910813A2974BB43733A501C4B20D94AA7F400E9FFB902"
|
||||
$"001602B82588B66C3639EE80BB7BCD4B41FC4B6A3B0066FF9002011603BC39E2"
|
||||
$"364286B516B5BB0AE64B8EF94A7E0500FF56E5FFC1050004006D0E0A04525E5C"
|
||||
$"CAE766C6EB5E4B0A0442544648524E525C0A04525C524E5C445E500A04464852"
|
||||
$"4E5C44503E0A02B8DABA5FB913BA320A02BA25B92DB942BA0F0803B70BBCFDB8"
|
||||
$"C7BB84B7BBBB82000CC151BD52C151BD52C151BD52C0B0BDBEC0B0BDBEC105BD"
|
||||
$"83C0EEBD21C116BD40C0C6BD01C00CBD38C022BD28BFCCBD68BFEBBDBDBFCBBD"
|
||||
$"A7C016BDDBC08ABDBEC08ABDBEC072BDD0C080BE0CC065BDF9C0ADBE2CC19CBD"
|
||||
$"CEC15EBE0BC20ABD6244BCC9C1A4BCF5C0ABBC64BF88BD14BFD6BCC6BEE4BDB8"
|
||||
$"BFE6BE44BFB33CC052BE81C100BE6EC0E2BE7AC100BE6E0406FA0FBD8FC04CBC"
|
||||
$"96BFB3BD553FBCDC3FBDCE3FBE873FBE3BBF8DBED4BF27BE94BE7ABEDABEB4BE"
|
||||
$"4EBE41BDB5BE94BDCFBE7ABDB5BE940802C63340C74DC0670802C5C6C01FC6D8"
|
||||
$"C0CB0802C791BFECC5F9C0E50802C58DC0A5C712BFA00007C361C29EC361C29E"
|
||||
$"C33FC2BBC39AC332C35FC30EC3D5C357C496C30FC468C33EC4C4C2E1C463C25D"
|
||||
$"C4D4C29EC4D4C29EC537C245C515C26DC558C21DC50AC1AA4EC1DDC4C1C177C4"
|
||||
$"4945C461C1A9C449450E0A030303020130001001178322040A0001012000100A"
|
||||
$"0101022000100A0201032000100A030303020130101801178322040A00010120"
|
||||
$"10180A0101022010180A0201032010180A040100000A03030302011001178322"
|
||||
$"040A000101000A010102000A020103000A030A0D090A0B0C0807060504100117"
|
||||
$"812204"
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2014 Freeman Lou <freemanlou2430@yahoo.com>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
#include "VirtualKeyboardWindow.h"
|
||||
|
||||
class VirtualKeyboardApp : public BApplication{
|
||||
public:
|
||||
VirtualKeyboardApp();
|
||||
private:
|
||||
VirtualKeyboardWindow* fWindow;
|
||||
};
|
||||
|
||||
|
||||
VirtualKeyboardApp::VirtualKeyboardApp()
|
||||
:
|
||||
BApplication("application/x-vnd.VirtualKeyboard")
|
||||
{
|
||||
fWindow = new VirtualKeyboardWindow();
|
||||
fWindow->Show();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
VirtualKeyboardApp app;
|
||||
app.Run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2014 Freeman Lou <freemanlou2430@yahoo.com>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "VirtualKeyboardWindow.h"
|
||||
|
||||
#include <Screen.h>
|
||||
|
||||
#include "KeyboardLayoutView.h"
|
||||
#include "KeymapListItem.h"
|
||||
#include "Keymap.h"
|
||||
|
||||
VirtualKeyboardWindow::VirtualKeyboardWindow()
|
||||
:
|
||||
BWindow(BRect(0,0,0,0),"Virtual Keyboard",
|
||||
B_NO_BORDER_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL,
|
||||
B_NOT_RESIZABLE | B_WILL_ACCEPT_FIRST_CLICK)
|
||||
{
|
||||
BScreen screen;
|
||||
BRect screenRect(screen.Frame());
|
||||
ResizeTo(screenRect.Width(), screenRect.Height()/3);
|
||||
MoveTo(0,screenRect.Height()-screenRect.Height()/3);
|
||||
|
||||
Keymap keymap;
|
||||
fKeyboardView = new KeyboardLayoutView("Keyboard");
|
||||
fKeyboardView->SetKeymap(&keymap);
|
||||
AddChild(fKeyboardView);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualKeyboardWindow::MessageReceived(BMessage* message)
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2014 Freeman Lou <freemanlou2430@yahoo.com>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef VIRTUAL_KEYBOARD_WINDOW_H
|
||||
#define VIRTUAL_KEYBOARD_WINDOW_H
|
||||
|
||||
#include <Window.h>
|
||||
|
||||
class KeyboardLayoutView;
|
||||
|
||||
|
||||
class VirtualKeyboardWindow : public BWindow{
|
||||
public:
|
||||
VirtualKeyboardWindow();
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
private:
|
||||
KeyboardLayoutView* fKeyboardView;
|
||||
};
|
||||
|
||||
#endif // VIRTUAL_KEYBOARD_WINDOW_H
|
Loading…
Reference in New Issue