Modifying Your PM Programs for Pen for OS/2

From EDM2
Jump to: navigation, search

by Vera Dulaney and Kevin Lee

Last quarter, we provided an overview of how to best use Pen for OS/2. This quarter, we pick up on that concept and describe how to modify a Presentation Manager (PM) program to be pen-aware and, if modification is not possible, we also describe how to write a gesture command handler for an existing PM program.

To Recap...

Pen for OS/2 gives the computer users a much more intuitive pointing device over a mouse. Existing Presentation Manager (PM) applications can easily be modified to make use of the pen interface.

When a menu item is selected from a PM application window, the WM_COMMAND message is sent to its window procedure. Similarly, in a Pen for OS/2 system, when a gesture is given on a PM window, the WM_RECO message is sent to its window procedure. Therefore, Pen for OS/2 performs the actions specified by the gesture. If your application wants to handle the gesture by itself, it must process the WM_RECO message, returning to PM with the RECO_PROCESSED message.

If you do not want to modify your PM application to handle a gesture, your application was already distributed, or no way exists to recall, modify, or redistribute it, but you still want to handle a gesture within your application, you can write a separate command handler. Then, put this command handler name in the gesture setting of your application. When a gesture is given on your application window, Pen for OS/2 initiates this command handler to take care of the gesture.

Recognizing the Pen

For all gestures given on an PM window (even if the gesture is not recognized by Pen for OS/2), the WM_RECO message is sent to the window program. Like other PM messages, the WM_RECO message has 2 parameters, mp1 and mp2. The mp1 parameter has the window coordinate of the hot spot of a gesture; the mp2 parameter has the address of RECODATA structure. Each gesture has its own hot spot. The WM_RECO message is sent to the window that has the hot spot. The RECODATA structure has 13 fields; however, this article explains only the two most common, HRECO and RECOID. For an explanation of the other fields, please see the PENPM.H file in Pen for OS/2 Developer's Toolkit, which is on your accompanying Developer Connection CD-ROM.

The HRECO field is the Recognition subsystem handle. Pen for OS/2 Version 1.0 supports only the Gesture subsystem. Future Pen for OS/2 releases might also include support for the Voice subsystem.

The RedQueryRecoSubsystem API takes the handle as input and returns the subsystem name, as well as the number of events in the subsystem. The RECOID field is an Event ID of the recognition subsystem. The RedRecoNameFromID API uses the HRECO and RECOID fields, and returns the event name. For example, if the HRECO is 1 and RECOID is 6, the Pigtail gesture name is returned. The application writer can do whatever is needed with the subsystem and its event ID.

For the unrecognized gesture, a NULL event name will be returned by the RedRecoNameFromID API. While you process the WM_RECO message, do not give a message or a dialog box. This will cause a deadlock to occur. If you have to give the box, simply post a message to yourself, then give the box while you process the message. After you process the WM_RECO message, return to PM with RECO_PROCESSED. Then, Pen for OS/2 knows that the application has handled the gesture. The sample code shows the WM_RECO message handler in the client window. For more information, please check the WMRECO program in the Pen for OS/2 Developer's Toolkit sample program.

#define   RECO_MSG   WM_USER + 1
MRESULT EXPENTRY ClientWndProc( HWND hwnd, ULONG msg, MPARAM
       mp1, MPARAM mp2 )
{
USHORT         usXpos, usYpos;
ULONG  ulEventCnt;
CHAR   achRecoSubsys[15], achRecoEvent[15];
static RECODATA  rcData;

   switch ( msg )
   {
      case WM_RECO:
/*********************************************************************
 *  The MP1 has hot spot in window coordinate, NOT in screen         *
 *  coordinate.                                                      *
 *********************************************************************/
         usXPos = SHORT1FROMMP( mp1 );
         usYPos = SHORT2FROMMP( mp1 );

/*********************************************************************
 *  The MP2 has pointer to RECODATA and copy it to local memory      *
 *  because it is good while the WM_RECO message is processed.       *
 *********************************************************************/
         rcData = * ( RECODATA * ) PVOIDFROMMP( mp2 );

/**********************************************************************
 *  There are 3 pointers in RECODATA pointing to command,             *
 *  argument, and prefix command. You have to allocate memory         *
 *  to retrieve them while the WM_RECO message is processed.          *
 **********************************************************************/

/*********************************************************************
 *  Get the Reco subsystem name and number of events from handle.    *
 *********************************************************************/
         RedQueryRecoSubsystem( rcData.hReco, achRecoSubsys, &ulEventCnt );

/*********************************************************************
 *  Get the Event Name.                                              *
 *********************************************************************/
         RedRecoNameFromID( hReco, rcData.rID, achRecoEvent );

/*********************************************************************
 *  You can do whatever you would like to do with the gesture.       *
 *  If you have to give a message box, post a message to yourself    *
 *  Do NOT send the message.                                         *
 *********************************************************************/
         WinPostMsg( hwnd, RECO_MSG, mp1, mp2 );

         return( ( MRESULT ) RECO_PROCESSED );

      case RECO_MSG:
/*********************************************************************
 *  A message box can be given here without deadlock.                *
 *********************************************************************/

   }
}

Writing a Pen for OS/2 Command Handler

