Non-US Keyboard Support for Console Proxy
Originally Cloudstack console proxy supports US keyboard. As time goes on, we add support for Japanese language keyboard. Other types of language keyboard support are added per customer requests. We realize the keyboard support matrix for all the languages can grow quite large and complex, it is necessary to automate this process.
In a cloud system, typical customer deployments involve various browsers, hypervisors, host OSes, and keyboards. Keyboard is not an isolated component. The hypervisors, OSes, browsers all affect the key translation along the way as they each has its own translation.
The combination of these system variables can be quite large. The current infrastructure requires the developers to understand the architecture of the console proxy and write a new keyboard translation table for every new combination.
The new feature is to help automate the language keyboard support so that users and developers can add new support for a non-US keyboard in Cloudstack system without much difficulty. The goal is to make it simpler and easier to add new non-US keyboard support.
1.The journey of a key
When a key is pressed on the user keyboard, before it reaches its destination and displays a key output on the user VM, it passes through the browser the client uses, the client OS, the console proxy, the server side hypervisor, host OS, and displays a key on the VM. Even when console proxy does the key translation correctly according to the standard and keyboard layout, any component in this path can change the key translation, hence it can make the key output at the end user VM incorrect.
Each component, for example, the OS, usually has its own key translation mechanism. With all the components’ own key translation involved, it adds complication to the console proxy key translation. In the end, the correct key translation for the console proxy is to make the correct key gets displayed at the end user VM.
For each different language keyboard, the main engine to be changed is the keyboard key translation rules and tables. There are ten parameters affecting which keyboard translation table to deploy. Five parameters are on server side, which include server OS, hypervisor, and keyboard. Server can obtain this information from the ManagementServer (MS) in Cloudstack.
Parameters on server side:
There are five parameters on client side:
2. Console proxy language keyboard translation
At present, if anyone needs to add a new keyboard support for a language, he needs to spend time understanding the console proxy code and the various key translation logic and rules. Then he can modify a javasript file to add the new language key translation table. However, since this javascript file contains complex logic and rules used for other languages, any change to this file needs to be thought out carefully, otherwise it can cause regression and made some existing keys fail.
To make this process easier, we refactor the console proxy code for keyboard support. Our goal is to save users and developers time to understand the code path, and limit the scope of change as much as possible. Hence it can reduce the possibility of regression and testing time. After the refactor, the majority of keyboard translation rules and logic stay in a file which the users will not need to understand fully and make modifications. Users only need to modify a small table, which has direct input and output mapping, This small file to be modified handles some special keys for the special language keyboard.
For special keyboard such as Japanese keyboard, it is required that the hypervisor, host OS and browser also need to deploy Japanese version.
Even though the console proxy does the translation we expect, the final result at the end may not display the correct key, because of the other components and their own key translations along the way. Hence, all we need to find at the console proxy level is to get the input key translated to a value that eventually display the proper key at the user VM terminal.
2.1 What is input and output key codes
Ajaxviewer.js is a javascript file handles all the keys translations. We refer to this translation ‘T’.
There are two codes users need to be aware of and work with:
Input_keycode: this is the keycode when user presses a key, before any translation. It is related to the key’s physical location on the keyboard;
Output_keycode: this is the keycode sent out by console proxy, to display the correct key on the VM, after all the translations;
T(input_keycode) = output_keycode;
For example, a general ASCII letter a, we may have T(97) = 97; – when the key for ‘a’ is pressed, the input code is 97; when output keycode is 97, letter ‘a’ is displayed on user VM;
2.2 key type events
There are two types of key events: key down/key up, and key press.
There are two main translation schemes: X11 key system and non-X11 key systems. For X11 key system, the translation follows the X11 keys. It handles the key down/key up events, and skip the key press event. For example, a translation for CapsLock key may be like this (we use X11_ prefix to denote the X11 key):
T(KEY_CAPSLOCK) = X11_KEY_CAPSLOCK
For non-X11 key system, it does not use the standard X11 key code. It mainly handles the key press event. We build a translation table to handle the majority of the keys for non-X11.
For the majority of the keys, the logic and rules are set up in ajaxviewer.js. Users do not normally need to modify this file, unless there is a need to change or add any rule there. Users for a special keyboard support need to add entries into the table in ajaxkeys.js. This is a table to handle the special keys which are not covered by the rules and tables in ajaxviewer.js. The rules in ajaxviewer.js handles the default key translation and covers the common keys on the keyboard. It has rules for the special keys. It treats the special keys in a conditional mapping table. For each special keyboard, users just add the entries in the conditional event mapping table in ajaxkeys.js.
Algorithm for the X11 type of keys:
Step 1: if entry exists in the jsX11KeysymMap table in ajaxviewer.js, translate that key to a corresponding X11_key in that table using key down/key up event;
Step 2: if the condition matches, translate the keys in the conditional map table with jsX11KeysymMap;
For non-X11 type of keys: it handles only the keyPress events;
Step 1: for every keyPress event, do the key translation;
Step 2: shall only handles key down/up event if all the conditions are true for a special key in the conditional mapping table.
The conditional mapping table is in a separate file ajaxkeys.js, it has the format for each conditional entry:
Conditional mapping entry { type: <event type>, code: <mapped key code>, modifiers: <modifiers>, shift : <shift state match condition>, -- match on shift state guestos : <guest os match condition>, -- match on guestos type browser: <browser type match condition>, -- match on browser browserVersion: <brower version match condition> -- match on browser version }
For example a match for a particular browser can be like this:
{keycode: 59, entry: X11_KEY_COLON, browser: "Firefox"}, when the browser is Firefox, the rule will apply. Otherwise the rule will not apply.
Browser, type and guestos all have string type.
2.3 find the input_keycode and output_keycode for a particular key
The new translation table in ajaxkeys.js handles the conditional mappings for some special keys. It implements the “T” function: T(input_keycode) = output_keycode.
When a key is pressed, we need to know its input_keycode, so that we can find out if it already has an entry in the table. If it has an entry in the table, but the output is not correct, we need to modify or add a conditional entry to match the current browser and os conditions. If there is no existing entry for this key, we need to add a new entry in the table. So by all means, we need to know its input_keycode.
To find out the Input_keycode, we can turn on the keyboard logger window which will show the keycode for every key pressed. “CTRL-ATL-SHIFT+SPACE” turns on the logger. To have a fresh window to show the code for a new key, clear the window first, and then press a key.
For example, an output of a logger window:
(insert the figure)
However, for the value of output_keycode, it is tricky. It is more of a trial and error process. We can search the keyboard standard key document, and try various values. In the future release, we expect that a script will be added to automatically display the output of a range of output_keycodes on the user VM. From this range of output, we can inspect and find the matching output code for the key. This process can be repeated till the correct output_keycode is found.
Conclusion
With the refactored infrastructure, when we add a new keyboard support, we modify the sjaxkeys.js file. We need to find out the input_keycode and output_keycode for special keys for a new language keyboard and add them in the conditional mapping table with the proper conditions specified. Ajaxviewer.js will parse this file and add them into its main key translation table to support the new keyboard.