2 years ago 2 years ago C++ Share

Pretty Lines – A Win32 C++ Screen Saver with Source Code

This page gives an example of C++ Win32 source code for a Windows screen saver I wrote as a learning exercise, when I was first learning Win32 programming. You can view and download the source, and/or download the compiled working Win32 screen saver, here on this page.

I wrote it around the year 2000, and it still works now on Windows 10, and would probably work on Windows 11 (and probably all previous versions of Windows, from Windows 95 onwards.

Though it doesn't handle multiple monitors ideally well (since in 2000 no-one that I knew of really used two screens on the one PC). It treats multiple monitors/screens all as one big screen. So if you have two, it will treat the join between the two screens as the "centre" of what it treats as the one screen, and draw the pattern from there. Which still works fine, though it doesn't look as good I think as it did on the one screen.

Also, you don't really need a screen saver on most modern screens, so it's value here on Codewiz is more historical and educational than anything else.

How the Code Example Works

I'll update this web page in more detail later...

(At this stage I'm still trying to get enough pages up on the new website so there's a decent amount of content here — and also some material which I can use as a demonstration of coding experience in various different languages and frameworks.)

This program was essentially just a more fancy version of "Hello World". That is, something I wrote quickly when I first learned Win32 C++ programming, just to get some practice getting a running Windows program.

As I recall, there was a pre-built framework included as part of Visual C++ which had an option to make a screen saver. So Microsoft Visual C++ added most of the code — and all I had to actually write was some sort of algorithm to draw something onto the screen. Plus there was an optional control box for settings — the one as used in Pretty Lines just lets you adjust the speed of how fast it draws the lines.

The algorithm is more simple than it looks, its basically just two spirals, which rotate in opposite directions to each other, and have a gradually increasing radius. There are many things which are randomly chosen, including the rate/speed at which the spirals change their angle and their radius. Then the program just draws a line between the co-ordinates of each of the two spirals. Also, the colours change at a random rate (which is occasionally zero, and a single pattern will be all the one same colour then) through the full spectrum of hues. After it's drawn a certain number of lines with both ends off the screen, it ends, clears the screen, and starts drawing a new pattern. That's pretty much it.

I had just finished a degree in Astrophysics not long before I began this project, so the mathematics as needed for the above algorithm was a lot easier for me to think up quickly than it would have been otherwise.

The cover photo of this web page is a screenshot from it working. Some of the patterns look better than others, due to the many randomly chosen variables each time it starts drawing a new pattern.

I'll look over this project more later... (It's been years since I've even thought about "Pretty Lines", until the other day, when I realised it would make a good web page here — and I didn't have any web pages under the C++ coding category yet).

Download Pretty Lines

This is a link to download the final working version of Pretty Lines, as a Windows screen saver .scr file. If you download it in Windows, you can right-click on the Pretty-Lines.scr file, and select "Test" to see what it looks like, and/or "Install" if you want to try using it as a screen saver.

To be honest, I'm surprised that it still works as well as it does on Windows 10 — considering that Pretty Lines is 12 years old now, and hasn't been modified at all (or re-compiled, etc.) since then.

This is a link to download a .zip file of the entire package of Pretty Lines, including the released .scr file, and all the C++ source and build files as used in Microsoft Visual Studio, in whatever version of that was current in 1999-2000. Sometime I might try installing a current version of Visual C++ Studio and see how well it still works and re-compiles.

Licensing

I really have no idea what the licence for this would be. I wrote it all myself — though I did some of it I while I was working as an employee, on company time, and some of it in my own time.

It was pretty much just a learning exercise, and an extension of the study I was doing (and the study was also partly on company time and partly on my own time). Pretty Lines really didn't take very long to write, and it was never released as a product (or any part of a product) by the company I was working for.

The situation was basically: Install Microsoft Visual C++, see if it's working and get familiar with it by writing something quick, and then move on to proper work. This screen saver was the "write something quick" step of that process.

Nearly all the Win32 C++ code which I wrote, and worked with in the past, was done as an employee, and was part of released products, so I can't publish any of it (nor do I even still have any of it, except for Pretty Lines).

Win32 Source Code in C++ for the Pretty Lines Windows Screen Saver

// Pretty Lines.cpp : Defines the entry point for the application. //

#include "stdafx.h"
#include "resource.h"
#include <scrnsave.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>

int i = 0; //counter used for redrawing in four different colours
int nColor = 0, nDeltaColor, nRed, nGreen, nBlue;
int nXres, nYres; // size of window/screen
double lfRadius1, lfRadius2, lfTheta1, lfTheta2; 
double lfDeltaRadius1, lfDeltaRadius2;
double lfDeltaTheta1, lfDeltaTheta2;
int lSpeedPermanent;
int nErrorCode = 0, nCounter = 0;

