In this article we show you how to use OpenGL to create a screen saver; you may use .NET if you want. The code shown here can be adapted to work with any rendering tool such as DirectX or even GDI. For this the knowledge of Win32 and some OpenGL is a prerequisite, although you can get along just fine learning from the provided source code. A Windows screen saver is a normal executable file (yes, a '.exe' file) with a .scr extension, the only difference lies in the way a program is launched and the program's response to the arguments that have been passed to it. The table given below lists the arguments passed to the screen saver and their meanings.
Another factor that differentiates a screen saver from a regular application program is the way in which it responds to certain messages, such as pressing the key and moving or clicking the mouse.This message handling is done in the message loop of the Windows procedure. We can generate a full screen display by getting the screen resolution, creating a window with no borders or title (pop-up style) and using an API (OpenGL in this case). Though Visual Studio also provides the functionality to create screen savers in the 'scrnsave' library, not too many people may have its licensed version. On the other hand, the code shown here will run on any free compiler for Windows (such as BC 5.5) and produce the same effect although with a little extra effort.
|
Let's first create a full screen OpenGL application using Win32. After this, initiate the appropriate action according to the command-line arguments passed when the application is launched. The OpenGL specific code, which draws a part of a solar system, is written first.
#define THRESHHOLD 1
#include
#include
#include
int firstTime,curx,cury;
//------------------------OpenGL specific code-----------------
// Lighting values
GLfloat whiteLight<> = { 0.2f, 0.2f, 0.2f, 1.0f };
.
.
void renderscene(void)
{
.
.
// Set material color, Red
// Sun
.
.
// Rotate coordinate system
glRotatef(fEarthRot, 0.0f, 1.0f, 0.0f);
.
.
fEarthRot += 2.0f;
if(fEarthRot > 360.0f)
fEarthRot = 0.0f;
}
void Init()
{
// Light values and coordinates
glEnable(GL_DEPTH_TEST); // Hidden surface removal
glFrontFace(GL_CCW);
.
.
}
void resize(GLsizei w,GLsizei h)
{
.
.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// Window procedure, handles all messages for this program
LRESULT CALLBACK WndProc( HWND hWnd,UINT message,WPARAM wParam, LPARAM lParam)
{
static HGLRC hRC; // Permenant Rendering context
static HDC hDC; // Private GDI Device context
static POINT pt;
static int mx,my;
switch (message)
{
// ------------------Window creation----------------------------
case WM_CREATE:
// Store the device context
hDC = GetDC(hWnd);
.
.
break;
//----------- Window is being destroyed, cleanup------------- case WM_DESTROY:
.
.
resize(LOWORD(lParam), HIWORD(lParam));
break;
case WM_TIMER:
InvalidateRect(hWnd,NULL,FALSE);
break;
case WM_PAINT:
renderscene();
.
.
//-----these are messages that cause the screensaver to be terminated--
case WM_MOUSEMOVE:
if(firstTime)
{
GetCursorPos(&pt);
curx=pt.x;
cury=pt.y;
firstTime=0;
}
else
{
GetCursorPos(&pt);
mx=curx - pt.x;
my=cury-pt.y;
if(my<0)
my=my*-1;
if(mx<0)
mx*=-1;
}
if(mx>THRESHHOLD || my >THRESHHOLD)
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
PostQuitMessage(0);
default: // Passes it on if unproccessed
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0L);
}
The code for message loop differentiates this screen saver from a Windows application. Each of WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_KEYDOWN, WM_SYSKEYDOWN (all have self explanatory names) should cause the screen saver to stop. Each message should terminate the application, which is done by issuing the PostQuitMessage() function. The WM_MOUSEMOVE message is a little different though. If you terminate the application when this message is received, the screen saver becomes too sensitive. You can counter this by calculating the movement of the mouse by getting the new mouse coordinates and comparing them with the old coordinates. If the difference is greater than a given threshold then you can safely post a quit message.
Argument | What it means passed |
/c or -c or c |
Open the screen saver configu-ration dialog with the parent set to the handle of the calling program. This is used when configured from the control panel |
/p or -p or p |
Preview mode. It is followed by a handle to a window where a screensaver preview can be rendered |
/s or -s or s |
Run the actual screen saver None Run the configuration dialog for the screen saver |
int APIENTRY WinMain(HINSTANCE hinstance,HINSTANCE hprevinstance,LPSTR lpcmdline,int
ncmdshow)
{
.
.
//what have i been called as?
if(strcmpi(lpcmdline,"/s")!=0&&strcmpi(lpcmdline,"-s")!=0&& strcmpi(lpcmdline,"s")!=0)
{
if(strcmpi(lpcmdline,"/c")==0||strcmpi(lpcmdline,"-c")==0||strcmpi(lpcmdline,"c")==0)
{
MessageBox(GetParent(NULL),"You can create the config window and put it right here","Supposed ScreenSaver Configuration Dialog",MB_OK);
return 0;
}
else if(lpcmdline<1>=='p')
{
return 0;
}
else
{
MessageBox(NULL,"You can create the config window and put it right here","Supposed ScreenSaver Configuration Dialog",MB_OK);
return 0;
}
}
firstTime=1;
.
.
// Create the main application window
hWnd = CreateWindow(
"Test ScreenSaver",
"Test ScreenSaver",
// OpenGL requires WS_CLIPCHILDREN and WS_CLIPSIBLINGS WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
// Window position and size
.
.
NULL);
.
.
// Process application messages until the application closes
while( GetMessage(&msg, NULL, 0, 0))
.
.
return msg.wParam;
}
Now let's write the WinMain() function. We have only shown the parts that handle the command-line arguments. The entire code is available in a ready to compile form as myss.c along with the compiled version as myss.scr. The code shown here checks for whether it has been launched in the screen saver mode/s. If yes, run the animation and if no, check for other options (/c and /p). The configuration dialog box is not needed but can be launched from the place currently held by the message box. You can also implement the preview mode optionally. If these options fail, the configuration dialog is launched.Now you have a screen saver skeleton. To compile this under BC 5.5, use the command line 'bcc32 -6 -tW -e"myss.scr" myss.c'.Once you have built it, you have to right click on the output file in explorer to either test the screen saver, configure it or install it.
Rakesh Iyer