Jump to content

Introduction to PM Programming - Feb 1994: Difference between revisions

From EDM2
Prokushev (talk | contribs)
No edit summary
Ak120 (talk | contribs)
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Written by [[Larry Salomon Jr.]]
''Written by [[Larry Salomon Jr.]]''


== Introduction ==
==Introduction==
The 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 curiosity, 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.


The purpose of this column is to provide to the readers out there who are
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.
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
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.
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
===Last Month===
you how to develop PM applications; instead, this should be viewed as a
Last month, we took a good hard look at a very typical main() function for a PM applicationWe 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.
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 Month ===
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.
 
Last 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 Painting ===
 
Since 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.


==Presentation Spaces, the WM_PAINT Message, and Painting==
Since 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.
<pre>
<pre>
MRESULT EXPENTRY windowProc(HWND hwndWnd,
MRESULT EXPENTRY windowProc(HWND hwndWnd,
Line 80: Line 51:
}
}
</pre>
</pre>
Figure 1)  Simple window procedure</SMALL>
''Figure 1)  Simple window procedure''


Hmmm...The variable hpsPaint - of the type HPS - seems to be used quite a
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.
bit. What is an HPS?  Looking into os2defs.h, we don't see much to help
typedef LHANDLE HPS;
answer this.
typedef HPS *PHPS;
<pre>
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?
typedef LHANDLE HPS;
typedef HPS *PHPS;
</pre>
 
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 Contexts ===


The easiest way to explain a presentation space is to use the familiar (and
==Presentation Spaces and Device Contexts==
usually very vague, as you should remember from your college days :)
The 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 colours, the current font, etc. All of these characteristics are physical characteristics, and they are collectively known as a device context.
"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.


[[Image:Intropm21.gif|thumb|Figure 2) Physical output device]]  
[[Image:Intropm21.gif|Figure 2) Physical output device]]  


However, as a programmer, you don't want to have to be cognizant of the
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 analogous 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.
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.


[[Image:Intropm22.gif|thumb|Figure 3) Logical output which results in the figure 2]]
[[Image:Intropm22.gif|Figure 3) Logical output which results in the figure 2]]


The point is, and this is the only part you need to remember, any drawing
The point is, and this is the only part you need to remember, any drawing is done on an HPS.
is done on an HPS.


=== Invalidation ===
=== Invalidation ===
One 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.


One of the many characteristics that an HPS has is called a clipping
[[Image:Intropm23.gif|Figure 4) No 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.
 
[[Image:Intropm23.gif|thumb|Figure 4) No clipping]]
 
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):


[[Image:Intropm24.gif|thumb|Figure 5) Triangular clipping]]
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
[[Image:Intropm24.gif|Figure 5) Triangular clipping]]
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 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...


Whenever your window has an invalid region, PM sends your window procedure
==The WM_PAINT Message==
a WM_PAINT message.
Whenever your window has an invalid region, PM sends your window procedure a WM_PAINT message.
 
WM_PAINT


===WM_PAINT===
This message occurs when an application needs to repaint itself.
This message occurs when an application needs to repaint itself.
<pre>
<pre>
Parameters
Parameters
Line 163: Line 98:
NULL  Reserved value.
NULL  Reserved value.
</pre>
</pre>
 
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().
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().
 
<pre>
<pre>
HPS WinBeginPaint(HWND hwndWindow,
HPS WinBeginPaint(HWND hwndWindow,
                   HPS hpsCreated,
                   HPS hpsCreated,
                   PRECTL prclInvalid);
                   PRECTL prclInvalid);
</pre>
<DL>
<DT>hwndWindow
<DD>the window for which the HPS is to be obtained.
<DT>hpsCreated
<DD>used whenever you have already created an HPS and would like
to use it instead of having the system allocate one for you.  We will
always specify NULLHANDLE for this parameter.
<DT>prclInvalid
<DD>points to a RECTL structure to receive the bounding
rectangle.  This can be NULL.
</DL>
For reasons beyond the scope of this column, the HPS must be returned to
the system once you are finished.  The function to do this is
WinEndPaint(), and its only parameter is the HPS to release.
<pre>
BOOL WinEndPaint(HPS hpsRelease);
</pre>
</pre>
;hwndWindow:the window for which the HPS is to be obtained.
;hpsCreated:used whenever you have already created an HPS and would like to use it instead of having the system allocate one for you. We will always specify NULLHANDLE for this parameter.
;prclInvalid:points to a RECTL structure to receive the bounding rectangle. This can be NULL.


For reasons beyond the scope of this column, the HPS must be returned to the system once you are finished. The function to do this is WinEndPaint(), and its only parameter is the HPS to release.
  BOOL WinEndPaint(HPS hpsRelease);
Some important things to note:
Some important things to note:
 
