Jump to content

PMGuide - Mouse and Keyboard Input: Difference between revisions

From EDM2
Line 184: Line 184:
*Each time the frame window is activated, the client window receives a WM_ACTIVATE message with the low word of the mp2 equal to TRUE. When the frame window is deactivated, the client window receives a WM_ACTIVATE message with a FALSE activation indicator.
*Each time the frame window is activated, the client window receives a WM_ACTIVATE message with the low word of the mp2 equal to TRUE. When the frame window is deactivated, the client window receives a WM_ACTIVATE message with a FALSE activation indicator.
===Checking for a Key-Up or Key-Down Event===
===Checking for a Key-Up or Key-Down Event===
The following code fragment shows how to decode a WM_CHAR message to determine whether it indicates a key-up event or a key-down event:
The following code fragment shows how to decode a [[WM_CHAR]] message to determine whether it indicates a key-up event or a key-down event:
<pre>
<pre>
     USHORT fsKeyFlags;
     USHORT fsKeyFlags;

Revision as of 03:16, 6 May 2025

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Presentation Manager Programming Guide and Reference
  1. How to Use this Book
  2. Device Functions
  3. Direct Manipulation Functions
  4. Dynamic Data Formatting Functions
  5. Hooks and Procedures
  6. Profile Functions
  7. Spooler Functions
  8. Window Functions
  9. Message Processing
  10. Data Types
  11. Errors
  12. Atom Tables
  13. Button Controls
  14. Clipboards
  15. Combination Box
  16. Container Controls
  17. Control Windows
  18. Cursors
  19. Dialog Windows
  20. Direct Manipulation
  21. Drawing in Windows
  22. Dynamic Data Exchange
  23. Entry-Field Controls
  24. File Dialog Controls
  25. Font Dialog Controls
  26. Frame Windows
  27. Hooks
  28. Initialization Files
  29. Keyboard Accelerators
  30. List-Box Controls
  31. Menus
  32. Messages and Message Queues
  33. Multiple-Line Entry Field Controls
  34. Mouse and Keyboard Input
  35. Mouse Pointers and Icons
  36. Notebook Controls
  37. Painting and Drawing
  38. Presentation Parameters
  39. Resource Files
  40. Scroll-Bar Controls
  41. Slider Controls
  42. Spin Button Controls
  43. Static Controls
  44. Title-Bar Controls
  45. Value Set Controls
  46. Windows
  47. Window Classes
  48. Window Procedures
  49. Window Timers
  50. Appendices
  51. Notices
  52. Glossary

An OS/2 Presentation Manager application can accept input from both a mouse (or other pointing device) and the keyboard. This chapter explains how these input events should be received and processed.

About Mouse and Keyboard Input

Only one window at a time can receive keyboard input, and only one window at a time can receive mouse input; but they do not have to be the same window. All keyboard input goes to the window with the input focus, and, normally, all mouse input goes to the window under the mouse pointer.

System Message Queue

The operating system routes all keystrokes and mouse input to the system message queue, converting these input events into messages, and posts them, one at a time, to the proper application-defined message queues. An application retrieves messages from its queue and dispatches them to the appropriate window procedures, which process the messages.

Mouse and keyboard input events in the system message queue are strictly ordered so that a new event cannot be processed until all previous events are fully processed: the system cannot determine the destination window of an input event until then. For example, if a user types a command in one window, clicks the mouse to activate another window, then types a command in the second window, the destination of the second command depends on how the application handles the mouse click. The second command would go to the second window only if that window became active as a result of the mouse click.

It is important for an application to process all messages quickly to avoid slowing user interaction with the system. A message must be responded to immediately in the current thread, but the processing it initiates should be done asynchronously in another thread that has no windows in the desktop tree.

The OS/2 operating system can display multiple windows belonging to several applications at the same time. To manage input among these windows, the system uses the concepts of window activation and keyboard focus.

Window Activation

