You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



You are here: Editor project page > Key Events in SeaMonkey

Key Events in SeaMonkey

1. Introduction

This document is a specification for all aspects of key events in SeaMonkey applications. It describes key event generation, propogation, and processing. It is particularly important for platform developers to understand the key event generation section.

2. Key Event Generation

Native input methods (used when typing Japanese), aka IME, must have first crack at all keyboard events this includes accelerators and keybindings because they might be used by the native input method (such as to turn them on and off). While the IME is active, key events (keydown, keypress, keyup) will not be generated. IME input generates text events which are handled by a separate set of handlers. If a key event isn't consumed by the native input method, it will result in a sequence as described below.

Each keyboard input results in 3 separate key events: KeyDown, followed by KeyPress, followed KeyUp. All platforms must generate these events in this sequence for every keyboard input (except IME events).

Key repeat (automatic generation of keyevents when a key is held down) results in a sequence of a KeyDown event, followed by multiple keypress events, terminated by a KeyUp event. A keypress event should be generated for each platform key repeat event recieved.

Each key event has 2 data fields: a virtual key code and a computed character code. The key code corresponds to the physical key that was typed. The char code represents the Unicode character that results from the keyboard input. The char code field is never set during KeyDown or KeyUp events. It is only set during a KeyPress event in which the key maps to an actual character. The following table describes how these data are interpreted for each type of key event:

Key Event key code char code
KeyDown set 0 (ignored)
KeyPress if char code is set, key code is not set so key code is only set for input that doesn't map to a character, like "home" key or for dead key actions set if the key typed represents a character determined by nsIsPrintable(), otherwise 0 (ignored)
KeyUp set 0 (ignored)

Key listeners are responsible for knowing when to use key codes and when to use char codes. In particular, the platform must not try to map these data in any way other than as specified in the table above.

There are a few special cases that need to be described:

  • If an alphanumeric key (a - z or 0-9) is pressed, a charcode value must be returned, even if a modifier bit is set. If the charcode is a resulting character which should be inserted in an editor, all 4 modifier flags should be cleared (0). If the charcode is not a character but instead a command or keybinding, all modifier flags should be left intact.
  • Modifier keys (such as shift, alt, control, command) pressed in isolation generate the same event sequence as normal keys. This is for completeness, so an app can choose to do something special when any of these keys are pressed in isolation.
  • Dead keys (such as keyboard input used to generate accented characters) generate the same event sequence as normal keys. Key events have a bit that says whether the keyboard input was from a dead key action. The char code for a dead key is always 0 (ignored).
  • The absolute order of key events generated by a combination of keys is indeterminate. For example, if shift and A are pressed, the keyup events could come in either order.
  • There won't be a distinction between right and left modifier keys (nor distinction between keyboard and keypad keys).
  • Platform special keys (Windows key, contextual menu key on Windows, Macintosh startup key, monitor control keys, etc.) do not propagate events. They should be handled in platform code if necessary.

3. Key Event Propogation

Key events are DOM events, and follow all the normal DOM event rules for propogation. See http://www.w3.org/TR/WD-DOM-Level-2/events.html. Key event propogation is entirely XP.

4. Key Event Processing

from Tom Pixley (joki@netscape.com)

Internally we're using the consumeNoDefault flag. Now ignore that for a second. From the DOM perspective, there are three things that can be done to an event during processing. One is cancelling the default action. This is the equivalent of setting the consume flag. This does *not* prevent further processing. It simply prevents the default action of the system. Now in most cases the system would be the browser itself but since much of the browser is implemented inside the content area this is a little confusing as the system is really now Gecko, not the browser, and since Gecko doesn't generally do too much with the event themselves, cancelling the default action won't buy you much.

Then there are two other methods, preventCapture and preventBubble. These methods will, respectively, prevent further handling by lower Nodes in the tree after capturing and prevent further handling by higher Nodes in the tree during bubbling. If you wish a lower or higher level Node to get an event and then stop the event from going further you need to use these flags. The consumeNoDefault flag will only tell the system to stop processing the event but it will still go give the event to all the other DOM handlers. Note that one of the properties of DOM event processing is that an event processor cannot prevent propogation of the event to other listeners on that same node. Since DOM does not provide for ordering of event delivery to listeners on a single node, there is no general mechanism to allow for any communication between them.

Key listeners are responsible for knowing when to look at key codes and when to look at char codes.

Key listeners should handle keyboard input using the KeyPress event handler.