void InitPattern(HDC hdc)
{
			lfRadius1 = rand() % 200;
			lfRadius2 = rand() % 20;
			lfTheta1 = (double)rand() / RAND_MAX * 3.14159;
			lfTheta2 = (double)rand() / RAND_MAX * 3.14159;

			lfDeltaRadius1 = rand() % 7 + 1;
			lfDeltaRadius2 = rand() % 10 + 1;
			lfDeltaTheta1 = ((double)(rand() % 10)) / 60;
			lfDeltaTheta2 = -((double)(rand() % 20)) / 60;

			nColor = rand() % 1530;
			nDeltaColor = rand() % 10 - 5;
			if (abs(nDeltaColor) > 3) nDeltaColor *= 3;

		//	SelectObject(hdc, CreatePen(PS_SOLID, 1,
		//		RGB(nRed, nGreen, nBlue)));
}

/*
void ColorLookup(int nColor) // This one doesn't look as good
{
	
	if (nColor < 255)
	{
		nBlue = 255 - nColor;
		nGreen = nColor;
		nRed = 0;
	}

	else if (nColor < 510)
	{
		nBlue = 0;
		nGreen = 255 - (nColor - 255);
		nRed = nColor - 255;
	}

	else
	{
		nBlue = nColor - 510;
		nGreen = 0;
		nRed = 255 - (nColor - 510);
	}
}

*/
void ColorLookup(int nColor)
{
	if (nColor <= 255)
	{
		nBlue = 255;
		nGreen = nColor;
		nRed = 0;
	}

	else if (nColor <= 510)
	{
		nBlue = 510 - nColor;
		nGreen = 255;
		nRed = 0;
	}

	else if (nColor <= 765)
	{
		nBlue = 0;
		nGreen = 255;
		nRed = nColor - 510;
	}

	else if (nColor <= 1020)
	{
		nBlue = 0;
		nGreen = 1020 - nColor;
		nRed = 255;
	}

	else if (nColor <= 1275)
	{
		nBlue = nColor - 1020;
		nGreen = 0;
		nRed = 255;
	}

	else
	{
		nBlue = 255;
		nGreen = 0;
		nRed = 1530 - nColor;
	}

	nBlue /= 1.2;
	nGreen /= 1.2;
	nRed /= 1.2;
}

/*

LONG WINAPI ScreenSaverProc(HWND, UINT, DWORD, LONG);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{

	// TODO: Place code here.
	static char szAppName[] = "Pretty Lines";
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;

	if (!hPrevInstance)
	{
		wndclass.style			= CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc	= ScreenSaverProc;
		wndclass.cbClsExtra		= 0;
		wndclass.cbWndExtra		= 0;
		wndclass.hInstance		= hInstance;
		wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
		wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
		wndclass.hbrBackground  = reinterpret_cast<HBRUSH>
			(GetStockObject(WHITE_BRUSH));
		wndclass.lpszMenuName	= NULL;
		wndclass.lpszClassName  = szAppName;

		RegisterClass(&wndclass);
	}

	hwnd = CreateWindow(szAppName,		// window class name
			"Pretty Lines",				// window caption
			WS_OVERLAPPEDWINDOW,		// window style
			CW_USEDEFAULT,				// init x pos
			CW_USEDEFAULT,				// init y pos
			CW_USEDEFAULT,				// init x size
			CW_USEDEFAULT,				// init y size
			NULL,						// parent window handle
			NULL,						// window menu handle
			hInstance,					// program instance handle
			NULL);						// creation parameters

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

*/

#define MINVEL  1               /* minimum redraw speed value    */ 
#define MAXVEL  10              /* maximum redraw speed value    */ 
#define DEFVEL  5               /* default redraw speed value    */ 
 
LONG    lSpeed = DEFVEL;        /* redraw speed variable         */ 
 
extern HINSTANCE hMainInstance; /* screen saver instance handle  */ 
 
