[ Next ] [ Previous ]   Chapter 9
[ Contents ]  [ Chapter 8: Interfacing with OS/2 Devices ]  [ Chapter 10: Window Management ]

Introduction to Windows.

Introduction

The basic building block for all Presentation Manager (PM) programming is a window. Most items displayed on the screen are windows, of some shape or fashion. A window is designed to react to messages sent to it either from the system or from another window. These messages are placed into a message queue that is unique to each PM application. A message is used to signal events that happen to a window. For example, a WM_CREATE message is sent when a window is halfway through its creation process; a WM_SlZE message is sent after the user has sized the window; a WM_DESTROY message just before the destruction of the window is complete. Each window has a specific window procedure that is used to respond back to the system when a message is sent. The programmer is responsible for creating this window procedure. The window procedure is a switch statement that will filter out certain messages that are of interest to the application. The messages that are not interesting can be passed on to a default window procedure or a default dialog procedure. For instance, the programmer may want to initialize some data in the WM_CREATE message processing or free up memory when the WM_DESTROY is received.

What Is a Window?

The first thing to understand when beginning Presentation Manager programming is the concept of a window. A window is a graphical image of a rectangle that sits on the screen and is used to provide a uniform interface with which a user can interact. (See Figure 9.1.)
Figure 9.1 A Window.
A window can be sized larger or smaller, it can be opened or closed, it can be made visible or invisible. Suffice it to say that there are a lot of things to do with a window.

Figure 9.2 Drawing of a window's components

Figure 9.2 looks like one  window but, in reality, it is seven  windows:
  • The frame window
  • The title bar
  • The system menu
  • The close/maximize/minimize buttons
  • The client window

Each of the five windows has a window procedure associated with it. In most cases, the programmer will be able to use the system-defined window procedures for all but the client window. The window procedure is a function that tells the window how to behave. Windows that share the same window procedure belong to the same window class. This is a familiar concept for those readers acquainted with object-oriented  programming.

Imagine a fast food restaurant. Each item on the menu could be considered one class - a hot dog class, a hamburger class, and a pizza class. Suppose mustard, mayo, relish, or cheese could be put on a hot dog, in any combination. Each of these condiments would he a hot dog style.

The same is true for window classes. There are many predefined window classes, including some classes specific to pen computing and the multimedia extensions. The classes specific to Presentation Manager are:

Symbolic constant Meaning
WC_FRAME The Frame control class
WC_COMBOBOX Combo box control class
WC_BUTTON Button control class
WC_MENU Menu control class
WC_STATIC Static text control class
WC_ENTRYFIELD Entryfield control class
WC_LISTBOX Listbox control class
WC_SCROLLBAR Scroll bar control class
WC_TITLEBAR Titlebar control class
WC_MLE Multi-line edit control class
WC_SPINBUTTON Spinbutton control class
WC_CONTAINER Container control class
WC_SLIDER Slider control class
WC_VALUESET Valueset control class
WC_NOTEBOOK Notebook control class


Each window class is very different from the others. Some of these predefined classes will be covered in later chapters. The client window, which is the area inside the window frame, belongs to a user-defined class. Each window class also contains a set of window styles specific to that class. There is a set of class styles available to all classes. The styles are:

These styles will be covered in more detail in the section entitled "Window Stylin".
Once we know a little bit about the window classes the operating system offers, we can decide which are best suited for our application, or, as most of us do-it-yourselfers will do, you can create your own. So, let's do just that.
WIN1.C
WIN1.MAK
WIN1.DEF

The INCLUDE Files

The OS/2 Toolkit provides oodles and oodles of header files. These files contain structure definitions, function prototypes, and many system-defined constants to make OS/2 programs much easier to read. The large size of these files and the tremendous amount of overhead they create make it advantageous to selectively pick and choose those parts that are applicable to a program. This is done by placing a series of #defines before the inclusion of OS2.H. In this program, we will use #define INCL_WIN.
#define INCL_WIN
#include <os2.h>
This is an all-encompassing define that will include the necessary headers for all the Win... functions. This  is overkill in most cases, but for our first example we'll keep things simple.

