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:
Freeman Lou 2014-01-02 03:05:01 +00:00 committed by François Revol
parent 694be58ab1
commit 857688724e
13 changed files with 3046 additions and 0 deletions

View File

@ -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
;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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)
{
}

View File

@ -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

View File

@ -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"
};

View File

@ -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;
}

View File

@ -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)
{
}

View File

@ -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