Applications find out what the user is doing on the keyboard mainly through messages reporting key-down events. An application can usually determine what the user's intent was in pressing a key by looking at the character recorded in the message. But, as discussed under B_KEY_DOWN in the Message Protocols appendix, the message carries other keyboard information in addition to the character--the key the user pressed, the modifier states that were in effect at the time, and the current state of all keys on the keyboard.
Some of this information can be obtained in the absence of key-down messages through two global functions:
This section discusses in detail the kinds of information that you can get about the keyboard through interface messages and these functions.
To talk about the keys on the keyboard, it's necessary first to have a standard way of identifying them. For this purpose, each key is arbitrarily assigned a numerical code.
The illustrations on the next few pages show the key identifiers for typical keyboards. The codes for the main part of a standard 101-key keyboard are shown on page 4. Other keyboards differ primarily in having additional keys in the bottom row. These differences are illustrated on page 5. The codes for the numerical keypad and for the keys between it and the main keyboard are shown beginning on page 6.
Different keyboards locate keys in slightly different positions. The function keys may be to the left of the main keyboard, for example, rather than along the top. The backslash key (0x33) shows up in various places--sometimes above the Enter key, sometimes next to Shift, and sometimes in the top row (as shown here). No matter where these keys are located, they have the codes indicated in the illustration above.
Some keyboards have additional keys, mainly in the bottom row. The keys at the bottom of a Microsoft natural keyboard are coded as follows:
The keys on the bottom row of a 105-key extended keyboard for the Macintosh have the following codes:
The keys on the right of the keyboard are assigned the following codes:
Some of the keys on a Macintosh keyboard have different labels from those shown above, but they have the same key codes. The Macintosh also adds a Power key and an "=" key on the keypad, as shown below:
The "Euro" key on some European keyboards is coded 0x69.
The BMessage that reports a key-down event contains an entry named "key" for the code of the key that was pressed.
Keys on the keyboard can be distinguished by the way they behave and by the kinds of information they provide. A principal distinction is between character keys and modifier keys:
If a key doesn't fall into one of these two categories, there's nothing for it to do; it has no role to play in the interface. For most keys, the categories are mutually exclusive. Modifier keys are typically not mapped to characters, and character keys don't set modifier states. However, the Scroll Lock key is an exception. It both sets a modifier state and generates a character.
Keys can be distinguished on two other grounds as well:
All keys are repeating keys except for Pause, Break, and the three that set locks (Caps Lock, Num Lock, and Scroll Lock). Even modifier keys like Shift and Control would repeat if they were mapped to characters.
Dead keys are dead only when certain prescribed modifiers (by default, just the Option key) are held down. They're most appropriate for situations where the user can imagine a character being composed of two distinguishable parts--such as 'a' and 'e' combining to form 'æ'.
The system permits up to five dead keys. By default, they're reserved for combining diacritical marks with other characters. The diacritical marks are the acute (´) and grave (`) accents, dieresis (¨), circumflex (^), and tilde ().
The system key map determines the role that each key plays--whether it's a character key or a modifier key, which modifier states it sets, which characters it produces, whether it's dead or not, how it combines with other keys, and so on. The map is shared by all applications.
Users can modify the key map with the Keymap utility. Applications can look at it by calling the get_key_map() global function. See that function in the Interface Kit chapter for details on the structure of the map. The discussion here assumes the default key map that comes with the computer.
The role of a modifier key is to set a temporary, modal state. There are eight modifier states--eight different kinds of modifier key--defined functionally. Three of them affect the character that's reported in a key-down event:
Two modifier keys permit users to give the application instructions from the keyboard:
Three modifiers toggle in and out of locked states:
There are two things to note about these eight modifier states. First, since applications can read the modifiers directly from the messages that report key-down events and obtain them at other times by calling the modifiers() and get_key_info() functions, they are free to interpret the modifier states in any way they desire. You're not tied to the narrow interpretation of, say, the Control key given above. Control, Option, and Shift, for example, often modify the meaning of a mouse event or are used to set other temporary modes of behavior.
Second, the set of modifier states listed above doesn't quite match the keys that are marked on a typical keyboard. A standard 101-key keyboard has left and right "Alt(ernate)" keys, but lacks those labeled "Command," "Option," or "Menu."
The key map must, therefore, bend the standard 101-key keyboard to the required modifier states. The default key map does this in three ways:
The illustration below shows the modifier keys on the main keyboard, with labels that match their functional roles. Users can, of course, remap these keys with the Keymap utility. Applications can remap them by calling set_modifier_key().
The extended Macintosh and Microsoft keyboards are mapped to closely match the key caps; all the modifier keys work as you'd expect:
Current modifier states are reported in a mask that can be tested against these constants:
B_SHIFT_KEY | B_COMMAND_KEY | B_CAPS_LOCK |
B_CONTROL_KEY | B_MENU_KEY | B_NUM_LOCK |
B_OPTION_KEY | B_SCROLL_LOCK |
The ..._KEY modifiers are set if the user is holding the key down. The ..._LOCK modifiers are set only if the lock is on--regardless of whether the key that sets the lock happens to be up or down at the time.
If it's important to know which physical key the user is holding down, the one on the right or the one on the left, the mask can be more specifically tested against these constants:
B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY |
B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY |
B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY |
B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY |
If no keyboard locks are on and the user isn't holding a modifier key down, the modifiers mask will be 0.
The modifiers mask is returned by the modifiers() function and, along with the state of all the keys, by get_key_info(). It's also included as a "modifiers" entry in every BMessage that reports a keyboard or mouse event.
The key map records character values using the UTF-8 encoding of the Unicode Standard, making it possible to map keys to characters in any of the world's scripts. UTF-8 encodes 16-bit Unicode values in a variable number of bytes (from one to four).
A B_KEY_DOWN message holds the character mapped to the key the user pressed as an array of bytes named, simply, "byte". The array is passed as a string to KeyDown() along with a count of the number of bytes in the string:
virtual void KeyDown(const char *bytes, int32 numBytes) |
See Character Encoding in the Interface Kit for a description of UTF-8 encoding and get_key_map() for an explanation of the key map.
Most keys are mapped to more than one character. The precise character that the key produces depends on which modifier keys are being held down and which lock states the keyboard is in at the time the key is pressed.
A few examples are given in the table below:
Key | No modifiers | Shift alone | Option alone | Shift & Option | Control |
---|---|---|---|---|---|
0x15 | 4 | $ | ¢ | 4 | |
0x18 | 7 | & | ¶ | § | 7 |
0x26 | B_TAB | B_TAB | B_TAB | B_TAB | B_TAB |
0x2e | i | I | B_TAB | ||
0x40 | g | G | © | 0x07 | |
0x51 | n | N | ñ | Ñ | 0x0e |
0x55 | / | ? | ÷ | ¿ | / |
0x64 | B_INSERT | 0 | B_INSERT | 0 | B_INSERT |
The mapping follows some fixed rules, including these:
The default key map also follows the conventional rules for Caps Lock and Control:
However, if the lock doesn't affect the character, Shift plus the lock is the same as Shift alone. For example, Caps Lock-7 produces '7' (the lock is ignored) and Shift7 produces '&' (Shift has an effect), so Shift-Caps Lock-7 also produces '&' (only Shift has an effect).
When Control is used with a key that doesn't produce an alphabetic character, the character that's reported is the same as if no modifiers were on. For example, Control7 produces a '7'.
The Interface Kit defines constants for characters that aren't normally represented by a visible symbol. This includes the usual space and backspace characters, but most invisible characters are produced by the function keys and the navigation keys located between the main keyboard and the numeric keypad. The character values associated with these keys are more or less arbitrary, so you should always use the constant in your code rather than the actual character value. Many of these characters are also produced by alphabetic keys when a Control key is held down.
The table below lists character constants defined in the kit and the keys they're associated with.
Key label | Key code | Character reported |
---|---|---|
Backspace | 0x1e | B_BACKSPACE |
Tab | 0x26 | B_TAB |
Enter | 0x47 | B_ENTER |
(space bar) | 0x5e | B_SPACE |
Escape | 0x01 | B_ESCAPE |
F1 - F12 | 0x02 through 0x0d | B_FUNCTION_KEY |
Print Screen | 0x0e | B_FUNCTION_KEY |
Scroll Lock | 0x0f | B_FUNCTION_KEY |
Pause | 0x10 | B_FUNCTION_KEY |
System Request | 0x7e | 0xc8 |
Break | 0x7f | 0xca |
Insert | 0x1f | B_INSERT |
Home | 0x20 | B_HOME |
Page Up | 0x21 | B_PAGE_UP |
Delete | 0x34 | B_DELETE |
End | 0x35 | B_END |
Page Down | 0x36 | B_PAGE_DOWN |
(up arrow) | 0x57 | B_UP_ARROW |
(left arrow) | 0x61 | B_LEFT_ARROW |
(down arrow) | 0x62 | B_DOWN_ARROW |
(right arrow) | 0x63 | B_RIGHT_ARROW |
Several keys are mapped to the B_FUNCTION_KEY character. An application can determine which function key was pressed to produce the character by testing the key code against these constants:
B_F1_KEY | B_F6_KEY | B_F11_KEY |
B_F2_KEY | B_F7_KEY | B_F12_KEY |
B_F3_KEY | B_F8_KEY | B_PRINT_KEY (the "Print Screen" key) |
B_F4_KEY | B_F9_KEY | B_SCROLL_KEY (the "Scroll Lock" key) |
B_F5_KEY | B_F10_KEY | B_PAUSE_KEY |
Note that key 0x30 (P) is also mapped to B_FUNCTION_KEY when the Control key is held down.
Each of the character constants listed above is a one-byte value falling in the range of values where ASCII and Unicode overlap. For convenience, the Interface Kit also defines some constants for common characters that fall outside that range. These characters have multibyte representations in UTF-8, so the constant is defined as a character string. For example:
#define B_UTF8_OPEN_QUOTE "\xE2\x80\x9C"
#define B_UTF8_CLOSE_QUOTE "\xE2\x80\x9D" #define B_UTF8_COPYRIGHT "\xC2\xA9" |
See Character Constants in the Interface Kit for a full list of these constants.
You can look at the state of all the keys on the keyboard at a given moment in time. This information is captured and reported in two ways:
In both cases, the bitfield is an array of 16 bytes,
uint8 states[16];
with one bit standing for each key on the keyboard. Bits are numbered from left to right, beginning with the first byte in the array, as illustrated below:
Bit numbers start with 0 and match key codes. For example, bit 0x3c corresponds to the A key, 0x3d to the S key, 0x3e to the D key, and so on. The first bit is 0x00, which doesn't correspond to any key. The first meaningful bit is 0x01, which corresponds to the Escape key.
When a key is down, the bit corresponding to its key code is set to 1. Otherwise, the bit is 0. However, for the three keys that toggle keyboard locks--Caps Lock (key 0x3b), Num Lock (key 0x22), and Scroll Lock (key 0x0f)--the bit is set to 1 if the lock is on and set to 0 if the lock is off, regardless of the state of the key itself.
To test the bitfield against a particular key,
For example:
if ( states[keyCode>>3] & (1 << (7 - (keyCode%8))) ) . . .
Here, on the left, the key code is divided by 8 to obtain an index into the states array. This selects the byte (the uint8) in the array that contains the bit for that key. On the right, the part of the key code that remains after dividing by 8 is used to calculate how far a bit needs to be shifted to the left so that it's in the same position as the bit corresponding to the key. This mask is compared to the states byte with the bitwise & operator.
The Be Book, in lovely HTML, for the BeOS Preview Release.
Copyright © 1997 Be, Inc. All rights reserved.
Be is a registered trademark; BeOS, BeBox, BeWare, GeekPort, the Be logo, and the BeOS logo are trademarks of Be, Inc.
Last modified June 28, 1997.