The Window Procedure Definition

MRESULT  EXPENTRY ClientWndProc(
 HWND hwndWnd,
 ULONG ulMsg,
 MPARAM  mpParm1,
 MPARAM mpParm2 );
Window procedures are declared in a very special way, using the prefix MRESULT EXPENTRY. In OS2DEF.H, these expand to VOID * _System. The return type, MRESULT, gives the window procedure the freedom to return whatever it needs to by using the VOID * type. The _System tells the C-compiler that the operating system will be calling the function. It is a good idea to use the Presentation Manager-defined data types when dealing with window procedures and messages. There is a good probability that some definitions will change when moving to other machine architectures, and by using the defined data types, we save some headaches if we need to port the application to some other version of OS/2. A more detailed explanation of window procedure is in the section "The Window Procedure Revisited"

The function's parameters are HWND  hwndWnd, ULONG ulMsg, MPARAM mpParm1, and MPARAM mpParm2. This may look very familiar to Microsoft Windows programmers. The variable hwndWnd is a window handle. Each window has its  own unique window handle, and most Win... functions will include this as a parameter. In this case, hwndWnd is the window to which the message is being sent. The parameter ulMsg is the specific message being sent so the window. We will cover messages in more detail in Chapter11.

The last two parameters are mpParm1 and mpParm2 which have the type MPARAM. These are "shape-shifter" parameters. MPARAM is really a PVOID in disguise. This gives the operating system two 32-bit spaces to insert whatever data corresponds to the message being sent. These values could be pointers or short or long integers. For example. the message WM_MOUSEMOVE is sent whenever the mouse is moved. The first message parameter, mpParm1, would contain two SHORTs. The second message parameter, mpParm2, also contains two SHORTs. Figure 9.3 provides a breakdown of a message-parameter variable.
Y Coordinate X Coordinate
SHORT2 (16 bits) SHORT1 (16 bits)

32-bit MPARAM mp1
Figure 9.3 Breakdown of a message-parameter variable

Helper Macros

Many data-type conversions are necessary in a Presentation Manager application because of the multiple data types that can be used as an MPARAM or MRESULT. MRESULT is the value returned by the window procedure and is also a "shape-shifter". The Toolkit includes a group of helper macros to make these conversions easier.
Table 9.1 presents the macros used to convert some standard data type into a MPARAM data type that can be used when sending or posting a window message.
 
Table 9.1 Macros to Convert into MPARAM
Macro Converts into MPARAM
MPFROMVOID 0
MPFROMP PVOID
MPFROMHWND HWND
MPFROMCHAR CHAR
MPFROMSHORT SHORT
MPFROM2SHORT  2 SHORTs
MPFROMSH2CH 2 CHARs
MPFROMLONG ULONG

Table 9.2 presents the macros used to convert a MPARAM data type into a standard data type that can be used when receiving a window message.
 
Table 9.2 Macros to Convert from MPARAM
Macro Converts from MPARAM
PVOIDFROMMP PVOID
HWNDFROMMP HWND
CHAR1FROMMP CHAR
CHAR2FROMMP second CHAR
CHAR3FROMMP third CHAR
CHAR4FROMMP fourth CHAR
SHORT1FROMMP low SHORT
SHORT2FROMMP high SHORT
LONGFROMMP ULONG

Table 9.3 presents the macros used to convert a MRESULT data type into standard data type that can be used to examine a return value for the window procedure.
 
Table 9.3 Macros to Convert from MRESULT
Macro  Converts from MRESULT
PVOIDFROMMR PVOID
SHORT1FROMMR low SHORT
SHORT2FROMMR high SHORT
LONGFROMMR ULONG

Table 9.4 presents the macros used to convert a standard  data typo into a MRESULT data type that can be used to construct a return value from the  window procedure.
 
