|
The screen savers are
different than other executables in their extension. All screen
savers have an extension of ‘.scr’ rather than the
traditional ‘.exe’. To ensure that it gets this
extension, follow these steps:
- Select ‘Project | Settings |
Debug’ property page from the ‘Developer
Studio’. Change the extension from ‘.exe’
file to ‘.scr’ in the edit box with a title
‘Executable for debug session’.
- Select ‘Project | Settings |
Link’ property page from the ‘Developer
Studio’. Change the extension from ‘.exe’
file to ‘.scr’ in the edit box that has a title
‘Output file name’. Press OK.
If we click on the
‘Preview’ button in the dialog box in Figure 1 the
screen saver would be put into action. If we click on another
button called ‘Settings’ one more dialog box would
appear which would help the user to control the various elements
of the screen saver. Naturally, the appearance and contents of
this dialog box would vary from one screen saver to another. The
dialog box for our screen saver is shown in the following
figure.
As soon as we change any of
the settings in the dialog box the change is immediately noticed
in the preview window in the dialog box.
Working
The screen saver developed
here randomly displays colorful squares/circles on the screen.
It uses an array of structures s[ ]
to display the squares/circles. We have defined this array to
hold 10 elements. The information stored in this array controls
the size, position and color of each square/circle. At any point
of time, a total of 10 squares/circles are displayed. When the
11th square/circle is drawn the
1st one is erased. This continues
till either some key is depressed or the mouse is moved or any
button on the mouse is clicked.
The screen saver program
runs in three different modes:
- Full Screen mode: The Screen
Saver runs in this mode in 4 cases:
- When the PC is idle for a time
greater than one that has been set up through the spin
button control in the ‘Start | Settings | Control
Panel | Display | Screen Saver’ property page.
- When the ‘Preview’
button in ‘Start | Settings | Control Panel | Display
| Screen Saver’ property page is clicked.
- When we right click on the screen
saver file and select ‘Test’ from the menu that
appears.
- When we run the screen saver by
double clicking the screen saver from the
‘Explorer’ or from ‘Start | Run’.
- Small Preview Window mode: The
Screen Saver runs in this mode in 2 cases:
- When ‘Start | Settings |
Control Panel | Display | Screen Saver’ property page
is displayed.
- When we right click on
‘.scr’ file and select ‘Install’.
- Settings Dialog Preview mode:
The Screen Saver runs in this mode in 3 cases:
- When we run the screen saver from
the ‘Developer Studio’.
- When we right click on the screen
saver file and select ‘Configure’ from the menu
that appears as shown in the Figure 4.
- When we click on the
‘Settings’ button from ‘Start | Settings |
Control Panel | Display | Screen Saver’ property page.
Figure 4
Figure 1 shows the screen
saver active in the ‘Small Window Preview’ mode,
whereas Figure 3 shows it active in the ‘Settings Dialog
Preview’ mode. The following figure shows it active in the
‘Full Screen’ mode.
Figure
5
Depending upon which of the
above three modes is used to invoke the screen saver it is
passed an argument ‘s’, ‘p’ and
‘c’ respectively. However there are two exceptions
here. In cases 3(a) and 3(b) above no argument is passed to the
program. However, in these two cases too we want the screen
saver to run in ‘Settings Dialog preview mode’. It
is necessary to identify which of these three cases has
occurred, because the size of the window where the random
squares/circles are to appear is different in the three cases.
When the
‘Settings’ dialog box is popped up, we have made a
provision to let the user select the shape of the objects
(rectangle or circle) that are going to get displayed on the
screen when the screen saver goes into action. Additionally, the
user can also select the fill style for the rectangles/circles.
We have provided three styles; solid, hatch and pattern to
choose from. For permitting the user to select the shape and the
style we have provided two combo boxes in the
‘Settings’ dialog box.
Irrespective of the mode in
which our Screen Saver is running, the control would ultimately
land in the myapp::InitInstance( )
function. Here it is first determined which command line
argument has been passed (if at all) to our program by calling
the myapp::checkoption( ) function.
It then appropriately calls the function myapp::doconfig( ), myapp::dofullscreen(
), or myapp:: doperview( ).
The
myapp::doconfig( ) Function
If it is determined that
the settings dialog box should be popped up, an object of the
settings dialog class is created through the statement,
settingdialog d ;
This calls the constructor
of the settings dialog. Here the settings dialog is created in
memory. This dialog is then displayed by calling CDialog::DoModal( ).
The myapp::dopreview( )
Function
The code of this function
given below:
void myapp::dopreview( ) {
CWnd* parent = CWnd::FromHandle ( ( HWND ) atol
( __argv[2] ) ) ; CRect r ; parent -> GetClientRect
( &r ) ; drawwnd* p = new drawwnd ( TRUE ) ; p
-> create ( NULL, WS_VISIBLE | WS_CHILD, r, parent, NULL
) ; m_pMainWnd = p ;
}
When the function is called
the drawing activity should take place in the preview window.
The drawing activity would be managed by the drawwnd class object. When this object is created the
window associated with it should be the child of the preview
window present in the property page. Hence the address of this
parent window needs to be determined. This has been achieved by
calling the function CWnd::FromHandle(
). The argument passed to this function is the handle to the
preview window of the property page. This handle is passed to
our program as a command line argument (argv[2]). Once the pointer to the parent window has
been obtained the drawwnd::create( )
function is called to create the window.
The
myapp::dofullscreen( ) Function
Lastly, if it is determined
that the actual screen saver should be put into action then a
full screen window is created. The code of dofullscreen( ) is shown below:
void myapp::dofullscreen( ) {
saverwindow* p = new saverwindow ( TRUE ) ; p
-> create( ) ; m_pMainWnd = p ;
}
The Settings
Dialog
When the DoModal( ) function is called to display the dialog box
control first reaches settingdialog::OnInitDialog( ). The code of this
function is given below.
BOOL mydialog::OnInitDialog( ) {
m_shape = AfxGetApp( ) -> GetProfileInt (
"Config", "Shape", 0 ) ; m_fillstyle
= AfxGetApp( ) -> GetProfileInt ( "Config",
"FillStyle", 0 ) ; CDialog::OnInitDialog( )
; CRect r ; CStatic *s = ( CStatic * ) GetDlgItem (
IDC_PREVIEW ) ; s -> GetWindowRect ( &r )
; ScreenToClient ( &r ) ; m_preview.create ( NULL,
WS_VISIBLE | WS_CHILD, r, this, NULL ) ; CenterWindow( )
; return TRUE ;
}
In this function, to begin
with, the values of shape and fill style are read from the
registry by calling the CWinApp::GetProfileInt( ) function. When you run the
program for the first time there won’t be any entry in the
registry. Hence default values of 0
and 0 (last parameter passed to GetProfileInt( )) would be assumed for
the shape and fill style. The values read from the registry (or
the assumed values if there are no entries in the registry) are
used to show the default selections for shape and fill style
when the dialog is popped up.
Next, the base class
implementation of OnInitDialog( ) is
called, which in turn calls settingdialog::DoDataExchange( ). In this function the
values of m_shape and m_fillstyle are used to set up the default selections
of the two combo boxes. Next, in the OnInitDialog( ) function the size of the preview window
is obtained and a preview window is created. Note that the
object m_preview (object of drawwnd class) which is a private data
member is used to create the preview window. As the window gets
created, the drawwnd::OnCreate( )
handler gets called. Here, to set the speed at which the
squares/circles should be displayed in the window, the CWnd::SetTimer( ) function is called.
This function simply sets a timer. Later on we would see how
this timer gets serviced.
As we make selections in
the dialog box its results are immediately shown in the preview
window. To ensure this a function settingdialog::newshapestyle( ) is called as soon as
the selections from the combo boxes are changed. This function
updates the drawing tools by calling the drawwnd::setdrawingtool( ) function.
Finally, when the user
dismisses the dialog box by clicking OK the selections made by
him are written to the registry by calling CWinApp::WriteProfileInt( ) function. The logic of
drawing in the preview window has been managed in the drawwnd
class.
Drawing in The
Window
To manage drawing in the
small preview window or the full screen window we have developed
a class called drawwnd. Whenever an
object of this class is created its zero argument constructor
gets called. This constructor function is shown below:
drawwnd::drawwnd ( BOOL deleteflag
) {
m_deleteflag = deleteflag
; m_shape = AfxGetApp( ) -> GetProfileInt (
"Config", "Shape", 0 ) ; m_fillstyle
= AfxGetApp( ) -> GetProfileInt ( "Config",
"FillStyle", 0 ) ;
for ( int i = 0 ; i < 10 ; i++
) s[i].x = s[i].y = -1 ;
setdrawingtool ( m_shape, m_fillstyle
) ; currentnum = 0 ;
}
Here, to begin with, the
values of shape and fill style are read from the registry. Next,
the array s[ ] is initialised such
that the x, y coordinates of all 10 squares/circles are set to
-1. These values are later on used to keep track of how many
square/circles have been drawn so far. Lastly, the values read
from the registry are used to create 10 brushes by calling the drawwnd::setdrawingtool( )
function.
Whenever the full screen
window or the small preview window is created, control reaches drawwnd::OnCreate( ). Here we have
set up a timer by calling CWnd::SetTimer(
).
When the time interval set
in CWnd::SetTimer( ) is over the
function drawwnd::OnTimer( ) gets
called. The code of this function is shown below:
void drawwnd::OnTimer ( UINT id )
{
CClientDC *dc ; dc = new CClientDC ( this )
; CRect rect ; GetClientRect ( &rect ) ; draw (
dc, rect ) ; delete dc ;
}
In this function we have
obtained the device context, determined the client area size and
then called drawwnd::draw( ) to
actually carry out the drawing.
Full Screen
Mode
When the screen saver runs
in the full screen mode the myapp::dofullscreen( ) function gets called. In this
function we have created an object of saverwindow class which has been derived from the
drawwnd class. On creating this object
the constructor of saverwindow gets
called. In the constructor we have set up the saverwindow::lastpoint
variable with a value (-1,-1). This value is later on used in
the saverwindow::OnMouseMove( )
function, as we would soon see. Once the object has been
created, using it, saverwindow::create(
) function is called. This function in turn calls
drawwnd::create( ) with WS_EX_TOPMOST as
the first argument and WS_POPOUP as one of the values in the
second argument. The first argument ensures that the full screen
window created becomes the topmost window. The value WS_POP
ensures that the window created doesn’t have a caption bar
and the border.
Once the window is created
the drawnd::OnCreate( ) gets called
which once again sets up a timer in response to which the OnTimer( ) function calls
drawwnd::draw( ) to do the drawing in
the full screen window.
Disabling The Screen
Saver
When the screen saver runs
in the full screen mode it should get disabled whenever the user
presses any key or performs any mouse operation. It means in the
saverwindow class we must handle all
keyboard and mouse related messages. Hence we have written the
handlers OnKeyDown( ), OnSysKeyDown( ),
OnLButtonDown( ), etc. in the saverwindow class. In each of these handlers all that
we have done is, sent a WM_CLOSE message to the message queue
and then called the base class implementation of the handler.
When the WM_CLOSE message would get picked up from the message
queue, the application would close itself. The only exception is
the OnMouseMove( ) handler. The code
of this handler is as shown below:
void saverwindow::OnMouseMove ( UINT
flags, CPoint pt ) {
if ( lastpoint == CPoint ( -1, -1 )
) lastpoint = pt ;
else if ( lastpoint != pt
) PostMessage ( WM_CLOSE ) ;
drawwnd::OnMouseMove ( flags, pt )
;
}
If you remember, in the
constructor of the saverwindow class
we have set up the variable saverwindow::lastpoint to a value
(-1, -1). In the OnMouseMove(
) handler we have checked whether the coordinates of lastpoint
are still (-1, -1). If they are then we do not post the WM_CLOSE
message into the message queue. It would be necessary to do so
in the following situation. Suppose a WM_MOUSEMOVE message is
present in the message queue and the screen saver gets active
before the WM_MOUSEMOVE message could get processed. In such an
event, as soon as the screen saver becomes active it would get
deactivated when the pending WM_MOUSEMOVE message gets
processed. Our code given above prevents this by not posting the
WM_CLOSE into the message queue in this situation. We also
update the value of lastpoint such
that next time a WM_MOUSEMOVE message arrives the screen saver
is shut down by posting a WM_CLOSE into the message queue.
Destroying The
Window
Once the screen saver
becomes active we can deactivate it in a number of ways, as
discussed above. Even though it has been deactivated from the
screen it continues to run in memory. If once again a
considerable time elapses without you hitting a key or clicking
a mouse yet again it would become active. If it gets activated
again then it would yet again reach the myapp::dofullscreen( ) function to create a window.
Here the code,
p = new saverwindow;
would get executed again.
But what has happened to
the object that p was pointing last
time around. It is still surviving, and now if p starts pointing to another object then a memory leak
would occur since we would have no way to access the earlier
object. Note that the earlier object would not get destroyed
through the destructor of myapp. This
is because the destructor doesn’t get called since the
application is still running and the myapp a object has not gone
out of scope.
To avoid this situation we
have used a drawwnd::deleteflag
variable. While creating the preview window of ‘Start |
Control Panel | Display | Screen Saver’, or the full
screen window we have passed a value TRUE to the drawwnd constructor which in turn sets it up in
drawwnd::m_deleteflag. As against this,
in case of the preview window of the settings dialog we have
passed a value FALSE to the drawwnd
constructor. This is because on dismissing the dialog box the
application would come to an end resulting in call to the myapp
class’s destructor function.
This would destroy the object associated with the preview window
using its address which is stored in myapp::m_pMainWnd.
In case of the preview window of
‘Start | Control Panel | Display | Screen Saver’ and
the full screen window the window object would be destroyed in
the drawwnd::PostNcDestroy( )
function using the m_deleteflag
variable.
Download
|