Click here to Skip to main content
15,880,967 members
Articles / Desktop Programming / MFC
Article

Smoov Code Project Screen Saver

Rate me:
Please Sign up or sign in to vote.
4.74/5 (25 votes)
24 May 200212 min read 235.4K   4.1K   77   55
Asynchronous XML Web Client Animated Screen Saver in Win32/MFC

Quick Start

The Smoov Code Project Screen Saver moves text and images around the screen. It runs with or without internet access, but is mainly intended to display the latest articles, comments and messages available from the Code Project web service.

Copy Code Project Smoov Saver.scr to your Windows folder on your computer. Right click on the desktop, select Properties and go to the Screen Saver tab. Select "Code Project Smoov Saver" from the drop down list. It will show the preview in the little window. You can select Settings... to see the configuration dialog which allows you to turn off internet web service access, and to turn on web access display (which signals web access in the upper left corner). You can select Preview... to see the full screen saver. You can also just invoke the screen saver directly from your File Explorer.

Thanks go to "The Code Project" and its tremendous community of users. This is for you!

Introduction

This screen saver combines several areas of interest. Firstly, there is the screensaver aspect. This article starts by describing the steps I took to create the basic screen saver application which handles certain Windows command line options. Secondly, there is the animation aspect. This article introduces CAnimationItem and CAnimationEngine classes for facilitating the development of text and bitmap animation effects.

Thirdly, there is the web service client aspect. This screen saver was developed for the Code Project Screen Saver Competition which revolved around displaying information from the latest articles, messages and comments provided by the Code Project's web service. However, other than a brief mention, this aspect is mostly saved for a later article.

Note: release 1.1 fixes proxy access, includes multi-monitor option (in the source code), and fixes VC7 compiler warnings. There is no password support.

Create Dialog-based App

I poked around first and found among other things an article in MSDN entitled "Creating 32-Bit Screen Savers with Visual C++ and MFC" by Nigel Thompson 12/04/1994 which tells how to do it. And I played with an old screen saver I created in 1996. In 1996 I believe I was concerned with creating one that worked on 16-bit windows as well as 32-bit with the same source code. To do that I had to link with the SCRNSAVE library and use straight Windows. Now I no longer want to support 16-bit, and I prefer to use MFC.

I used the MFC AppWizard, selected "Dialog Based" for the configuration dialog, and checked "Windows Sockets" for accessing the Code Project information.

Try it as a .SCR File

First I wanted to make sure it would work as a screen saver. I renamed this simple AppWizard executable to a .scr extension and copied it to the Windows (Winnt) directory. Sure enough it came up in the Desktop Screen Saver list and executed the dialog when selected. When it dawns on you that a screen saver "plug-in" is simply an EXE renamed to SCR, you'll say to yourself "I can do this!"

The problem was that the name that appeared in the list was the filename (I am using NT 4.0). I then took a long detour for a few hours trying to figure out how to change the name that appears in that drop-down list. MSDN seems to say you put the name under string resource ID=1. But two MSDN articles (Q137250 Q126239) both dated 09/25/1995 conflict on this issue. Finally I accepted that the easiest way is to name the executable the way you want the name to appear.

Having used up all my pizza and coffee, all I had to show was a vanilla MFC-generated application. But actually I was feeling like I had done a lot of my background research now and was ready to make some progress.

I've noticed being among programmers that we generally share a lack of awareness about where development time is wasted. The majority of time is wasted in the "in-betweens", those integration issues that shouldn't be so difficult, but are. Its okay as long as you expect these problems and allow time in your schedule for them.

Creating a Screen Saver Window

I used Visual Studio to create a generic CWnd class called CSaverWnd, and used the Nigel Thompson article as a starting point to implement the Screen Saver Window Class functionality. I also used the website called Windows Screen Saver Programming by Lucian Wischik mentioned in the discussion area of the Code Project article Creating a screen saver by Mehdi Mousavi 11/09/2001 which is a great article by the way. The different functionality of the screen saver exe is called upon by the Windows Desktop Properties Screen Saver dialog using command line parameters as follows:

none

Bring up a configuration dialog with no parent window. It appears that later Windows operating systems automatically supply a /s when you double click on the .scr file in Explorer, and it runs the screen saver rather than the configuration dialog.

/c

Bring up a configuration dialog using the active window as the parent

/s

Run as regular screen saver. It should be a topmost popup window occupying the whole screen that closes on keyboard or mouse input.

/p <hwnd>