Although the operating system can display windows from many different applications simultaneously during a PM session, the user can interact with only one application at a time-the active application. The other applications continue to run, but they cannot receive user input until they become active.

To enable the user to easily identify the active application, the system activates all frames in the tree between HWND_DESKTOP and the window with input focus. That is, the system positions the active frame window above all other top-level windows on the screen. If the active window is a standard frame window, the window's title bar and sizing border are highlighted.

The user can control which application is active by clicking on a window or by pressing the Alt+Tab or Alt+Esc key combinations. An application can set the active frame window by calling WinSetActiveWindow; it also can obtain the handle of the active frame window by using WinQueryActiveWindow.

When one window is deactivated and another activated, the system sends a WM_ACTIVATE message, first to the window being deactivated, then to the window being activated. The fActive parameter of the WM_ACTIVATE message is set to FALSE for the window being deactivated and set to TRUE for the window being activated. An application can use this message to track the activation state of a client window.

Keyboard Focus

The keyboard focus is a temporary attribute of a window; the window that has the keyboard focus receives all keyboard input until the focus changes to a different window. The system converts keyboard input events into WM_CHAR messages and posts them to the message queue of the window that has the keyboard focus.

An application can set the keyboard focus to a particular window by calling WinSetFocus. If the application does not use WinSetFocus to explicitly set the keyboard-focus window, the system sets the focus to the active frame window.

The following events occur when an application uses WinSetFocus to shift the keyboard focus from one window (the original window) to another (the new window):

  1. The system sends the original window a WM_SETFOCUS message (with the fFocus parameter set to FALSE), indicating that that window has lost the keyboard focus.
  2. The system then sends the original window a WM_SETSELECTION message, indicating that the window should remove the highlight from the current selection.
  3. If the original (frame) window is being deactivated, the system sends it a WM_ACTIVATE message (with the fActive parameter set to FALSE), indicating that the window is no longer active.
  4. The system then sends the new application a WM_ACTIVATE message (with fActive set to TRUE), indicating that the new application is now active.
  5. If the new (main) window is being activated, the system sends it a WM_ACTIVATE message (with fActive set to TRUE), indicating that the main window is now active.
  6. The system sends the new window a WM_SETSELECTION message, indicating that the window should highlight the current selection.
  7. Finally, the system sends the new window a WM_SETFOCUS message (with fFocus set to TRUE), indicating that the new window has the keyboard focus.

If, while processing a WM_SETFOCUS message, an application calls WinQueryActiveWindow, that function returns the handle of the previously-active window until the application establishes a new active window. Similarly, if the application, while processing WM_SETFOCUS, calls WinQueryFocus, that function returns the handle of the previous keyboard-focus window until the application establishes a new keyboard-focus window. In other words, even though the system has sent WM_ACTIVATE and WM_SETFOCUS messages (with the fActive and fFocus parameters set to FALSE) to the previous windows, those windows are considered the active and focus windows until the system establishes new active and focus windows.

If the application calls WinSetFocus while processing a WM_ACTIVATE message, the system does not send a WM_SETFOCUS message (with fFocus set to FALSE), because no window has the focus.

A client window receives a WM_ACTIVATE message when its parent frame window is being activated or deactivated. The activation or deactivation message usually is followed by a WM_SETFOCUS message that specifies whether the client window is gaining or losing the keyboard focus. Therefore, if the client window needs to change the keyboard focus, it should do so during the WM_SETFOCUS message, not during the WM_ACTIVATE message.

Keyboard Messages

