- Get Acquainted with Borland C++ 5.5.1
- Tour the Screensavers Library
- Presenting a Circles and Rectangles Screensaver
- Conclusion
Presenting a Circles and Rectangles Screensaver
We now have a screensavers library, and the next logical step is to create a simple screensaver that shows off the library’s capabilities. To that end, I created an sldemo.scr screensaver that draws colored circles or rectangles all over the screen. A prebuilt version of sldemo.scr is included in the code that accompanies this article.
Before exploring its source code, let’s visualize this screensaver. Copy sldemo.scr to the appropriate screensavers directory on your platform (I copied sldemo.scr to c:\windows on my Windows 98 SE platform). Activate your Display Properties dialog box (I right-click my desktop and select the Properties menu item from the popup menu) and select sldemo from the Screen Saver drop-down listbox on that dialog box’s Screen Saver tab (or do whatever is equivalent for your platform). Figure 2 reveals this screensaver running in the preview window.
Figure 2 Display Properties reveals sldemo as the current screensaver.
You can continue to observe sldemo in the preview window, or click the Preview button to see this screensaver operating in full-screen mode. After awhile, you might want to change the shape from circle to rectangle; you might also want to change the maximum number of shapes that are displayed (before the screen erases) to something other than the 50 default. Either task is accomplished from the configuration dialog box, which you activate by clicking the Settings button. Figure 3 presents this dialog box.
Figure 3 Configure the screensaver to display any number of circles or rectangles.
Having visualized sldemo.scr’s GUI, let’s examine the source code. This screensaver’s source code is presented in Listing 6.
Listing 6 sldemo.c
// sldemo.c // Screensaver library demonstration program. #include <windows.h> #include "sldemo.h" #include "slib.h" #pragma resource "sldemo.res" // configuration data -- type of shape to draw and maximum number of shapes that // can be drawn before clearing the screen static enum {shCircle, shRectangle} g_shape = shCircle; static int g_iMaxDrawnShapes = 50; // local function prototypes void ReadConfig (void); void WriteConfig (void); // ========================================================================= // BOOL WINAPI RegisterDialogClasses (HANDLE hinstance) // // Allow the programmer to register any child control windows. // // Arguments: // // hinstance - screensaver instance handle // // Return: // // TRUE if child control windows successfully created or no controls needed; // otherwise FALSE // ========================================================================= #pragma argsused BOOL WINAPI RegisterDialogClasses (HANDLE hinstance) { return TRUE; // no controls need to be registered. } // =========================================================================== // BOOL WINAPI ScreenSaverConfigureDialog (HWND hwnd, int iMsg, WPARAM wparam, // LPARAM lparam) // // Screensaver-specific configuration procedure for processing messages. // // Arguments: // // hwnd - handle of window associated with this screensaver procedure // // iMsg - message identifier // // wparam - 32-bit word parameter with message data (if applicable) // // lparam - 32-bit long word parameter with message data (if applicable) // // Return: // // TRUE if message processed; otherwise FALSE if message not processed or // Windows should not set the default keyboard focus (WM_INITDIALOG) // =========================================================================== #pragma argsused BOOL WINAPI ScreenSaverConfigureDialog (HWND hwnd, UINT iMsg, WPARAM wparam, LPARAM lparam) { char szBuffer [256]; switch (iMsg) { case WM_INITDIALOG: // Read configuration data from Windows registry. ReadConfig (); // Update dialog box controls based on configuration data. SendDlgItemMessage (hwnd, (g_shape == shCircle) ? IDR_CIRCLES : IDR_RECTANGLES, BM_SETCHECK, 1, 0); wsprintf (szBuffer, "%d", g_iMaxDrawnShapes); SetWindowText (GetDlgItem (hwnd, IDE_MDS), szBuffer); break; case WM_COMMAND: if (wparam == IDOK) { // Fetch configuration data from dialog box controls. if (!SendDlgItemMessage (hwnd, IDR_CIRCLES, BM_GETCHECK, 0, 0)) g_shape = shRectangle; else g_shape = shCircle; if (GetWindowText (GetDlgItem (hwnd, IDE_MDS), szBuffer, 256)) g_iMaxDrawnShapes = atoi (szBuffer); // Write configuration data to Windows registry. WriteConfig (); } if (wparam == IDOK || wparam == IDCANCEL) { // Cancel dialog box. EndDialog (hwnd, 0); return TRUE; } } return FALSE; } // ===================================================================== // LRESULT WINAPI ScreenSaverProc (HWND hwnd, int iMsg, WPARAM wparam, // LPARAM lparam) // // Screensaver-specific procedure for processing messages. // // Arguments: // // hwnd - handle of window associated with this screensaver procedure // // iMsg - message identifier // // wparam - 32-bit word parameter with message data (if applicable) // // lparam - 32-bit long word parameter with message data (if applicable) // // Return: // // 0 if message processed, otherwise DefScreenSaverProc() result // ===================================================================== LRESULT WINAPI ScreenSaverProc (HWND hwnd, UINT iMsg, WPARAM wparam, LPARAM lparam) { HDC hdc; RECT rect; int iWidth, iX, iY; HBRUSH hbrush, hbrushOld; static int iNumDrawnShapes; static UINT uTimerID; switch (iMsg) { case WM_CREATE: // Create an animation timer that results in a WM_TIMER message // approximately every 50 milliseconds. uTimerID = SetTimer (hwnd, 1, 50, NULL); // Read configuration data from Windows registry. ReadConfig (); return 0; case WM_DESTROY: // Destroy the timer. KillTimer (hwnd, uTimerID); // Inform Windows that screensaver is terminating. PostQuitMessage (0); return 0; case WM_TIMER: // Grab a device context associated with this window. hdc = GetDC (hwnd); // Obtain window coordinates. GetClientRect (hwnd, &rect); // Create a solid brush with a random color. hbrush = CreateSolidBrush (RGB (rand () % 256, rand () % 256, rand () % 256)); // Select solid brush into device context so that filled shape // appears in brush color. hbrushOld = SelectObject (hdc, hbrush); // Compute upper-left corner coordinates. iX = rand () % rect.right; iY = rand () % rect.bottom; // Compute width of shape -- 10% of the lesser of the window’s width // and height. iWidth = rect.right / 10; if (rect.bottom / 10 < iWidth) iWidth = rect.bottom / 10; // Draw either a filled ellipse or a filled rectangle. if (g_shape == shCircle) Ellipse (hdc, iX, iY, iX+iWidth, iY+iWidth); else Rectangle (hdc, iX, iY, iX+iWidth, iY+iWidth); // Increment drawn shape counter. If counter reaches the limit, // erase the screen and reset counter to 0. iNumDrawnShapes++; if (iNumDrawnShapes == g_iMaxDrawnShapes) { InvalidateRect (hwnd, NULL, TRUE); iNumDrawnShapes = 0; } // Select original brush into the device context and delete created // solid brush. SelectObject (hdc, hbrushOld); DeleteObject (hbrush); // Release the device context. ReleaseDC (hwnd, hdc); return 0; } return DefScreenSaverProc (hwnd, iMsg, wparam, lparam); } // ==================================================== // void ReadConfig (void) // // Read configuation information from Windows registry. // // Arguments: // // none // // Return: // // none // ==================================================== static void ReadConfig (void) { LONG res; HKEY skey; DWORD val, valSize, valType; // Open the specified key. res = RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\Jeff Friesen\\SLDEMO", 0, KEY_ALL_ACCESS, &skey); if (res == ERROR_SUCCESS) { // Fetch shape value from the Shape field of the open key. valSize = sizeof(val); res = RegQueryValueEx (skey, "Shape", 0, &valType, (LPBYTE)&val, &valSize); if (res==ERROR_SUCCESS) g_shape = val; // Fetch maximum number of drawn shapes value from the MDS field of the // open key. valSize = sizeof(val); res = RegQueryValueEx (skey, "MDS", 0, &valType, (LPBYTE)&val, &valSize); if (res==ERROR_SUCCESS) g_iMaxDrawnShapes = val; // Close the open key. RegCloseKey (skey); } } // =================================================== // void WriteConfig (void) // // Write configuation information to Windows registry. // // Arguments: // // none // // Return: // // none // =================================================== static void WriteConfig (void) { LONG res; HKEY skey; DWORD disp, val; // Create the specified key if it does not exist or open the key if it does // exist. res = RegCreateKeyEx (HKEY_CURRENT_USER, "Software\\Jeff Friesen\\SLDEMO", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &skey, &disp); if (res == ERROR_SUCCESS) { // Store shape value in the Shape field of the open key. val = g_shape; RegSetValueEx (skey, "Shape", 0, REG_DWORD, (CONST BYTE *) &val, sizeof(val)); // Store maximum number of drawn shapes value in the MDS field of the // open key. val = g_iMaxDrawnShapes; RegSetValueEx (skey, "MDS", 0, REG_DWORD, (CONST BYTE *) &val, sizeof(val)); // Close the open key. RegCloseKey (skey); } }
Because of the many comments in sldemo.c, I don’t have much to say about this source file. As with all other screensaver source files, sldemo.c needs to include the slib.h header file. Also, because screensavers have resource files, those resources need to be linked into their final executables. The way to do this with Borland C++ 5.5.1 is to include a #pragma resource directive in the source code: #pragma resource "sldemo.res" accomplishes this task for sldemo.c. Finally, configuration data is loaded from and saved to the Windows registry. I used Software\\Jeff Friesen\\SLDEMO in conjunction with HKEY_CURRENT_USER to form the registry key in which I store my configuration data for sldemo.scr. You might want to adopt this format for your own screensavers. Simply replace Jeff Friesen with your company name, and SLDEMO with the name of your screensaver.
In addition to slib.h, the sldemo.c source code depends on the sldemo.h header file and the sldemo.rc resource file. Listing 7 presents sldemo.h.
Listing 7 sldemo.h
// sldemo.h #define IDE_MDS 100 #define IDG_MDS 200 #define IDG_SHAPES 201 #define IDR_CIRCLES 300 #define IDR_RECTANGLES 301
The various constants in sldemo.h identify control resources in the configuration dialog box area of the sldemo.rc resource file. That file’s contents are shown in Listing 8.
Listing 8 sldemo.rc
// sldemo.rc #include <windows.h> #include "sldemo.h" #include "slib.h" DLG_SCRNSAVECONFIGURE DIALOG 0, 0, 121, 116 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "SLDEMO" FONT 8, "MS Shell Dlg" { GROUPBOX "Shape", IDG_SHAPES, 8, 3, 103, 33, BS_GROUPBOX AUTORADIOBUTTON "Circles", IDR_CIRCLES, 11, 17, 35, 12, BS_AUTORADIOBUTTON | WS_TABSTOP AUTORADIOBUTTON "Rectangles", IDR_RECTANGLES, 58, 17, 50, 12, BS_AUTORADIOBUTTON | WS_TABSTOP GROUPBOX "Maximum drawn shapes", IDG_MDS, 8, 45, 103, 33, BS_GROUPBOX EDITTEXT IDE_MDS, 14, 58, 91, 12, WS_BORDER | WS_TABSTOP DEFPUSHBUTTON "OK", IDOK, 38, 94, 48, 14 } ID_APP ICON "sldemo.ico" STRINGTABLE BEGIN IDS_DESCRIPTION, "SLDEMO" END VERSIONINFO_1 VERSIONINFO FILEVERSION 1, 0, 0, 0 PRODUCTVERSION 1, 0, 0, 0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "CompanyName", "Jeff Friesen\000\000" VALUE "FileDescription", "Screensaver library demonstration screensaver\000" VALUE "FileVersion", "1.00\000\000" VALUE "InternalName", "sldemo\000" VALUE "LegalCopyright", "Copyright © Jeff Friesen 2006\000\000" VALUE "OriginalFilename", "sldemo\000" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x409, 1252 } }
Don’t worry about the copyright notice in Listing 8’s version resource. It’s present only to illustrate how you might embed your own copyright information when creating your screensavers.
Build the screensaver
I built sldemo.scr with the help of a batch file called makedemo.bat. Let’s examine that batch file’s contents, so that you will know how to build sldemo.scr and other screensavers. Listing 9 presents the contents of makedemo.bat.
Listing 9 makedemo.rc
brc32 -r -32 -i"c:\borland\bcc55\include" sldemo.rc bcc32 -tW -I"c:\borland\bcc55\include" -L"c:\borland\bcc55\lib" sldemo.c slib.lib copy sldemo.exe sldemo.scr erase sldemo.exe
The batch file first invokes the brc32.exe resource compiler to convert sldemo.rc into the binary sldemo.res file. It is this resource file that is included by the #pragma resource directive in sldemo.c.
The batch file next invokes bcc32.exe to compile sldemo.c and link the resulting sldemo.obj file with slib.lib. After the resulting sldemo.exe file is copied to sldemo.scr, that executable is erased (because it is no longer needed).
Before you run makedemo, you must ensure that Borland C++’s Bin directory is included in the PATH and that the current directory includes all of sldemo’s files, along with slib.h and slib.lib. From the command line, invoke makedemo. If there are no problems, you should discover an sldemo.scr file with a file size of 54,784 bytes.
As an interesting exercise, modify sldemo.c’s ScreenSaverProc() function to include case WM_ERASEBKGND: return 1; in its switch statement. Rebuild and rerun the screensaver. After clicking the Preview button, you’ll notice that the screensaver overwrites the desktop instead of first blanking the screen to black.