Table 9.4 Macros to Convert to MRESULT
Macro Converts to MRESULT
MRFROMP PVOID
MRFROMSHORT SHORT
MRFROM2SHORT 2 SHORTs
MRFROMLONG ULONG

Presentation Manager Program Initialization

habAncbor = WinInitialize ( 0 ) ;
hmqQueue = WinCreateMsgQueue( habAncor,0) ;
The beginning of a PM program will always start with a few things. First, WinInitialize  is called to obtain an anchor block handle, or HAB. An anchor block is specific to each thread that contains a window procedure.
HAB  WinInitialize( ULONG. flOptions)
The only parameter for WinInitialize is a ULONG that is used for initialization  options. In a PM environment, this should be 0. An anchor block currently contains error information for each thread and also may be used for "future portability issues". Each Presentation Manager thread should obtain its own anchor block for two reasons: portability and also to obtain error information specific to that thread.
 HMQ WinCreateMsgQueue( HAB habAnchor, Long lQueuesize )
WinCreateMsgQueue  will create a message queue for the thread that called the function. The message queue is how Presentation Manager communicates back and forth with the windows. The first parameter is the anchor block handle, habAnchor. The second parameter is the queue size. A parameter of 0 indicates the default queue size in OS/2, which holds 10 messages. A full queue will cause the user interface to respond rather slowly and sometimes to stop responding completely. The default  queue size should be fine for most applications. If a queue is getting too full, the program should be checked to see where messages are getting backlogged. (One of the requirements for a PM interface is a crisp user response. Any response that consumes more than 100 milliseconds probably should be put in a separate thread. See Chapter 30 for more information on multithreading in a PM program.)

Creating a New Class

WinRegisterClass
     ( habAnchor,
       CLS_CLIENT,
       ClientWndProc,
       0,
       0 ) ;
The function WinRegisterClass is used to create a new class of windows, in this case CLS_CLIENT.
BOOL WinRegisterClass
      ( HAB hab,
        PSZ pszClassName,
        PFNWP pfnWndProc,
        ULONG flStyle,
        ULONG cbWindowData)
The first parameter is the anchor,  habAnchor. The next parameter is the class name. This parameter is a null-terminated string. The next parameter is the window procedure the class is assigned to, ClientWndProc. The fourth parameter is the class styles used for the new class. We're not going to use any class styles for now, so we put 0 here. The last parameter is the number of bytes of storage space that will be tacked on to each window belonging to this class. This piece of space is commonly referred to as "window words." This is covered in more detail later.
 

Creating a Window

 
By now readers are probably thinking "But I just wanted to create one lousy window". Well, this is it, the function call you've been waiting for: WinCreateStdWindow.  This function actually creates five windows as stated earlier; but only two that are of any interest to us - the frame window and the client window.

ulFlags = FCF_TITLEBAR |FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_SHELLPOSITION | FCF_TASKLIST ;

hwndFrame = WinCreateStdWindow(
      HWND_DESKTOP,
      WS_VISIBLE,
      &ulFlags,
      CLS_CLIENT,
      "Titlebar",
      0L,
      NULLHANDLE,
      0,
      &hwndClient );
The function returns the frame window handle.

/* This function creates a standard window. */
 #define INCL_WINFRAMEMGR /* Or use INCL_WIN, INCL_PM, Also in COMMON section */
 #include <os2.h>

 HWND       hwndParent;      /* Parent-window handle. */
 ULONG      flStyle;         /* Frame-window style.   */
 PULONG     pflCreateFlags;  /* Frame-creation flags. */
 PSZ        pszClassClient;  /* Client-window class name. */
 PSZ        pszTitle;        /* Title-bar text. */
 ULONG      flStyleClient;   /* Client-window style. */
 HMODULE    Resource;        /* Resource identifier. */
 ULONG      ulId;            /* Frame-window identifier. */
 PHWND      phwndClient;     /* Client-window handle. */
 HWND       hwndFrame;       /* Frame-window handle.  */

 hwndFrame = WinCreateStdWindow(hwndParent,
               flStyle, pflCreateFlags, pszClassClient,
               pszTitle, flStyleClient, Resource,
               ulId, phwndClient);

