Introduction
This multi-language onscreen keyboard is a result of "laziness" to not invent the wheel again. It reads all the keyboard layouts that are available in the Windows-directory and a layout is presented with an Apple / Android / Windows 8 look-a-like keyboard. Deadkeys are yellow and presented on a separate row, and all available keys are shown.
The keyboard has been tested on Windows XP / 2003 server / Vista / 7 / 2008 Server / 8, all operating systems seem to work as expected.
Background
My initial start was to create my own keyboard maps manually, going through each language, using the Microsoft On-screen Keyboard. After I started on my third keyboard map, I got fed up and started searching for solutions to read existing layouts.
In my quest I discovered a bug while loading keyboards on x86/x64 systems, the solution can be found here. The same technique is used in this application. The solution is two different kbd.h versions, one for x86 and one for x64. I wanted to know what caused this problem, so I contacted the Microsoft-keyboard-guru Michael Kaplan but his answer was negative, since this is "undocumented". His thumb down made me even more persistent to actually complete this project, and here we are . Another smart guy "kwhat" made a solution that shows the x86/x64 bit-shifts more clearly, his code is also usable with GNU Compiler Collection (GCC).
(Note: Please share if you know any good APIs for getting the keyboard layout. I'm looking for approaches/API/functions that extract the scan code + character relationship; comment your solution below!)
Scan codes
Scan codes are the actually physical reference for a key on the keyboard, a hex-number. See the picture to get an overview, an English layout uses the 0x10 as "q"-key, the 0x11 is "w", the 0x1E is "a", and so on...
The main work done by this class is to sort out all the characters and link them to the correct scan code. There are three different scan code registers as described in Wiki, Microsoft uses set 1; IBM PC XT.
Using the code
The code is split between the loading of the keyboard layout and the presentation of it.
Loading keyboard DLLs
There is one class that handles the loading of DLL layouts; CKLL
(Keyboard Layout Loader). The function LoadDLL
loads the keyboard layout, and afterwards the function GetCharFromSC
is used to retrieve a VK_CHAR
based on scan code and keyboard-state. The variables returned are:
struct VK_CHAR
{
WCHAR wChar;
OSKState nState;
BOOL bDead;
};
The keyboard states are the variation of common-modifiers:
enum OSK_KEYBOARD_STATES
{
Normal,
Shift,
Alt,
ShiftAlt,
Ctrl,
ShiftCtrl,
CtrlAlt,
ShiftCtrlAlt,
};
Each keyboard layout DLL has a file description that makes a reasonable description of the layout. This info can be retrieved by calling GetKeyboardName()
(only in MFC-version).
Presentation
The presentation is done by a CStatic
derivate called CKeyboardStatic
. It has a subclass called CKeyButton
that contains each character with the scan code, character, and state. Which key goes where is linked to the array OSKScanCodes
, in this version the standard qwerty is used. A change of this table will affect the presentation directly.
const char OSKScanCodes[4][13] = {
{0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D},
{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x00},
{0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x2B, 0x00},
{0x56, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x00, 0x00}
};
The different presentations were made just for fun . The picture below shows the variations of presentations, the function of the keyboard is the same in all three:
By using GDI+ it is possible to create good looking graphics, using AA and the ARGB-coloring, that is the technique used here. The Apple style requires a lot of painting to replicate, but it sure looks the best of all three (in my eyes...) CKeyboardStatic
has these public functions, read comments behind for their descriptions.
void Clear();
BOOL SetKey(...);
void SetKeyboardState(...);
OSKState GetKeyboardState(...);
void SetKeyboardPresentation(...);
Using the CKLL
class you can set the scan code + character, using the SetKey()
to link to the "physical" key, based on the OSKScanCodes
-array. The SetKeyboardPresentation()
changes the characters, based on the OSK_KEYBOARD_STATES
-array value. The SetKeyboardLayout()
changes the presentation of keyboard.
MFC or not to MFC
The CKLL
class comes in two flavors, one MFC version and one MFC-free. The MFC-free CKLL
uses a vector based array to to keep track of scan codes and characters instead of CPtrArray
. CKeyboardStatic
is pure MFC and is not straightforward to convert to other types. Project "ConsoleKeyboard" is a pure Win32 console application and the string handling between CKLL
has changed to wstring
(for this mod). It was made to show off how little code is needed to present a keyboard. See picture below for results:
You have to change the console font to a Unicode supported one, select Lucida Console or Consolas. Right click on header and select "Properties" to change it. The source for getting this presentation is simple (code has been stripped of error handling for easy reading):
int _tmain(int argc, _TCHAR* argv[])
{
_setmode(_fileno(stdout), _O_U16TEXT);
CKLL m_kll;
m_kll.LoadDLL(argv[1]);
OSKState nState = OSK::Normal;
wprintf(L"\n");
for(int nRow = 0; nRow < ( sizeof(OSK::OSKScanCodes) /
sizeof(OSK::OSKScanCodes[0]) ) ; nRow++)
{
wprintf(L"-----------------------------------------------------\n");
for(int nScanCode = 0; nScanCode < (sizeof(OSK::OSKScanCodes[0]) /
sizeof(OSK::OSKScanCodes[0][0])); nScanCode++)
{
if(OSK::OSKScanCodes[nRow][nScanCode] != 0x00)
{
CKLL::VK_CHAR aChar;
if(m_kll.GetCharFromSC(OSK::OSKScanCodes[nRow][nScanCode], nState, aChar))
wprintf(L"| %c ", aChar.wChar);
}
}
wprintf(L"|\n");
}
wprintf(L"-----------------------------------------------------\n");
return 0;
}
By using the CKLL
class you could easily make a multi-language onscreen keyboard for an OpenGL, QT, or any other implementation in Windows.
Issues
Most of the layouts have not been confirmed, and frankly I don't "understand" all of them. Please take time to check your day-to-day keyboard layout and see if the keys are placed in your normal position. The keyboard does not present modifiers and functions keys as shift, control, enter, space, tab etc... I did not use any time to include these so consider the presentation as a proof-of-concept. In some layouts a square is presented as char, that is normally not a Unicode-character, that isn't handled. This may be fixed in a future version. Microsoft keyboard DLLs can have multiple layouts in them, this class does only handle the first item we find.
History
- v1.00 - First public release.