//CHAR   szAppName[80]; 
//CHAR   szIniFile[80];           /* .ini section name             */ 
CHAR   szTemp[20];              /* temporary array of characters */ 
CHAR   szRedrawSpeed[] = "Redraw Speed";   /* .ini speed entry   */ 
 
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, 
				UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    static HWND hSpeed; /* handle to speed scroll bar */ 
    static HWND hOK;    /* handle to OK push button */ 
 
    switch(message) 
    { 
        case WM_INITDIALOG: 
 
            /* Retrieve the application name from the .rc file. */ 
 
            LoadString(hMainInstance, IDS_APP_NAME, szAppName, 40); 
 
            /* Retrieve the .ini (or registry) file name. */ 
 
            LoadString(hMainInstance, IDS_INI_FILE, szIniFile, 
                MAXFILELEN); 
 
            /* Retrieve any redraw speed data from the registry. */ 
 
            lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed, 
                DEFVEL, szIniFile); 
 
            /* 
             * If the initialization file does not contain an entry 
             * for this screen saver, use the default value. 
             */ 
 
            if(lSpeed > MAXVEL || lSpeed < MINVEL) 
                lSpeed = DEFVEL; 
 
            /* Initialize the redraw speed scroll bar control. */ 
 
            hSpeed = GetDlgItem(hDlg, ID_SPEED); 
            SetScrollRange(hSpeed, SB_CTL, MINVEL, MAXVEL, FALSE); 
            SetScrollPos(hSpeed, SB_CTL, lSpeed, TRUE); 
 
            /* Retrieve a handle to the OK push button control. */ 
 
            hOK = GetDlgItem(hDlg, ID_OK); 
 
            return TRUE; 
 
        case WM_HSCROLL: 
 
            /* 
             * Process scroll bar input, adjusting the lSpeed 
             * value as appropriate. 
             */ 
 
            switch (LOWORD(wParam)) 
                { 
                    case SB_PAGEUP: 
                        --lSpeed; 
                    break; 
 
                    case SB_LINEUP: 
                        --lSpeed; 
                    break; 
 
                    case SB_PAGEDOWN: 
                        ++lSpeed; 
                    break; 
 
                    case SB_LINEDOWN: 
                        ++lSpeed; 
                    break; 
 
                    case SB_THUMBPOSITION: 
                        lSpeed = HIWORD(wParam); 
                    break; 
 
                    case SB_BOTTOM: 
                        lSpeed = MINVEL; 
                    break; 
 
                    case SB_TOP: 
                        lSpeed = MAXVEL; 
                    break; 
 
                    case SB_THUMBTRACK: 
                    case SB_ENDSCROLL: 
                        return TRUE; 
                    break; 
                } 
                if ((int) lSpeed <= MINVEL) 
                    lSpeed = MINVEL; 
                if ((int) lSpeed >= MAXVEL) 
                    lSpeed = MAXVEL; 
 
                SetScrollPos((HWND) lParam, SB_CTL, lSpeed, TRUE); 
            break; 
 
        case WM_COMMAND: 
            switch(LOWORD(wParam)) 
            { 
                case ID_OK: 
 
                   /* 
                    * Write the current redraw speed variable to 
                    * the .ini file. 
                    */ 
 
                    wsprintf(szTemp, "%ld", lSpeed); 
				//	wsprintf(szIniFile, "hahahaha.haa");
                    WritePrivateProfileString(szAppName, szRedrawSpeed, 
                        szTemp, szIniFile); 
					//	szTemp, "ggggg.ini");

                case ID_CANCEL: 
                    EndDialog(hDlg, LOWORD(wParam) == ID_OK); 
					return TRUE; 
            } 
    } 
    return FALSE; 
} 

