Introduction to PM ProgrammingWritten by Larry Salomon, Jr. |
IntroductionThe purpose of this column is to provide to the readers out there who are not familiar with PM application development the information necessary to satisfy their curiousity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem with books in general is that they don't answer the questions you have after you read the book the first time through. I will gladly entertain feedback from the readers about what was "glossed over" or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for. It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that would like to see discussed that really do not belong here. Specific questions can be directed to the Scratch Patch, where an attempt to answer them will be made. Last MonthLast month, we took a good hard look at a very typical main() function for a PM application. We discussed in detail the WinCreateStdWindow() function, as well as the many parameters and flags it can take. Finally, we started looking at window procedures and some of the more important messages that you - as a PM developer - will be interested in. Since a large portion of PM application development is done in coding the window procedure, this month, we will begin to look more closely at this beast and see how, through the use of various messages, we can tame it in order to accomplish our own goals. Presentation Spaces, the WM_PAINT Message, and PaintingSince the most important task you will probably have to perform will be painting your window, let us first look at the mechanism used to accomplish this. Reproduced below from last month's column is the simple window procedure that we saw. MRESULT EXPENTRY windowProc(HWND hwndWnd, ULONG ulMsg, MPARAM mpParm1, MPARAM mpParm2) { switch (ulMsg) { case WM_PAINT: { HPS hpsPaint; RECTL rclPaint; hpsPaint=WinBeginPaint(hwndWnd,NULLHANDLE,&rclPaint); WinFillRect(hpsPaint,&rclPaint,SYSCLR_WINDOW); WinQueryWindowRect(hwndWnd,&rclPaint); WinDrawText(hpsPaint, -1, "Hello world.", &rclPaint, CLR_BLACK, 0, DT_CENTER|DT_VCENTER); WinEndPaint(hpsPaint); } break; default: return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2); } /* endswitch */ return MRFROMSHORT(FALSE); }Figure 1) Simple window procedure Hmmm...The variable hpsPaint - of the type HPS - seems to be used quite a bit. What is an HPS? Looking into os2defs.h, we don't see much to help answer this. typedef LHANDLE HPS; typedef HPS *PHPS; In case you haven't associated the title of this section with our question, an HPS is a handle to a presentation space. Okay, that helps a lot...not! What is a presentation space? Presentation Spaces and Device ContextsThe easiest way to explain a presentation space is to use the familiar (and usually very vague, as you should remember from your college days :) "logical and physical" explanation. Consider an output device, which has numerous physical characteristics: the size of the output medium, the current colors, the current font, etc. All of these characteristics are physical characteristics, and they are collectively known as a device context. However, as a programmer, you don't want to have to be cognizant of the types and brands of each output device; thus, OS/2 provides the concept of a logical device which has an analagous set of characteristics. This logical device is called a presentation space (HPS). When you draw on the HPS, PM automatically converts all of the logical characteristics to their physical equivalents. The point is, and this is the only part you need to remember, any drawing is done on an HPS. InvalidationOne of the many characteristics that an HPS has is called a clipping region. If you'll recall, last issue in the New Concepts section of this column, figure 5 illustrated many children clipped to their parent. While it was implied that clipping areas - or regions as they are called - are rectangular, this is not always the case. Figure 4 illustrates a drawing of a box with no clipping applied. Applying a clipping region of a triangular shape yields the following (the clipping boundary is shown as a dotted line for illustrative purposes only): Whenever any portion of your window needs repainting, the section that needs to be painted is said to be invalid. While PM could in fact provide a precise clipping region that outlines the invalid portion, for performance reasons, it instead provides the coordinates of the smallest rectangle to completely bound the invalid area. Why are we bothering with all of this? Keep reading... The WM_PAINT Message Whenever your window has an invalid region, PM sends your window procedure a WM_PAINT message. WM_PAINT This message occurs when an application needs to repaint itself. Parameters param1 Reserved. NULL Reserved value. param2 Reserved. NULL Reserved value. Returns reply Reserved. NULL Reserved value.Since we have already said that, in order to do any drawing, you need an HPS, all that is left to reveal is the method by which you obtain one. As you have probably guessed, the function to use is WinBeginPaint(). HPS WinBeginPaint(HWND hwndWindow, HPS hpsCreated, PRECTL prclInvalid);
BOOL WinEndPaint(HPS hpsRelease);Some important things to note:
Now That I Have an HPS, What do I do Next?There is an entire system devoted to drawing, known as the Graphical Programming Interface but usually referred to as the Gpi. Since it is so extensive, we will not cover it directly; instead, some of the more common functions will be explained as they are encountered. In the Win subsystem, however, there are a number of functions which provide access to the more commonly needed functions.
And On The First Day......&deity. sent a WM_CREATE message. :) The final topic for this month's column is two new messages: these are the WM_CREATE and WM_DESTROY messages. WM_CREATEThis message occurs when an application requests the creation of a window. Parameters param1 pvData (PVOID) Window-specific data that is specified on the call to WinCreateWindow(). param2 pcsCreate (PCREATESTRUCT) Points to a CREATESTRUCT structure that specifies the various initial characteristics of the window, e.g. size, position, etc. Returns reply bReply (BOOL) Success indicator: FALSE Initialization succeeded. Continue with window creation. TRUE Initialization failed. Do not create the window. WM_DESTROYThis message occurs when an application destroys a window. Parameters param1 Reserved. NULL Reserved value. param2 Reserved. NULL Reserved value. Returns reply Reserved. NULL Reserved value.These two messages are sent to allow a window to perform any initialization and termination processing. As shown, if initialization fails, the window procedure should return TRUE to prohibit the creation of the window. A couple of things should be noted:
Think back to the first installment of this column, where it was said that a window procedure is the common way of referring to the entity called a window class procedure. The point here is that, should you write an application that has to create multiple windows of the same class (which you developed), you can no longer use global or static variables to store information that must be shared among message processing blocks. This is because all windows of the class share these variables; if one window needs to update the value of one variable, the update will affect all windows of that class. To alleviate this, PM has the idea of window words, which are additional bytes of memory allocated for each instance of a window class. You could, for example, then specify that an additional 4 bytes of memory is to be allocated, and then use those 4 bytes to point to a structure containing instance-specific data. See the section Design in the article Development of a New Window Class - Part 1 in issue 4 of EDM/2 for more information about window words. Another common reason for using these messages is for creating other window that are the children of your window class. For example, say you want to display a list of choices, and underneath you want to display the text currently selected. There are two defined window classes - listboxes and static controls - which individually do part of what is desired. You could then create them as part of the initialization process and if either should fail, return TRUE to stop the application from continuing. SummaryThis month, we took a much more detailed look at the concept of painting and how presentation spaces are used by the system to hide the specifics of whatever physical output medium is connected to the CPU. We explained update regions and their corresponding bounding rectangles, and their relationship to invalid regions. Finally, we looked at two new messages - WM_CREATE and WM_DESTROY - and briefly touched on their purpose. If you understood everything in this column and the last one, you should be able to look at intro.zip (provided last month) and understand most - if not all - of the program source code. This should be verified in order to determine your retention percentage, and, should any further clarification be necessary, send me email with your questions. Next month, we will introduce some of the other controls in order to discuss dialog boxes and how they are used to communicate with the user. We will also take a more detailed look at resources and how they are used in the development of dialog boxes. Finally, we will continue our perusal of the messages that are commonly used in PM application development. |