The system sends keyboard input events as WM_CHAR messages to the message queue of the keyboard-focus window. If no window has the keyboard focus, the system posts WM_CHAR messages to the message queue of the active frame window. Following are two typical situations in which an application receives WM_CHAR messages:

  • An application has a client window or custom control window, either of which can have the keyboard focus. If the window procedure for the client or control window does not process WM_CHAR messages, it should pass them to WinDefWindowProc, which will pass them to the owner. Dialog control windows, in particular, should pass unprocessed WM_CHAR messages to the WinDefDlgProc function, because this is how the user interface implements control processing for the Tab and Arrow keys.
  • An application window owns a control window whose window procedure can handle some, but not all, WM_CHAR messages. This is common in dialog windows. If the window procedure of a control in a dialog window cannot process a WM_CHAR message, the procedure can pass the message to the WinDefDlgProc function. This function sends the message to the control window's owner, which usually is a dialog frame window. The application's dialog procedure then receives the WM_CHAR message. This also is the case when an application client window owns a control window.

A WM_CHAR message can represent a key-down or key-up transition. It might contain a character code, virtual-key code, or scan code. This message also contains information about the state of the Shift, Ctrl, and Alt keys.

Each time a user presses a key, at least two WM_CHAR messages are generated: one when the key is pressed, and one when the key is released. If the user holds down the key long enough to trigger the keyboard repeat, multiple WM_CHAR key-down messages are generated. If the keyboard repeats faster than the application can retrieve the input events from its message queue, the system combines repeating character events into one WM_CHAR message and increments a count byte that indicates the number of keystrokes represented by the message. Generally, this byte is set to 1, but an application should check each WM_CHAR message to avoid missing any keystrokes.

An application can ignore the repeat count. For example, an application might ignore the repeat count on Arrow keys to prevent the cursor from skipping characters when the system is slow.

Message Flags

Applications decode WM_CHAR messages by examining individual bits in the flag word contained in the first message parameter (mp1) that the system passes with every WM_CHAR message. The type of flag word indicates the nature of the message. The system can set the bits in the flag word in various combinations. For example, a WM_CHAR message can have the KC_CHAR, KC_SCANCODE, and KC_SHIFT attribute bits all set at the same time. An application can use the following list of flag values to test the flag word and determine the nature of a WM_CHAR message:

Flag Name Description
KC_ALT Indicates that the Alt key was down when the message was generated.
KC_CHAR Indicates that the message contains a valid character code for a key, typically an ASCII character code.
KC_COMPOSITE In combination with the KC_CHAR flag, this flag indicates that the character code is a combination of the key that was pressed and the previous dead key. This flag is used to create characters with diacritical marks.
KC_CTRL Indicates that the Ctrl key was down when the message was generated.
KC_DEADKEY In combination with the KC_CHAR flag, this flag indicates that the character code represents a dead-key glyph (such as an accent). An application displays the dead-key glyph and does not advance the cursor. Typically, the next WM_CHAR message is a KC_COMPOSITE message, containing the glyph associated with the dead key.
KC_INVALIDCHAR Indicates that the character is not valid for the current translation tables.
KC_INVALIDCOMP Indicates that the character code is not valid in combination with the previous dead key.
KC_KEYUP Indicates that the message was generated when the user released the key. If this flag is clear, the message was generated when the user pressed the key. An application can use this flag to determine key-down and key-up events.
KC_LONEKEY In combination with the KC_KEYUP flag, this flag indicates that the user pressed no other key while this key was down.
KC_PREVDOWN In combination with the KC_VIRTUALKEY flag, this flag indicates that the virtual key was pressed previously. If this flag is clear, the virtual key was not previously pressed.
KC_SCANCODE Indicates that the message contains a valid scan code generated by the keyboard when the user pressed the key. The system uses the scan code to identify the character code in the current code page; therefore, most applications do not need the scan code unless they cannot identify the key that the user pressed. WM_CHAR messages generated by user keyboard input generally have a valid scan code, but WM_CHAR messages posted to the queue by other applications might not contain a scan code.
KC_SHIFT Indicates that the Shift key was down when the message was generated.
KC_TOGGLE Toggles on and off every time the user presses a specified key. This is important for keys like NumLock, which have an on or off state.
KC_VIRTUALKEY Indicates that the message contains a valid virtual-key code for a key. Virtual keys typically correspond to function keys. For those using hooks, when this bit is set, KC_SCANCODE should usually be set as well.