LONG WINAPI ScreenSaverProc(HWND hwnd, UINT message, 
							WPARAM wParam, LPARAM lParam) 
{ 
static HDC          hdc;    //device-context handle  
static RECT         rc;     // RECT structure  
static UINT         uTimer; // timer identifier 
 
    switch(message) 
    { 
        case WM_CREATE: 
 
            // Retrieve the application name from the .rc file.  
 
            LoadString(hMainInstance, IDS_APP_NAME, szAppName, 40); 
 
            // Retrieve the .ini (or registry) file name. 
 
            LoadString(hMainInstance, IDS_INI_FILE, szIniFile, 
                MAXFILELEN); 
 
            // Retrieve any redraw speed data from the registry. 
 
            lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed, 
                DEFVEL, szIniFile); 
			
			lSpeedPermanent = lSpeed;

			// Get the size of the display window

			// Seed random number generator
			srand((unsigned)time(NULL));

			// Set up the variables for the pattern generating

			/*lfRadius1 = 200.0;
			lfRadius2 = 20.0;
			lfTheta1 = 0;
			lfTheta2 = 1.57;

			lfDeltaRadius1 = 3;
			lfDeltaRadius2 = 5;
			lfDeltaTheta1 = 0.1;
			lfDeltaTheta2 = -0.2;*/

			/*lfRadius1 = rand() % 200;
			lfRadius2 = rand() % 20;
			lfTheta1 = 0;
			lfTheta2 = 1.57;

			lfDeltaRadius1 = rand() % 7 + 1;
			lfDeltaRadius2 = rand() % 10 + 1;
			lfDeltaTheta1 = ((double)(rand() % 10)) / 30;
			lfDeltaTheta2 = -((double)(rand() % 20)) / 30;
            */
			  
			InitPattern(hdc);

			// 
            // Set a timer for the screen saver window using the 
            // redraw rate stored in Regedit.ini. 
            // 
 
            uTimer = SetTimer(hwnd, 1, 
			         (int)pow(2.0, (double)lSpeed) * 1.5625, NULL); 
            break; 
 
        case WM_ERASEBKGND: 
 
           // 
           // The WM_ERASEBKGND message is issued before the 
           // WM_TIMER message, allowing the screen saver to 
           // paint the background as appropriate. 
           // 
 
            hdc = GetDC(hwnd); 
            GetClientRect (hwnd, &rc); 
            FillRect (hdc, &rc, 
				reinterpret_cast<HBRUSH>
					(GetStockObject(BLACK_BRUSH))); 
            ReleaseDC(hwnd,hdc); 
            break; 

		case WM_SIZE:

			nXres = LOWORD(lParam);
			nYres = HIWORD(lParam);
			break;

        case WM_TIMER: 
 
           // 
           // The WM_TIMER message is issued at (lSpeed * 1000) 
           // intervals, where lSpeed == .001 seconds. This 
           // code repaints the entire desktop with a white, 
           // * light gray, dark gray, or black brush each 
           // time a WM_TIMER message is issued. 
           // 
 
            hdc = GetDC(hwnd); 
            GetClientRect(hwnd, &rc); 
			CHAR sStatus[120];
			POINT Coordinates[2];
			Coordinates[0].x = lfRadius1 * cos(lfTheta1) + nXres / 2;
			Coordinates[0].y = lfRadius1 * sin(lfTheta1) + nYres / 2;
			Coordinates[1].x = lfRadius2 * cos(lfTheta2) + nXres / 2;
			Coordinates[1].y = lfRadius2 * sin(lfTheta2) + nYres / 2;			
			
			lfRadius1 += lfDeltaRadius1;
			lfRadius2 += lfDeltaRadius2;
			lfTheta1  += lfDeltaTheta1;
			lfTheta2  += lfDeltaTheta2;

			nColor += nDeltaColor;
			if (nColor >= 1530) nColor -= 1530;
			if (nColor <= -1) nColor += 1530;
			ColorLookup(nColor);

			if(nRed < 0 || nRed > 255 || nGreen < 0 || nGreen > 255 ||
				nBlue < 0 || nBlue > 255)
				nErrorCode = 10;

			HGDIOBJ PrevObject, CurrentObject;
			CurrentObject = CreatePen(PS_SOLID, 1, 
								RGB(nRed, nGreen, nBlue));

			PrevObject = SelectObject(hdc, CurrentObject);

			 // if (nCounter > 9800) 
			Polyline(hdc, Coordinates, 2);

			SelectObject(hdc, PrevObject);
			DeleteObject(CurrentObject);

		//	wsprintf(sStatus, "@@ nColor=%d, nDeltaColor=%d,
		//		R=%d, G=%d, B=%d, ErrorCode=%d, nCounter=%d ",
		//		nColor, nDeltaColor, nRed, nGreen, nBlue,
		//		nErrorCode, nCounter);
		//	DrawText(hdc, sStatus, -1, &rc, DT_SINGLELINE);
			
			if ((Coordinates[0].x < 0 || Coordinates[0].x > nXres ||
				Coordinates[0].y < 0 || Coordinates[0].y > nYres) &&
				(Coordinates[1].x < 0 || Coordinates[1].x > nXres ||
				Coordinates[1].y < 0 || Coordinates[1].y > nYres))
				i++;
			
			nCounter++;

			if (i == 35) 
			{
                FillRect(hdc, &rc, 
				reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH))); 
				//PatBlt(hdc, i*100, i*70, 100, 100, BLACKNESS);
				i = 0;
				InitPattern(hdc);
			}
			//else 
            //    (i = 0); 

            ReleaseDC(hwnd,hdc); 
            break; 
 
        case WM_DESTROY: 
 
           // 
            // When the WM_DESTROY message is issued, the screen saver 
            // must destroy any of the timers that were set at WM_CREATE 
            // time. 
            // 
 
            if (uTimer) 
                KillTimer(hwnd, uTimer); 
            break; 
    } 
 
   // 
    // DefScreenSaverProc processes any messages 
    // ignored by ScreenSaverProc. 
   /// 
 
    return DefScreenSaverProc(hwnd, message, wParam, lParam); 
} 

BOOL WINAPI RegisterDialogClasses(HANDLE hInst) 
 
{ 
    return TRUE; 
} 

I'll further tidy up the code formatting, and explain more of the code (and the other functions it calls, etc.) later on...

Categories Cpp,Coding
Byte.Yoga Homepage - Australian Cyber Security Web Magazine

Share This Page

If you liked this page, please share it with others! You can use the links to share on Facebook, Twitter, LinkedIn, Pinterest, and Email. Ther is also an RSS feed to get updates for the website.