The. first parameter specified is the parent of the frame window. We'll discuss parents and owners in a minute. The second parameter is the frame style. A frame can draw from two sets of styles: frame styles, because this is a frame window; and window styles, because the frame class is a subset of the window class "window". The most common window style available is WS_VISIBLE. Yep, you guessed it, this means the window is not only created but will show up as well.
The third parameter is the frame flags. Frame flags describe how the frame will look. The possible descriptors are OR'ed together. Figure 9.4 is a diagram of all the possible descriptors and the bits that correspond to them.
 
Bit  Constant
0 FCF_TITLEBAR
1 FCF_SYSMENU
2 FCF_MENU
3 FCF_SIZEBORDER
4 FCF_MINBUTTON
5 FCF_MAXBUTTON
6 FCF_VERTSCROLL
7 FCF_HORZSCROLL
8 FCF_DLGBORDER
9 FCF_BORDER
10 FCF_SHELLPOSITION
11 FCF_TASKLIST
12 FCF_NOBYTEALIGN
13 FCF_NOMOVEWITHOWNER
14 FCF_ICON
15 FCF_ACCELTABLE
16 FCF_SYSMODAL
17 FCF_SCREENALIGN
18 FCF_MOUSEALIGN
[...]
-
24 FCF_HIDEBUTTON
25
-
26 FCF_CLOSEBUTTON
[...]
30 FCF_AUTOICON
Figure 9.4 Frame creation flags
Table 9.5 Frame Creation Flags Description
Flag Description 
FCF_TITLEBAR Creates a title bar on the frame.
FCF_SYSMENU Creates a system menu on the frame.
FCF_MENU Creates an application menu on the frame. This is loaded from the resource file or .DLL. (See Chapter 12 for more information.)
FCF_SIZEBORDER Creates a sizing border on the frame.
FCF_MINBUTTON Creates a minimize button on the frame.
FCF_MAXBUTTON Creates a maximize button on the frame.
FCF_MINMAX Creates both a minimize and maximize button on the frame.
FCF_VERTSCROLL Creates a vertical scroll bar on the frame.
FCF_HORZSCROLL Creates a horizontal scroll bar on the frame.
FCF_DLGBORDER Creates the thick dialog box border on the frame.
FCF_BORDER Creates a thin border on the frame.
FCF_SHELLPOSITION The system determines the initial size and placement of the frame window.
FCF_TASKLIST Adds the program title to the task list and window title to the window list.
FCF_NOBYTEALIGN Do not optimize window movements in 8 pel multiples.
FCF_NOMOVEWITHOWNER The frame window will not move when the owner is moved.
FCF_ICON An icon is added to the frame. This is loaded from the resource file or .DLL. (See Chapter 12 for more information.)
FCF_ACCELTABLE An accelerator table is added to the frame. This is loaded from the resource file or DLL. (See Chapter 12 for more information)
FCF_SYSMODAL The frame window is system modal.
FCF_SCREENALIGN The frame window is positioned relative to the desktop rather than relative to the owner window.
FCF_MOUSEALIGN The frame window is positioned relative to the position of the mouse rather than relative to the owner  window.
FCF_HIDEBUTTON Creates "hide" button on the frame
FCF_HIDEMAX Creates "hide" and maximize buttons on the frame.
FCF_CLOSEBUTTON use when no other min/max button is present
FCF_AUTOICON A WM_PAINT message will tot be sent to the application when the frame window is iconized
FCF_STANDARD FCF_TITLEBAR | FCF_SYSMENU | FCF_MINBUTTON | FCF_MAXBUTTON | FCF_SIZEBORDER |FCF_ICON |FCF_MENU | FCF_ACCELTABLE | FCF_SHELLPOSITION |FCF_TASKLIST