Run as preview screen saver inside specified window (preview pane). It must be a child window without sensitivity to keyboard or mouse input, and close when this child window is destroyed.

/a

Password option which I do not know anything about

Getting this going took about 2 hours. The hardest part was finding instructions for how to do the preview because the articles I was using neglected to document the command line /p. The preview is when the screen saver renders inside the Windows Desktop Properties dialog Screen Saver tab miniature screen. In /p mode, the screen saver window is a child window of the command line provided HWND and the screen saver closes on WM_DESTROY. In /s mode, the screen saver window handles certain messages like keyboard and mouse actions to close the screen saver.

The screen saver debugging problem is due to the screen saver running with the WS_EX_TOPMOST style. I did not realize this for some time and was frustrated because it is not possible to use breakpoints at certain parts of the code. I originally attributed this to the scrnsave.lib and so I was surprised to encounter it again. Now I use #ifdef _DEBUG to turn off this style during debugging. It is documented that you cannot debug SCRNSAVE applications very well in MSDN Q123871 "PRB: Screen Saver Applications Cannot Be Debugged Properly" 07/25/1997.

Windows Timer-Based Animation

The main thing about creating Windows animations is to use the timer (SetTimer) with somewhere around 20 or 50 milliseconds between WM_TIMER messages. This interval is like the frames in a film. A film has somewhere around 15 to 30 frames per second, and the 20 milliseconds used in this screen saver is up to 50 frames per second which is probably not necessary.

This timer mechanism allows the message pump to be free to process other windows messages while your animations are moving around. In fact, it also allows frames to be gracefully "dropped" so that if the animation pauses due to an unrelated CPU surge it won't appear to "speed up"' afterwards like it is trying to catch up. Many games take over the computer to avoid the occasional jerkiness that is natural in a multitasking operating system. The best way you can make your animations smooth in a Windows program is to keep down the CPU requirements of your own animations, so that other applications can easily fit their operations between your OnTimer handling.

The alternative to a timer is a background thread (or threads) posting notification messages when its time to redraw something. Like timer-based animation, this frees up your GUI thread's message pump, but it introduces tricky synchronization issues.

Animation Item and Engine

Each animation item that is on the screen is generally represented by a structure or class that maintains its status. I use a CAnimationItem class to hold item information, and the items are stored in a linked list CList class. The CAnimationEngine class renders a frame in the RunFrame method by looping once through all of the existing items, updating their status, and drawing them. This creates one frame of the movie. In the next WM_TIMER handler it is called again to draw the next frame.

These two animation classes are designed together to make the job of creating animations quick and intuitive. A window class that contains animations, declares a CAnimationEngine member and all of the animations can be created from there. Here is an overview of the animation engine calls to be made if your engine object is m_ae.

// OnCreate
m_ae.SetScreen( rectClient );

// OnTimer
CClientDC dc(this);
int nItems = m_ae.RunFrame( dc );
if ( nItems == 0 )
    m_ae.CreateItem( "hello" ).SetPoint(0,40).SetFrames(50).FadeOut();

// OnDestroy
m_ae.DestroyGDIs();

That's all you really have to do! This just shows the word hello located at 0,40 and repeatedly fading out every 50 frames (about 1 second if the timer interval is 20 milliseconds). Of course you can create a bunch of items with all kinds of different settings. One of the things to notice in the example is that RunFrame returns a number of items remaining. You generally want to create a fresh batch of new items whenever the previous batch runs out, like separate scenes in the movie. When an item runs out of frames, the engine deletes it automatically.

Animation Item Settings

Now let's look at some more examples of the settings you can use with the items. These settings use the return *this reference trick to make the code more easy and readable by allowing you to stack the desired settings methods.

POSITION pos = m_ae.CreateItem( "The Code Project" )
.SetFont( "36,Arial,B" )
.AlignCenter()
.SetStart( ptIntro.x, ptIntro.y - 70 )
.SetFinishRel( 0, 20 )
.SetFrames( 70 )
.GetPos();

Here it is setting the font and alignment of the text, a starting and a relative finishing point. The font string is an intuitive way of specifying basic font characteristics comma-delimited. The engine will move the item from the start to the finish point over the course of 70 frames. This above example also uses GetPos as the last method to store the position and create a continuation item after it:

m_ae.CreateItemAfter( pos )
.SetFrames( 20 )
.FadeOut();

