- Download OnScreenKeyboard.zip – 267 KB
- Download ConsoleKeyboard.zip – 55.6 KB
- Download CKLL_-_MFC.zip – 51.1 KB
- Download CKLL_-_Non_MFC.zip – 43.8 KB
Introduction
The multi language onscreen keyboard is a result of "lazyness" to not invent the wheel again.It reads all the keyboard layouts that are available in the windows-directory and layout is presented with a Apple / Android / Windows 8 look-a-like keyboard. Deadkeys are yellow and presented on a seperate row, 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 inital start was to create 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 the 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, here we are 🙂
Another smart guy "kwhat" made another solution that shows the x86/x64 bit-shifts more clear, 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 approache/API/function 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 correct scan code.
There are three different scan codes registers as described in wiki, Microsoft uses set 1; IBM PC XT.
Using the code
There are a split between the loading of a keyboard layout and the presentation of it.
Loading keyboard dlls
There are 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 retrive a VK_CHAR
based on scancode and keyboard-state. The variables returned are:
struct VK_CHAR { WCHAR wChar; //Actual char OSKState nState; //Which state it are in BOOL bDead; //Dead key };
The keyboard states are the variation of common-modifiers:
enum OSK_KEYBOARD_STATES { Normal, //000 Shift, //001 Alt, //010 ShiftAlt, //011 Ctrl, //100 ShiftCtrl, //101 CtrlAlt, //110 ShiftCtrlAlt, //111 };
Each keyboard layout dll has a file description that make a reasonable description of the layout. This info can be retrived 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 contain each character with the scan code, character and state.
Which key goes where are linked to the array OSKScanCodes
, in this version the standard qwerty is used. A change of this table will affect 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 was made just for fun 🙂 The picture below shows the variations of presentations, function of keyboard is equal 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 require much painting to replicate, but it sure looks the best of all three (in my eyes…)
CKeyboardStatic
has these public functions, read comment behind for description.
void Clear(); //Resets the static from all keys BOOL SetKey(...); //Adds a character linked to a scan code + deadkey info void SetKeyboardState(...); //Sets the state the keyboard are in OSKState GetKeyboardState(...); //Returns the state the keyboard are in void SetKeyboardPresentation(...); //Sets the presentation of the static; Apple, Android or Windows 8
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
.
The CKeyboardStatic
is a 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 needed to present an keyboard. See picture below for results:
You have to change the console font to a unicode supported one, select Lucida Console or Consolas.
Rightclick on header and select "Properties" to change it.
The source for getting this presentation is simple (code has been stripped for errorhandling for easy reading):
int _tmain(int argc, _TCHAR* argv[]) { //Set unicode mode _setmode(_fileno(stdout), _O_U16TEXT); //Load the keyboard-dll CKLL m_kll; m_kll.LoadDLL(argv[1]); //Set our state to "Normal" OSKState nState = OSK::Normal; wprintf(L"\n"); //Loop through each row for(int nRow = 0; nRow < ( sizeof(OSK::OSKScanCodes) / sizeof(OSK::OSKScanCodes[0]) ) ; nRow++) { wprintf(L"-----------------------------------------------------\n"); //Loop through each scan code in that row for(int nScanCode = 0; nScanCode < (sizeof(OSK::OSKScanCodes[0]) / sizeof(OSK::OSKScanCodes[0][0])); nScanCode++) { //The non 0x00 are actually keys 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 CKLL
class you could easly make an multilanguage onscreen keyboard for a OpenGL, QT, or any other implementation in Windows.
Issues
Most of the layouts has not been confirm, and frankly I don’t "understand" all of them. Please take time to check your day-by-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 this so concider 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. It will might 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