You can write a command handler for an application and put the handler name in the gesture setting of the application. When a gesture is given on the application window, Pen for OS/2 initiates the command handler, retrieve the RECODATA, and perform an action required for the gesture. For a subsequent gesture given on the application window, WM_RECO_COMMAND is sent to the command handler window procedure. In this way, any actions required for the gestures can be done by the handler, not by Pen for OS/2.

The following explains how to assign the command handler to the gesture setting of the application:

  • On the Workplace Shell desktop, click the right mouse button on the application icon.
  • Select the right arrow in Open item, and then select Settings. The settings dialog window appears and the Gesture setting page is added by Pen for OS/2. In this page, from the listbox of all gestures, you can give your command handler name to any gestures.
  • To set a command handler to a gesture, highlight the gesture and select the Edit button. Another dialog window appears with Command and Parameters fields.

You can put the command handler name (without the .EXE extension) in the Command field. If the command handler is not in a directory identified on the PATH= parameter in your CONFIG.SYS file, you must include its full path name. Any parameters put in the Parameters field are in argv of command handler. Several gestures can share one command handler or each gesture can have its own command handler. Up to 20 command handlers can run at any time.

When the command handler is initiated, it registers itself as a command handler using the RedRegisterRecoCommand API. Pen for OS/2 saves the command handler name in its internal table. After the registration, it calls the RedRecoDataFromEnv API to retrieve the RECODATA structure. The Command, Parameter, and Prefix Command fields pointed to by three pointer fields in RECODATA are attached after the RECODATA, so this API must have a large buffer.

Like a Pen-aware application, the command handler can use the HRECO and RECOID in RECODATA to retrieve subsystem and event names. When subsequent gesture is given on the application window, Pen for OS/2 checks the gesture setting of the subsequent gesture to get the command handler name and, if it cannot find the command handler in its internal table, it initiates another command handler. Otherwise it sends the WM_RECO_COMMAND message to the running command handler. The processing of this message is same as the WM_RECO message, except for the mp1 parameter.

If any file is accessed inside the command handler, use the full path name of the file. The DosQueryCurrentDisk and DosQueryCurrentDir APIs usually give a root directory of the boot disk, but not always.

An example of the Client Window procedure of a command handler follows. For command handler details, please see the RECODISP sample program in the Pen for OS/2 Developer's Toolkit.

#define    WM_RECO_INFO     WM_USER + 2 #define    CMD_STR          "recocmd"
#define    ID_CMD           1
MRESULT EXPENTRY ClientWndProc( HWND hwnd, ULONG msg, MPARAM mp1,
        MPARAM mp2 )
{
ULONG           nLen;
static RECODATA *pRecodata;

  switch( msg )
  {
     case WM_CREATE:
     {
        /*****************************************************************
        * Register Reco command handler in PenPM.                        *
        * If you have another command handler running and if you want to *
        * remove it, the RedQueryRecoCommand and                         *
        * RedDeregisterRecoCommand API's can be used.                    *
        *****************************************************************/
        RedRegisterRecoCommand( CMD_STR, ID_CMD, hwnd )  ;

        /*****************************************************************
        * Retrieve RECODATA from environment.                            *
        * First put NULL for the buffer pointer to get the actual size.  *
        * If NULL is given as buffer pointer, then NO error returned.    *
        *****************************************************************/
        nLen = 0;
        RedRecoDataFromEnv( NULL, &nLen );

        /******************************************************************
        *  The actual length of data is returned in nLen.                 *
        ******************************************************************/
        pRecodata = ( RECODATA * ) malloc( nLen );
        RedRecoDataFromEnv( pRecodata, &nLen );

        /*******************************************************************
        *  When the RECODATA is retrieved from environment, the Command,   *
        *  Argument, and PrefixCommand are attached after the RECODATA.    *
        *  So, the pszCmd points after the RECODATA, the pszArg points     *
        *  after the command data, and so forth.                           *
        *  All three data are terminated by NULL character.                *
        *  But the retrieval of RECODATA by WM_RECO_COMMAND message is     *
        *  quite different. Please check the message below.                *
        *******************************************************************/
        break;
     }

     case WM_RECO_COMMAND:
     {
        /*******************************************************************
        *  Replace this message handling code with yours for your own      *
        *  command handler. Handling of this message is similar to that of *
        *  the WM_RECO message for Pen for OS/2 aware application.         *
        *******************************************************************/

        /*******************************************************************
        * Copy the RECODATA.                                               *
        *******************************************************************/
        *pRecodata = * ( RECODATA * ) PVOIDFROMMP( mp2 );

        /*******************************************************************
        *  The RECODATA is retrieved, but the command, argument, and       *
        *  prefix command must be copied too.  These data are valid        *
        *  while the WM_RECO_COMMAND message is processed.                 *
        *******************************************************************/

        /*******************************************************************
        * Do NOT call WinMessageBox or WinDlgBox because deadlock can      *
        * occur. Post a message to yourself, do NOT send it.               *
        *******************************************************************/
        WinPostMsg( hwnd, WM_RECO_INFO, NULL, NULL );
        return( ( MRESULT ) ( TRUE ) );
     }

     case WM_RECO_INFO:
        /*******************************************************************
        *  A message or dialog box can be given here.                      *
        *******************************************************************/
        return FALSE;

  }
  return FALSE;
}

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