#The HPS returned by WinBeginPaint() automatically has a clipping region set to the bounding rectangle returned in prclInvalid. This rectangle is returned to the caller to take advantage of any painting optimizations possible by restricting the area that is repainted to only the rectangle.
<OL>
#The WinEndPaint() call sets the invalid region to empty (empty is also referred to as NULLHANDLE with respect to regions, since they are also a datatype in PM).
<LI>The HPS returned by WinBeginPaint() automatically has a clipping region
#You cannot use WinBeginPaint() and WinEndPaint() except in the context of a WM_PAINT message. Should you need to do any drawing in any other message, the functions WinGetPS() and WinReleasePS() should be used. This is because there will be no invalid region, so everything will be clipped.
set to the bounding rectangle returned in prclInvalid. This rectangle is
#An HPS is used to allow you to draw only. It does not remember what was drawn, so you must redraw each time you receive a WM_PAINT message. There are ways around this, however, which we will look at in future issues.
returned to the caller to take advantage of any painting optimizations
possible by restricting the area that is repainted to only the rectangle.
<LI>The WinEndPaint() call sets the invalid region to empty (empty is also
referred to as NULLHANDLE with respect to regions, since they are also a
datatype in PM).
<LI>You cannot use WinBeginPaint() and WinEndPaint() except in the context of
a WM_PAINT message. Should you need to do any drawing in any other
message, the functions WinGetPS() and WinReleasePS() should be used. This
is because there will be no invalid region, so everything will be clipped.
<LI>An HPS is used to allow you to draw only. It does not remember what was
drawn, so you must redraw each time you receive a WM_PAINT message. There
are ways around this, however, which we will look at in future issues.
</OL>


=== Now That I Have an HPS, What do I do Next? ===
=== 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.


There is an entire system devoted to drawing, known as the Graphical
In the Win subsystem, however, there are a number of functions which provide access to the more commonly needed functions.
Programming Interface but usually referred to as the Gpi. Since it is so
;WinDrawBitmap():draws a bitmap at a specified position, with some control over appearance.
extensive, we will not cover it directly; instead, some of the more common
;WinDrawBorder():draws a border inside a specified rectangle.
functions will be explained as they are encountered.
;WinDrawPointer():draws a mouse pointer or an icon at a specified position.
 
;WinDrawText():draws a string at a specified position, with many different options for appearance
In the Win subsystem, however, there are a number of functions which
;WinFillRect():fills the specified rectangle with the specified colour.
provide access to the more commonly needed functions.
;WinInvertRect():inverts (a la xor) the specified rectangle.
 
I will leave it as a reader exercise to refer to the Programming Reference for more information about these functions.
<DL>
<DT>WinDrawBitmap()
<DD>draws a bitmap at a specified position, with some control over appearance.
<DT>WinDrawBorder()
<DD>draws a border inside a specified rectangle.
<DT>WinDrawPointer()
<DD>draws a mouse pointer or an icon at a specified position.
<DT>WinDrawText()
<DD>draws a string at a specified position, with many different options for appearance
<DT>WinFillRect()
<DD>fills the specified rectangle with the specified color.
<DT>WinInvertRect()
<DD>inverts (a la xor) the specified rectangle.
</DL>
 
I will leave it as a reader exercise to refer to the Programming Reference
for more information about these functions.


=== And On The First Day... ===
=== And On The First Day... ===
...sent a WM_CREATE message. :)


... 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.
 
The final topic for this month's column is two new messages: these are the
WM_CREATE and WM_DESTROY messages.
 
==== WM_CREATE ====


===WM_CREATE===
This message occurs when an application requests the creation of a window.
This message occurs when an application requests the creation of a window.
<pre>
<pre>
Parameters
Parameters
Line 275: Line 158:
</pre>
</pre>


==== WM_DESTROY ====
===WM_DESTROY===
 
This message occurs when an application destroys a window.
This message occurs when an application destroys a window.
<pre>
<pre>
Parameters
Parameters
Line 292: Line 173:
NULL  Reserved value.
NULL  Reserved value.
</pre>
</pre>
 
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.
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:
A couple of things should be noted:
 
#The window procedure is directly invoked to send the WM_CREATE message.
<OL>
#:This is different than it being sent or posted to the window procedure in that the window doesn't actually exist until the processing completes and returns FALSE. This significance is noted because, since the window doesn't really exist, certain functions will not work properly (i.e. WinQueryWindowRect(), WinQueryWindowPos(), etc.). Instead, you should use the values of the fields of the pcsCreate parameter.
<LI>The window procedure is directly invoked to send the WM_CREATE message.
#Last issue, we saw the following code:
This is different than it being sent or posted to the window procedure in
that the window doesn't actually exist until the processing completes and
returns FALSE. This significance is noted because, since the window
doesn't really exist, certain functions will not work properly (i.e.
WinQueryWindowRect(), WinQueryWindowPos(), etc.). Instead, you should use
the values of the fields of the pcsCreate parameter.
<LI>Last issue, we saw the following code:
 
<pre>
<pre>
hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
Line 320: Line 190:
                             &hwndClient);
                             &hwndClient);
</pre>
</pre>
 