For the editor KeyPress handler, any charCode that is non-zero should be a valid unicode character which can be inserted. Keybindings and accelerators should cancel the event if it shouldn't be inserted in the editor. The keybinding component needs to look at key events prior to the editor component.

Character mapping occurs in the platform specific code, prior to the keypress event being generated. When a key combination, such alt+'s' could be both a keybinding and a character, the platform code will translate it into the appropriate Unicode character for the keypress event. Keybindings need to match against the generated Unicode character in the keypress event. The only exception to this is keys like Home or Page Up which don't have a Unicode character representation. For this case, keybindings should use the keycode in the keypress event.

5. Sample Code

 boolean IsNonCharacterKey(nativeKeyCode)
 {
  boolean isNonPrintable;
  //
  // this table is used to determine which keys are special and
  // should not generate a charCode
  //
  switch (nativeKeycode)
  {
   // modifier keys
   case kShiftKeyCode:      isNonPrintable = true; break;
   case kMetaKeyCode:       isNonPrintable = true; break;
   case kCapsLockKeyCode:   isNonPrintable = true; break;
   case kControlKeyCode:    isNonPrintable = true; break;
   case kAltKeyCode:        isNonPrintable = true; break;
   case kClearKeyCode:      isNonPrintable = true; break;
   
   // function keys
   case kF1KeyCode:         isNonPrintable = true; break;
   case kF2KeyCode:         isNonPrintable = true; break;
   case kF3KeyCode:         isNonPrintable = true; break;
   case kF4KeyCode:         isNonPrintable = true; break;
   case kF5KeyCode:         isNonPrintable = true; break;
   case kF6KeyCode:         isNonPrintable = true; break;
   case kF7KeyCode:         isNonPrintable = true; break;
   case kF8KeyCode:         isNonPrintable = true; break;
   case kF9KeyCode:         isNonPrintable = true; break;
   case kF10KeyCode:        isNonPrintable = true; break;
   case kF11KeyCode:        isNonPrintable = true; break;
   case kF12KeyCode:        isNonPrintable = true; break;
   case kPauseKeyCode:      isNonPrintable = true; break;
   case kScrollLockKeyCode:     isNonPrintable = true; break;
   case kPrintScreenKeyCode:    isNonPrintable = true; break;
   
   // misc. keys
   case kInsertKeyCode:         isNonPrintable = true; break;
   case kDeleteKeyCode:         isNonPrintable = true; break;
   case kTabKeyCode:            isNonPrintable = true; break;
   case kBackspaceKeyCode:      isNonPrintable = true; break;
   
   // navigation keys
   case kEscapeKeyCode:         isNonPrintable = true; break;
   case kHomeKeyCode:           isNonPrintable = true; break;
   case kEndKeyCode:            isNonPrintable = true; break;
   case kPageUpKeyCode:         isNonPrintable = true; break;
   case kPageDownKeyCode:       isNonPrintable = true; break;
   case kLeftArrowKeyCode:      isNonPrintable = true; break;
   case kRightArrowKeyCode:     isNonPrintable = true; break;
   case kUpArrowKeyCode:        isNonPrintable = true; break;
   case kDownArrowKeyCode:      isNonPrintable = true; break;
   case kReturnKeyCode:         isNonPrintable = true; break;
   case kEnterKeyCode:          isNonPrintable = true; break;
   default:                     isNonPrintable = false; break;
  }
  return isNonPrintable;
 }
 // logic for determining if a keyevent is printable
 boolean nsIsPrintable(nativeEvt)
 {
     if (IsNonCharacterKey(nativeEvt.keyCode)) return false;
     if (nativeEvt.charCode < 32)) return false;
     if (IsDeadKey(nativeEvt)) return false;
     return true;
 }
 
 
     // logic for generating a key down
     domEvent.charcode = 0;
     domEvent.keyCode = PlatformToDOMKeyCode(nativeEvt.keyCode);
     // logic for generating a key press
    if (nsIsPrintable(nativeEvt))
     {
       domEvent.charCode = ConvertToUnicode(nativeEvt.charCode);
       domEvent.keyCode = 0;
     }
     else
     {
         domEvent.keyCode = PlatformToDOMKeyCode(nativeEvt.keyCode);
         domEvent.charCode = 0;
     }
     // logic for generating a key up
     domEvent.charcode = 0;
     domEvent.keyCode = PlatformToDOMKeyCode(nativeEvt.keyCode);
  
Tague and Kathy's proposal
9/19/99 Results of second meeting Steve Clark (buster@netscape.com)
9/15/99 Initial draft Steve Clark (buster@netscape.com)