The mp1 and mp2 parameters of the WM_CHAR message contain information describing the nature of a keyboard input event, as follows:

  • SHORT1FROMMP (mp1) contains the flag word.
  • CHAR3FROMMP (mp1) contains the key-repeat count.
  • CHAR4FROMMP (mp1) contains the scan code.
  • SHORT1FROMMP (mp2) contains the character code.
  • SHORT2FROMMP (mp2) contains the virtual key code.

An application window procedure should return TRUE if it processes a particular WM_CHAR message or FALSE if it does not. Typically, applications respond to key-down events and ignore key-up events.

The following sections describe the different types of WM_CHAR messages. Generally, an application decodes these messages by creating layers of conditional statements that discriminate among the different combinations of flag and code attributes that can occur in a keyboard message.

Key-Down or Key-Up Events

Typically, the first attribute that an application checks in a WM_CHAR message is the key-down or key-up event. If the KC_KEYUP bit of the flags word is set, the message is from a key-up event. If the flag is clear, the message is from a key-down event.

Repeat-Count Events

An application can check the key-repeat count of a WM_CHAR message to determine whether the message represents more than 1 keystroke. The count is greater than 1 if the keyboard is sending characters to the system queue faster than the application can retrieve them. If the system queue fills up, the system combines consecutive keyboard input events for each key into a single WM_CHAR message, with the key-repeat count set to the number of combined events.

Character Codes

The most typical use of WM_CHAR messages is to extract a character code from the message and display the character on the screen. When the KC_CHAR flag is set in the WM_CHAR message, the low word of mp2 contains a character code based on the current code page. Generally, this value is a character code (typically, an ASCII code) for the key that was pressed.

Virtual-Key Codes

WM_CHAR messages often contain virtual-key codes that correspond to various function keys and direction keys on a typical keyboard. These keys do not correspond to any particular glyph code but are used to initiate operations. When the KC_VIRTUALKEY flag is set in the flag word of a WM_CHAR message, the high word of mp2 contains a virtual-key code for the key.

Note: Some keys, such as the Enter key, have both a valid character code and a virtual-key code. WM_CHAR messages for these keys will contain character codes for both newline characters (ASCII 11) and virtual-key codes (VK_ENTER).

Scan Codes

A third possible value in a WM_CHAR message is the scan code of the key that was pressed. The scan code represents the value that the keyboard hardware generates when the user presses a key. An application can use the scan code to identify the physical key pressed, as opposed to the character code represented by the same key.

Accelerator-Table Entries

The system checks all incoming keyboard messages to see whether they match any existing accelerator-table entries (in either the system message queue or the application message queue). The system first checks the accelerator table associated with the active frame window; if it does not find a match, the system uses the accelerator table associated with the message queues. If the keyboard input event corresponds to an accelerator-table entry, the system changes the WM_CHAR message to a WM_COMMAND, WM_SYSCOMMAND, or WM_HELP message, depending on the attributes of the accelerator table. If the keyboard input event does not correspond to an accelerator-table entry, the system passes the WM_CHAR message to the keyboard-focus window.

Applications should use accelerator tables to implement keyboard shortcuts rather than translate command keystrokes. For example, if an application uses the F2 key to save a document, the application should create a keyboard accelerator entry for the F2 virtual key so that, when pressed, the F2 key generates a WM_COMMAND message rather than a WM_CHAR message.

Mouse Messages

Mouse messages occur when a user presses or releases one of the mouse buttons (a click) and when the mouse moves. All mouse messages contain the x and y coordinates of the mouse-pointer hot spot (relative to the coordinates of the window receiving the message) at the time the event occurs. The mouse-pointer hot spot is the location in the mouse-pointer bit map that the system tracks and recognizes as the position of the mouse pointer.