In this example, we'll use the following flags:
FCF_TITLEBAR,  FCF_SYSMENU, FCF_SIZEBORDER, FCF_MINMAX,  FCF_SHELLPOSITION, FCF_TASKLIST

Gotcha!

Be sure to pass a pointer to a ULONG as this parameter

The fourth parameter is the name of the window class that the client window will belong to; in this case we use the string defined by CLS_CLIENT. The next parameter is the window text for the title bar. The sixth parameter is the client window style. Since we defined the parent of the client window hwndFrame to have the style WS_VISIBLE, the client, as a child of hwndFrame, will inherit the WS_VISIBLE style. This means we don't have to specify any window styles here; we'll just leave that a 0.
The next parameter is the resource ID location. The next parameter contains the resource ID for the frame window. This one resource ID will point to all the resources that are defined for the frame. This includes the menu, icon, accelerator table, and any other items defined using the frame creation fags. For more information on resources, see Chapter 12.
The last parameter is the address of a window handle. Presentation Manager will place the client window handle into this variable upon the function's return.
If WinCreateStdWindow fails, NULLHANDLE is returned. Before we attempt to do anything else, it is a good idea to check the return handle to make sure it is valid; if not, the application should quit, preferably with some sort of error message.

Message, Message, Who's Got the Message ?

bLoop = WinGetMsg ( habAnchor,
                    &qmMsg
                    NULLHANDLE,
                    0,
                    0);
while ( bLoop)
{
    WinDispatchMsg ( habAnchor, &qmMsg ) ;
    bLoop = WinGetMsg(  habAnchor,
                        &qmMsg,
                        NULLHANDLE,
                        0,
                        0 ) ;
} /* endwhile */

The two functions WinGetMsg  and WinDispatchMsg, are  the keys to getting the message queue up and running. Without some form of message retrieval and dispatch the system will respond with a "Program not responding..."  error message. The secret to a well thought out Presentation Manager application is a message queue that is quick and responsive. WinGetMsg will retrieve the message from the message queue and place it into the variable qmMsg. The QMSG structure looks very similar to the variables that are passed to the window procedure. Eventually the QMSG structure will be passed on to ClientWndProc or the window procedure for the window receiving the message. WinGetMsg  and WinDispatchMsg  form a post office for messages. They pick up the messages and then make sure that the messages are delivered to the correct window.

BOOL WinGetMsg(
         HAB hab,
         PQMSG pqmsgmsg,
         HWND hwnfFilter,
         ULONG ulFirst,
         ULONG ulLast )
The first parameter of WinGetMsg is the anchor block handle. The next one is the address of the QMSG structure that will handle the retrieved message information. The next three parameters are not used in this example. They provide a way for WinGetMsg to choose selectively which messages to pick out of the queue. By specifying zeroes here, WinGetMsg will retrieve all messages from the message queue in the order they were placed there. After the message is retrieved from the queue, it is then passed on to WinDispatchMsg.
MRESULT WinDispatchMsg ( HAB hab, PQMSG pgmsgMsg ) ;
It is WinDispatchMsg's  job to take the message from the qmMsg variable and send it on to the window procedure associated with the window it is addressed to. For instance, if qmMsg.hwnd were equal to hwndWnd, WinDispatchMsg  would take qmMsg  and send it on to ClientWndProc.

   /* QMSG structure */
   typedef struct _QMSG    /* qmsg */
   {
      HWND    hwnd; /* window handle that msg is being sent to */
      ULONG   msg;  /* the message itself */
      MPARAM  mp1; /* Message Parameter 1 */
      MPARAM  mp2; /* Message Parameter 2 */
      ULONG   time;  /* Time msg was sent  */
      POINTL  ptl; /* mouse position when msg was sent  */
      ULONG   reserved;
   } QMSG;
   typedef QMSG *PQMSG;

