|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
AcknowledgmentI would like to start off this article by thanking all of those individuals who have sent me unbelievable number of emails appreciating my previous articles, specially the ISAPI Extension one! You cannot imagine how I have been inspired by those lovely emails. I do appreciate your time, and I hope that this article suits your needs too. IntroductionUnlike the human life that could be hardly saved 'coz of being invaded by other different so-called humans, computer monitors could be easily saved against the phosphor burn using a screen saver! Today, I am going to talk about Windows Screen Savers, one of those interesting topics that most of the programmers out there would like to know about. FundamentalsA screen saver is just a plain Win32 application that will be launched automatically when the mouse and keyboard have been idle for a specified period of time. Whenever this timer expires, the user could see either a blank screen or a very complex animation. Thereafter, if the user presses a key on her keyboard or moves her mouse, the screen will be changed back to the normal one that disappeared when the screen saver started. To create your own screen saver, you have got two choices. First, you can create a normal Win32 application and handle the necessary messages sent to your window. Choosing the first way, you have to develop a program that creates a full screen window, as well as a window procedure that processes window messages sent to your application. Within the window procedure, you could have something as follows: switch(message) { case WM_SYSCOMMAND: if(wParam == SC_SCREENSAVE || wParam == SC_CLOSE) return FALSE; break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: case WM_KEYUP: case WM_MOUSEMOVE: //Quit the application here break; } In other words, you have to hard code the pieces required for your screen saver and take care of the messages that is common along all the Windows screen savers. The second choice, though, is trying to use a library file that is being shipped with your compiler and does all of the above-mentioned hard coding (and even more)! This way, you can focus on the actual problem - that is creating the visual effects. Obviously, we are going to use this library to avoid re-inventing wheels. The screen saver library (scrnsave.lib) contains the WNDCLASS cls; cls.hCursor = NULL; cls.hIcon = LoadIcon(hInst, MAKEINTATOM(ID_APP)); cls.lpszMenuName = NULL; cls.lpszClassName = "WindowsScreenSaverClass"; cls.hbrBackground = GetStockObject(BLACK_BRUSH); cls.hInstance = hInst; cls.style = CS_VREDRAW | CS_HREDRAW | CS_SAVEBITS | CS_DBLCLKS; cls.lpfnWndProc = (WNDPROC) ScreenSaverProc; cls.cbWndExtra = 0; cls.cbClsExtra = 0; In other words, it registers a black background window, having no mouse cursor and an icon identified by This procedure, like all the other window procedures, is declared as follows: LONG ScreenSaverProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); where Anyway, the library header file is named scrnsave.h and is placed in the include directory of the compiler. It contains the necessary prototypes to support the screen saver library. There are some global variables defined in the library and the extern HINSTANCE hMainInstance; extern BOOL fChildPreview; where GoalsWe are going to create a screen saver, named, Ball Fusion, that contains 7 balls moving after each other on a sine wave path: x = sin(ß) * cos(ß) * cos(2ß)
y = cos(ß)
where Other considerationsThe Ball Fusion will not have any configuration dialog box, and this means if you try to choose the Settings button in the screen saver control panel, you will get nothing in response. This is just because of the lack of time. Actually, my boss started to shout at me when he saw the screen saver running on my system at office! So I stopped any improvements, and that's why it has got no Settings box! Getting startedTo start, launch MSVC++ 6.0. From the "File" menu, select the "New" item. With the "Projects" tab selected, highlight "Win32 Application", and in the "Project Name" edit box, type BallFusion, and press the Ok button. Now, choose "An empty project" radio button, and press "finish" and "ok" respectively. Now, it is time to add the BallFusion.cpp to our project to start coding. To do so, select the "New" item from the "File" menu. Having the "C++ Source File" item selected in the "Files" tab, enter BallFusion as the "File Name" and press OK. This way, we have got the base framework in place. Open the BallFusion.cpp to insert the following code: #include "windows.h" #include "scrnsave.h" LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 0; } The screen saver library also needs us to include two other functions in our screen saver, BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg,
UINT message, WPARAM wParam, LPARAM lParam)
{
//We simply return FALSE to indicate that
//we do not use the configuration dialog box
return FALSE;
}
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
//Since we do not register any special window class
//for the configuration dialog box, we must return TRUE
return TRUE;
}
The next step is to add scrnsave.lib to the library modules we are going to use in our program. To do so, from the "Project" menu, select "Settings" and make the necessary changes shown below:
DescriptionHaving completed the setting process of the compiler options, it is now time to focus on the actual program. I have defined some global variables within the program, as follows: #define MAX_BALLS 7 #define pi 3.141592625 #define szAppName "BallFusion 1.0" #define szAuthor "Written by Mehdi Mousavi, © 2001" #define szPreview "Ball Fusion 1.0" typedef struct _BALLS { UINT uBallID; HICON hIcon; int x; int y; int angle; }BALLS; BALLS gBalls[] = {IDI_REDBALL, NULL, 0, 0, 0, IDI_GREENBALL, NULL, 0, 0, 25, IDI_BLUEBALL, NULL, 0, 0, 50, IDI_PURPLEBALL, NULL, 0, 0, 75, IDI_LIGHTREDBALL, NULL, 0, 0, 100, IDI_YELLOWBALL, NULL, 0, 0, 125, IDI_BLACKBALL, NULL, 0, 0, 150}; where When the screen saver procedure receives the for(i = 0; i < MAX_BALLS; i++) gBalls[i].hIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(gBalls[i].uBallID), IMAGE_ICON, 48, 48, LR_DEFAULTSIZE); Then the starting position ( x = sin(ß) * cos(ß) * cos(2ß)
y = cos(ß)
In other words: xpos = GetSystemMetrics(SM_CXSCREEN) / 2; ypos = GetSystemMetrics(SM_CYSCREEN) / 2; for(i = 0; i < MAX_BALLS; i++) { double alpha = gBalls[i].angle * pi / 180; gBalls[i].x = xpos + int((xpos - 30) * sin(alpha) * cos(alpha) * cos(2 * alpha)); gBalls[i].y = ypos - 30 + int(265 * cos(alpha)); } And a black brush is created to be used while putting the icons on screen and a timer is started: hBrush = CreateSolidBrush(RGB(0, 0, 0)); uTimer = SetTimer(hWnd, 1, 1, NULL); When the timer is started, the screen saver procedure receives the Then, the rectangular area of which the ball should be invalidated is calculated and placed in the for(i = 0; i < MAX_BALLS; i++) { double alpha = gBalls[i].angle * pi / 180; gBalls[i].x = xpos + int((xpos - 30) * sin(alpha) * cos(alpha) * cos(2 * alpha)); gBalls[i].y = ypos - 30 + int(265 * cos(alpha)); gBalls[i].angle = (gBalls[i].angle >= 360) ? 0 : gBalls[i].angle + 1; rc.left = gBalls[i].x; rc.right = gBalls[i].x + 48; rc.top = gBalls[i].y; rc.bottom = gBalls[i].y + 48; InvalidateRect(hWnd, &rc, FALSE); } When the invalidate function is called, the program receives the if(fChildPreview) { SetBkColor(hDC, RGB(0, 0, 0)); SetTextColor(hDC, RGB(255, 255, 0)); TextOut(hDC, 25, 45, szPreview, strlen(szPreview)); } else { SetBkColor(hDC, RGB(0, 0, 0)); SetTextColor(hDC, RGB(120, 120, 120)); TextOut(hDC, 0, ypos * 2 - 40, szAppName, strlen(szAppName)); TextOut(hDC, 0, ypos * 2 - 25, szAuthor, strlen(szAuthor)); for(i = 0; i < MAX_BALLS; i++) DrawIconEx(hDC, gBalls[i].x, gBalls[i].y, gBalls[i].hIcon, 48, 48, 0, (HBRUSH)hBrush, DI_IMAGE); } And what about
Final noteThe compiler generates a .EXE file for this project. However, you need to rename it to .SCR so that the screen saver control panel can load it within the Screen Saver combo-box (as shown above). Another important thing to remember, is to include an ICON identified by cls.hIcon = LoadIcon(hInst, MAKEINTATOM(ID_APP)); That's all folks - Aloha! Further improvementsI've got an idea that improves this saver, that is keeping the current desktop as the background of the saver and start the animating process on that. To understand what I am talking about, add the Any comments, suggestions and/or questions are welcomed. | ||||||||||||||||||||