This creates another item with the same text, font and alignment, that will fade out in a matter of 20 frames. The appearance will be seamless as if one item was there for some time before fading out. You can also use positions to create new items that GoAfter or GoWith rather than starting with a copy of the previous item.

Use a resource ID to create a simple bitmap item. Thanks go to Jörg König's DIB256 CDIBitmap class which isolates the complexities of 256 vs. big color monitor settings.

m_ae.CreateItem( "" ).SetBitmap( IDB_BOB1 );

To use a bitmap like an image list (as in the case of the emoticons), create a bitmap containing a row of square individual pictures (emoticons are 20X20, so 540X20 for 27 of them). This is because the height of the bitmap is currently used as the width of the individual pictures, but this could be easily changed. I started to implement AVI-like items by storing a first picture offset and last picture offset. Going the extra step to rotate through these pictures every few frames is a simple exercise I have left for the reader if you are inclined (or you can just create separate items instead).

POSITION posEmot = m_ae.CreateItem( "" )
.GoAfter( pos )
.SetBitmap( IDB_EMOTICONS, m_ae.Rand(27) )
.SetStart( m_ae.RandPt() )
.SetFinish( m_ae.RandPt() )
.SetFrames( 150 )
.GetPos();

Notice that the engine's random number methods are used to choose one of the 27 emoticons, and start and finish points on the screen.

How This Animation Works and Its Limitations

The animation implemented here uses a simple principle: to draw a single frame, each item is erased if it moves and is redrawn in its new position. The time between one frame and the next is what allows the scene to look solid rather than flashy. To erase a text item, the text is drawn over its old position in the background color. To erase a bitmap and reduce flashing, only the area to the side and top or bottom that is being vacated is filled with the background color.

One problem is that depending on the order of items in the list, they will have small conflicts with each other such as erasing part of an item that was already drawn. But to see this you have to slow it down to frame by frame.

This type of animation can only really work on a plain single colored background, unless you want smearing designs. In order to use a background image, one would start each frame with the original background image in memory, laying the items on in their new positions, and then only the affected regions are drawn directly onto the screen.

Accessing the Web Service

The CHttpThread class is used to go out and asynchronously access the Code Project web service in the background. It posts a WM_APP message when the information has been successfully received. The screen saver window copies the resulting information before submitting another request.

Simply put, being asynchronous allows you to exit the application at any time instantly without waiting for any current internet access to complete. HTTP GET retrieves the information just as if you click on the following link, try it:

http://www.codeproject.com/webservices/latest.asmx/GetLatestArticleBrief?NumArticles=1

The result is a text document that happens to be XML. This XML is navigated using CMarkup, a small footprint XML class presented in my other article markupclass.asp.

The Code Project Web Service currently has 11 operations described and demonstrated at latest.asmx in a very simple and helpful way. Even if you don't intend to program with it, I recommend that you check it out.

Downloading and Refreshing

All these web service operations are queued up and performed one after another when the screen saver starts up. See the CallWebService and ProcessWebInfo methods. The operations are numbered 0 to 10 and are fully described in the source code.

Four of these operations provide the interval in minutes for how often to download the summaries. Although it is a simple concept, implementing this can be a little tricky. First of all I keep times as integer seconds since the screen saver was started. For each type of interval-based web service access I store the time of the most recent request sent to the web service. In OnTimer whenever there are no web service requests pending, I compare the current time (in seconds since start of screen saver) to the stored time of each type of access. When it is time to submit another request, I update the stored time of most recent request to the current time.

Make Your Own

Feel free to develop your own screen savers and animations using this project source code. Let me know if you create some cool designs and enhancements.

Enjoy!

Ben Bryant, bcbryant@firstobject.com, www.firstobject.com

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Raised in Southern Ontario Canada. Bachelor of Science from the University of Toronto in Computer Science and Anthropology. Living near Washington D.C. in Virginia, USA.

Comments and Discussions

 
GeneralMulti monitors Pin
Christian Graus18-May-02 13:06
protectorChristian Graus18-May-02 13:06 
GeneralRe: Multi monitors Pin
Ben Bryant20-May-02 3:43
Ben Bryant20-May-02 3:43 
GeneralRe: Multi monitors Pin
Christian Graus20-May-02 4:06
protectorChristian Graus20-May-02 4:06 
GeneralVery Nice Pin
Jon Newman18-May-02 11:53
Jon Newman18-May-02 11:53 
GeneralRe: Very Nice Pin
Ben Bryant20-May-02 3:49
Ben Bryant20-May-02 3:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.