Since the WM_CREATE message is a direct function call, WinCreateStdWindow() hasn't returned yet, and so hwndFrame is still uninitialized. If the unlikely event that you declare hwndFrame to be a global variable, you cannot use its value in the WM_CREATE message processing.  Instead, you should use the function WinQueryWindow() in the following manner:
Since the WM_CREATE message is a direct function call, WinCreateStdWindow()
hasn't returned yet, and so hwndFrame is still uninitialized. If the
unlikely event that you declare hwndFrame to be a global variable, you
cannot use its value in the WM_CREATE message processing.  Instead, you
should use the function WinQueryWindow() in the following manner:
 
<pre>
<pre>
case WM_CREATE:
case WM_CREATE:
Line 338: Line 202:
   break;
   break;
</pre>
</pre>
Before you shrug your shoulders and say "Yeah, that's nice, but what is the real benefit of these messages?", keep reading...


</OL>
Think back to the first instalment 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.
 
Before you shrug your shoulders and say "Yeah, that's nice, but what is the
real benefit of these messages?", keep reading...
 
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
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.
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
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.
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.


=== Summary ===
==Summary==
This 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.


This month, we took a much more detailed look at the concept of painting
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.
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
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.
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
[[Category:PM Articles]]
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.

Latest revision as of 22:56, 30 October 2022

Written by Larry Salomon Jr.

Introduction

The 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 curiosity, 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 Month

Last 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 Painting

Since 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 Contexts

The 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 colours, the current font, etc. All of these characteristics are physical characteristics, and they are collectively known as a device context.

Figure 2) Physical output device

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 analogous 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.

Figure 3) Logical output which results in the figure 2

The point is, and this is the only part you need to remember, any drawing is done on an HPS.

Invalidation

One 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) No clipping

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):

Figure 5) Triangular clipping

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);
hwndWindow
the window for which the HPS is to be obtained.
hpsCreated
used whenever you have already created an HPS and would like to use it instead of having the system allocate one for you. We will always specify NULLHANDLE for this parameter.
prclInvalid
points to a RECTL structure to receive the bounding rectangle. This can be NULL.

For reasons beyond the scope of this column, the HPS must be returned to the system once you are finished. The function to do this is WinEndPaint(), and its only parameter is the HPS to release.

 BOOL WinEndPaint(HPS hpsRelease);

Some important things to note:

  1. The HPS returned by WinBeginPaint() automatically has a clipping region set to the bounding rectangle returned in prclInvalid. This rectangle is returned to the caller to take advantage of any painting optimizations possible by restricting the area that is repainted to only the rectangle.
  2. The WinEndPaint() call sets the invalid region to empty (empty is also referred to as NULLHANDLE with respect to regions, since they are also a datatype in PM).
  3. You cannot use WinBeginPaint() and WinEndPaint() except in the context of a WM_PAINT message. Should you need to do any drawing in any other message, the functions WinGetPS() and WinReleasePS() should be used. This is because there will be no invalid region, so everything will be clipped.
  4. An HPS is used to allow you to draw only. It does not remember what was drawn, so you must redraw each time you receive a WM_PAINT message. There are ways around this, however, which we will look at in future issues.

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.

WinDrawBitmap()
draws a bitmap at a specified position, with some control over appearance.
WinDrawBorder()
draws a border inside a specified rectangle.
WinDrawPointer()
draws a mouse pointer or an icon at a specified position.
WinDrawText()
draws a string at a specified position, with many different options for appearance
WinFillRect()
fills the specified rectangle with the specified colour.
WinInvertRect()
inverts (a la xor) the specified rectangle.

I will leave it as a reader exercise to refer to the Programming Reference for more information about these functions.

And On The First Day...

...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_CREATE

This 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_DESTROY

This 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:

  1. The window procedure is directly invoked to send the WM_CREATE message.
    This is different than it being sent or posted to the window procedure in that the window doesn't actually exist until the processing completes and returns FALSE. This significance is noted because, since the window doesn't really exist, certain functions will not work properly (i.e. WinQueryWindowRect(), WinQueryWindowPos(), etc.). Instead, you should use the values of the fields of the pcsCreate parameter.
  2. Last issue, we saw the following code:
hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
                             WS_VISIBLE,
                             &ulCreate,
                             CLS_CLIENT,
                             "Hello World Sample",
                             0,
                             NULLHANDLE,
                             RES_CLIENT,
                             &hwndClient);

Since the WM_CREATE message is a direct function call, WinCreateStdWindow() hasn't returned yet, and so hwndFrame is still uninitialized. If the unlikely event that you declare hwndFrame to be a global variable, you cannot use its value in the WM_CREATE message processing. Instead, you should use the function WinQueryWindow() in the following manner:

case WM_CREATE:
   {
      HWND hwndFrame;

        :
      hwndFrame=WinQueryWindow(hwndWnd,QW_PARENT);
        :
   }
   break;

Before you shrug your shoulders and say "Yeah, that's nice, but what is the real benefit of these messages?", keep reading...

Think back to the first instalment 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.

Summary

This 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.