If a window has the CS_HITTEST style, the system sends the window a WM_HITTEST message when the window is about to receive a mouse message. Most applications pass WM_HITTEST messages on to WinDefWindowProc by default, so disabled windows do not receive mouse messages. Windows that specifically respond to WM_HITTEST messages can change this default behavior. If the window is enabled and should receive the mouse message, the WinDefWindowProc function (using the default processing for WM_HITTEST) returns the value HT_NORMAL. If the window is disabled, WinDefWindowProc returns HT_ERROR, in which case the window does not receive the mouse message.

The default window procedure processes the WM_HITTEST message and the usHit parameter in the WM_MOUSEMOVE message. Therefore, unless an application needs to return special values for the WM_HITTEST message or the usHit parameter, it can ignore them. One possible reason for processing the WM_HITTEST message is for the application to react differently to a mouse click in a disabled window.

The contents of the mouse-message parameters (mp1 and mp2) are as follows:

  • SHORT1FROMMP (mp1) contains the x position.
  • SHORT2FROMMP (mp1) contains the y position.
  • SHORT1FROMMP (mp2) contains the hit-test parameter.

Capturing Mouse Input

The operating system generally posts mouse messages to the window that is under the mouse pointer at the time the system reads the mouse input events from the system message queue. An application can change this by using the WinSetCapture function to route all mouse messages to a specific window or to the message queue associated with the current thread. If mouse messages are routed to a specific window, that window receives all mouse input until either the window releases the mouse or the application specifies another capture window. If mouse messages are routed to the current message queue, the system posts each mouse message to the queue with the hwnd member of the QMSG structure for each message set to NULL. Because no window handle is specified, the WinDispatchMsg function in the application's main message loop cannot pass these messages to a window procedure for processing. Therefore, the application must process these messages in the main loop.

Capturing mouse input is useful if a window needs to receive all mouse input, even when the pointer moves outside the window. For example, applications commonly track the mouse-pointer position after a mouse "button down" event, following the pointer until a "button up" event is received from the system. If an application does not call WinSetCapture for a window and the user releases the mouse button, the application does not receive the button-up message. If the application sets a window to capture the mouse and tracks the mouse pointer, the application receives the button-up message even if the user moves the mouse pointer outside the window.

Some applications are designed to require a button-up message to match a button-down message. When processing a button-down message, these applications call WinSetCapture to set the capture to their own window; then, when processing a matching button-up message, they call WinSetCapture, with a NULL window handle, to release the mouse.

Button Clicks

An application window's response to a mouse click depends on whether the window is active. The first click in an inactive window should activate the window. Subsequent clicks in the active window produce an application-specific action.

A common problem for an application that processes WM_BUTTON1DOWN or similar messages is failing to activate the window or set the keyboard focus. If the window processes WM_CHAR messages, the window procedure should call WinSetFocus to make sure the window receives the keyboard focus and is activated. If the window does not process WM_CHAR messages, the application should call WinSetActiveWindow to activate the window.

Mouse Movement

The system sends WM_MOUSEMOVE messages to the window that is under the mouse pointer, or to the window that currently has captured the mouse, whenever the mouse pointer moves. This is useful for tracking the mouse pointer and changing its shape, based on its location in a window. For example, the mouse pointer changes shape when it passes over the size border of a standard frame window.

All standard control windows use WM_MOUSEMOVE messages to set the mouse-pointer shape. If an application handles WM_MOUSEMOVE messages in some situations but not others, unused messages should be passed to the WinDefWindowProc function to change the mouse-pointer shape.

Using the Mouse and Keyboard

This section explains how to perform the following tasks:

  • Determine the active status of a frame window
  • Check for a key-up or key-down event
  • Respond to a character message
  • Handle virtual-key codes
  • Handle a scan code

Determining the Active Status of a Frame Window

The activated state of a window is a frame-window characteristic. The system does not provide an easy way to determine whether a client window is part of the active frame window. That is, the window handle returned by the WinQueryActiveWindow function identifies the active frame window rather than the client window owned by the frame window.