The QMSG structure contains a lot of very interesting information about the message. The first field in the structure, hwnd, is the window handle the message is for. The field msg  is the constant identifying the message. Some common messages are WM_CREATE, WM_PAINT, WM_QUIT and WM_SIZE. The next two parameters, mp1 and mp2, are the message parameters. Each message has a set use for these parameters. Usually they are used to convey more information about the message. The time  field contains the time the message was sent, and the ptl field is a structure that contains the mouse position when the message was sent.

Terminating a Program

You may have noticed that WinGetMsg and WinDispatchMsg  were running in a while loop. While WinGetMsg  returns a TRUE value, this loop continues to process messages. When WinGetMsg  receives a WM_QUIT,  WinGetMsg returns FALSE and will fall out of the loop. At this point,  the user has elected to close the application, and it's time for the final cleanup. We have created three things that need to be destroyed - the frame window hwndFramehmqQueue, and habAnchor. Each of these items has its own destroy function.
BOOL WinDestroyMsgQueue( HMQ hmq ) ;
BOOL WinDestroyWindow(HWND hwnd );
BOOL WinTerminate (HAB hab);
By destroying hwndFrame, we also are destroying the client window, the title bar, and all the other windows that are children of the frame.

   WinDestroyWindow (hwndFrame );
} /* endif */
WinDestroyMsgQueue( hmqQueue ) ;
WinTerminate (habAnchor);
return 0;

The Window Procedure Revisited

You might have looked over main  and thought. "Is this it?" Well, no. We've presented just the tip of the iceberg. The window procedure is the meat of a Presentation  Manager program. A window procedure's sole purpose in life is to respond to the messages for the window that belongs to it. It is also important to realize that multiple windows can and will access the same window procedure. Programmers must be very careful with static and global variables or flags. They can come back to haunt developers if two windows are accessing the same procedure. Is is a good idea to avoid these if at all possible.
Most window procedures are nothing more than a giant switch  statement, with a case for each message. A window procedure does not have to respond to every message; it can filter the majority of the messages through to a function, WinDefWindowProc or WinDefDlgProc. This function lets the system handle messages in a system default manner. As the creator of the window procedure, it is the programmer's job to pick out which messages will  trigger a response in your program. For instance, when a WM_SIZE message is received, the programmer may wish to reflow any text on the window so that it is all visible and centered. Passing messages on to WinDefWindowProc or WinDefDlgProc  is very safe.
Gotcha!
Be very careful about accidentally reversing  WinDefWindowProc  and WinDefDlgProc. Strange things can occur when calling WinDefWindowProc  for a dialog box or using WinDefDlgProc  for a non-dialog box window.
The default action for these messages is listed in the online reference for the Toolkit. A few messages are very important to a window procedure. These will be covered later in this chapter.
In this example the window procedure, ClientWndProc,  is very small. It's not quite the smallest window procedure available, but it's pretty close.

MRESULT EXPENTRY ClientWndProc ( HWND hwndWnd,
                                 ULONG ulMsg,
                                 MPARAM mpParm1,
                                 MPARAM mpParm2 )
{
   switch ( ulMsg ) {
   case WM_ERASEBACKGROUND:
      return MRFROMSHORT ( TRUE ) ;

   default:
      return WinDefWindowProc ( hwndWnd,
                                ulMsg,
                                mpParm1,
                                mpParm2 ) ;
   } /* endswitch */

   return MRFROMSHORT ( FALSE ) ;
}

The only message that is utilized in ClientWndProc  is  WM_ERASEBACKGROUND. This message is used to fill the client window with the system-window background color. If we let this message pass on to WinDefWindowProc, the background of the window would be transparent and the desktop would show through. By returning TRUE, we tell the system to paint the client window with the background color. In some cases, this message doesn't need to be processed if the painting is handled in the WM_PAINT message. In a window procedure, most messages have a default handling of returning FALSE. Programmers can save a few extra function calls by returning FALSE themselves from the handled instead of calling WinDefWindowProc.


[ Next ] [ Previous ]   Chapter 9
[ Contents ]  [ Chapter 8: Interfacing with OS/2 Devices ]  [ Chapter 10: Window Management ]