Following are two methods for determining the activated state of a frame window that owns a particular client window:

  • Call WinQueryActiveWindow and compare the window handle it returns with the handle of the frame window that contains the client window, as shown in the following code fragment:
       HWND hwndClient;
       BOOL fActivated;

       fActivated = (WinQueryWindow(hwndClient, QW_PARENT) ==
                     WinQueryActiveWindow(HWND_DESKTOP));
  • Each time the frame window is activated, the client window receives a WM_ACTIVATE message with the low word of the mp2 equal to TRUE. When the frame window is deactivated, the client window receives a WM_ACTIVATE message with a FALSE activation indicator.

Checking for a Key-Up or Key-Down Event

The following code fragment shows how to decode a WM_CHAR message to determine whether it indicates a key-up event or a key-down event:

    USHORT fsKeyFlags;

    case WM_CHAR:  {
    USHORT fsKeyFlags = SHORT1FROMMP(mp1);

    if (fsKeyFlags & KC_KEYUP) {
         .
         . /* Perform key-up processing.   */
         .

    } else {
         .
         . /* Perform key-down processing. */
         .
    }

    return;

    }

Responding to a Character Message

The following code fragment shows how to respond to a character message:

    USHORT fsKeyFlags;
    UCHAR  uchChr1;

    case WM_CHAR:
    fsKeyFlags = (USHORT) SHORT1FROMMP(mp1);

    if (fsKeyFlags & KC_CHAR) {

        /* Get the character code from mp2. */
        uchChr1 = (UCHAR) CHAR1FROMMP(mp2);
        .
        . /* Process the character.         */
        .

        return TRUE;
    }

If the KC_CHAR flag is not set, the mp2 parameter from CHAR1FROMMP still might contain useful information. If either the Alt key or the Ctrl key, or both, are down, the KC_CHAR bit is not set when the user presses another key. For example, if the user presses the a key when the Alt key is down, the low word of mp2 contains the ASCII value for "a" (0x0061), the KC_ALT flag is set, and the KC_CHAR flag is clear. If the translation does not generate any valid characters, the char field is set to 0.

Handling Virtual-Key Codes

The following code fragment shows how to decode a WM_CHAR message containing a valid virtual-key code:

    USHORT fsKeyFlags;

    case WM_CHAR:
    fsKeyFlags = (USHORT) SHORT1FROMMP(mp1);

    if (fsKeyFlags & KC_VIRTUALKEY) {

        /* Get the virtual key from mp2.       */
        switch (SHORT2FROMMP(mp2)) {
         case VK_TAB:
             .
             . /* Process the TAB key.         */
             .
             return TRUE;
         case VK_LEFT:
             .
             . /* Process the LEFT key.        */
             .
             return TRUE;
         case VK_UP:
             .
             . /* Process the UP key.          */
             .
             return TRUE;
         case VK_RIGHT:
             .
             . /* Process the RIGHT key.       */
             .
             return TRUE;
         case VK_DOWN:
             .
             . /* Process the DOWN key.        */
             .
             return TRUE;
           .
           . /* Etc...                         */
           .
           default:
               return FALSE;
           }
       }

Handling a Scan Code

All WM_CHAR messages generated by keyboard input events have valid scan codes. WM_CHAR messages posted by other applications might or might not have valid scan codes. The following code fragment shows how to extract a scan code from a WM_CHAR message:

    USHORT fsKeyFlags;
    UCHAR  uchScanCode;

    case WM_CHAR:
    fsKeyFlags = (USHORT) SHORT1FROMMP(mp1);

    if (fsKeyFlags & KC_SCANCODE) {

        /* Get the scan code from mp1.   */
        uchScanCode = CHAR4FROMMP(mp1);
        .
        . /* Process the scan code.      */
        .

        return (MRESULT) TRUE;
       }