Lesson: MySQL Screensaver
An OpenGL screensaver harnessing the power of the MySQL database across a network
By Christopher Atkinson
Code ported from BC5.02 for MSVC by Brian Tegart


List of features that will be handled in the scope of this tutorial:
  • Creating an OpenGL window wrapper
  • Creating an outline font wrapper
  • Subclassing the OpenGL window
  • Using common controls
  • SQL language basics
  • Using MySQL to retrieve database records over a network
  • Setting up a screensaver framework
  • Using OpenGL to bind all of these together and create a nice looking screensaver
  • Reading from/writing into the Windows registry


The objective of this tutorial is to create a screensaver that, when executed, tries to access a database across the network at a specific IP to retrieve a poem from it in a modified text format that will then be displayed on the screen. If the server is offline or the database server is down, a built-in poem is used (I chose this to be "Funeral Blues" by W.H. Auden. Some of you may recognize it from the movie Three Weddings and a Funeral). The text is then displayed on the screen in an enjoyable fashion (lots of code for you to go through on your own :)).

To start off with this lengthy explanation, a few notes on this project as a whole are in order. We will be building a brand new base code (based on NeHe’s base code) that has one very different, but useful orientation: an Object Oriented approach. There’s quite a bit of code we will not go through in the tutorial itself, that will be commented in the source files – this will force you to read and try and understand it. All the essentials (the screensaver framework and MySQL stuff) will be explained in more detail, though. Why OO? Otherwise this screensaver would be very difficult to comprehend – especially if you’re interested in only one of the aspects (such as the screensaver framework, but not the wrapper classes). So, with that all aside, let’s get started!

The SQL language

One of the objectives of this tutorial is to demonstrate the use of libMySQL.dll (no, we will not be doing any connecting to a database on our own – instead we’ll let MySQL handle all of the database business). At this point, anyone with an understanding of how a database works and an understanding of the SQL language can skip this section.

What exactly is SQL? The acronym stands for Standard Query Language, which is the most widely used database language in the world. It is a very high level language and will therefore seem a whole lot like normal speech.

We will be concentrating on the MySQL database, which is used as the underlying database engine by almost any major wrapper (such as MS Access). There are many versions of the SQL language distributed by different vendors (Oracle, FoxPro, MS, etc), so finding a common way to communicate with them is almost impossible. We will be using, as mentioned above, the (freeware) MySQL library available at [1].

Databases traditionally contain two base types of entities: tables and queries. Tables are generally in the form:



ID (unsigned long / BIGINT)Poem (memo / LONGTEXT)Author (text / MEDIUMTEXT) Name (text / MEDIUMTEXT)Notes (memo / LONGTEXT)
[AutoNumber]Stop all the clocks, cut off the telephone, ...W. H. Auden Funeral Blues 
...............
...............

Table 1. An example of a table in a database


There are three key pieces of information in the above table (the types in capital letters are MySQL- specific and will be used in the next section – for now think of them as the equivalents in the types’ table below):
  1. In bold: field names. In a conventional database, there can be any number of fields. The one requirement is that they all have unique names, though.
  2. In italics: the field type. There are many field types (we will only discuss a few of these here) that allow a database programmer to either save space or allow for expansion. A database can contain such things as pictures and even 700 Megabyte movies (although linking to them via URL’s would be a lot more reasonable). Any decent database will be able to handle a lot of information. Just to give you a hint: think of a cross-US telephone network that has to handle all cell phone calls from and into the US in real-time. Each of these calls is logged (eg the time, caller and destination are stored in a database with perhaps other information as well), potentially resulting in an astounding several million calls per minute at prime time.

    The fields described in the above table:

    1. unsigned long – an either 32 or 64 bit variable (depending on which database engine you use)
    2. text – generally a string of up to 256 characters
    3. memo – a field type taken from MS Access since it so nicely describes a longer text (in Access, up to 65536 characters).


  3. [AutoNumber]: each table generally (but not always) requires a primary key – something that uniquely identifies each record in the table. This can be an integer value or some other value, or even a union of several values (for instance, if we didn’t have the ID field, but we still wanted to identify each field uniquely, we could have used the Author field. However, since one author has generally written more than one poem, we might have created a unique key by "uniting" the Author field with the Name field. Get it?). AutoNumber is a special value in a database – namely in this case it is up to the database engine to find a unique identifier for each record that is inserted (generally this number just keeps on increasing and is never reset unless we use some manual method to do it).
  4. The contents themselves: each of the lines describes a record in the database – this is pretty straightforward. The only note here is that any field that is not either a primary key or a secondary key, and is optional by definition can be left empty. These fields have the value NULL (which in no way means the same as 0).
The second important part of a database is queries. We will not go into detail about relationships because we only have one table to care about in this tutorial. "But, then – why do we need the ID field?" you may ask. Good question – we will cover this later.

There are three primary types of queries that you can use: the insert, select and delete queries. There are tons of additional keywords that allow for very intricate and complex queries (that can then take days to execute) for you to use. We will only be dealing with simple queries in this tutorial. We will now look at the use of the three above query types plus an example of how to start using a completely empty MySQL database with only command line support. Let’s suppose you have just got yourself a completely blank database (that contains no tables, no nothing), and you need to set up a viable database necessary for this tutorial. Basically, you need to create the above table (Table 1). You do that using the CREATE TABLE keywords (we will be using uppercase notation to better identify SQL keywords from here on):

CREATE TABLE
CREATE TABLE `Poems ` (

	`ID` BIGINT NOT NULL AUTO_INCREMENT,
	`Poem` LONGTEXT NOT NULL ,
	`Author` MEDIUMTEXT NOT NULL ,
	`Name` MEDIUMTEXT NOT NULL ,
	`Notes` LONGTEXT NOT NULL ,
	PRIMARY KEY ( `ID` ) ,
	FULLTEXT ( 
		`Poem` ,
		`Author` ,
		`Name` ,
		`Notes` 
	)
);
See the next section for an explanation on how to do this automatically in the MySQL wrapper kit. At this point we will only have a look at the SQL syntax. We’re using the MySQL syntax, so the picture is slightly different from what we looked at above. The name of the table we’re creating is "Poems" and it contains five fields. None of the fields can be left empty by definition (note the NOT NULL part). However, this isn’t the real case here since four of the fields are defined to be FULLTEXT fields and an empty string is a completely valid input: "". We have defined ID to be the primary key and set it to automatically increment every time a record is added. It is very important that we did not mess with this value later on when inserting stuff!

Once we have created the table, it is initially completely empty. Note that in the MySQL kit used in this tutorial, you will also have to explicitly create a database (to do that we simply pass "CREATE DATABASE MyDatabaseName;" to the database engine). We now want to add something to the table we just created. This is done using the INSERT keyword:

INSERT
INSERT INTO `Poems ` ( `ID` , `Poem` , `Author` , `Name` , `Notes` ) 
VALUES ( 
	'', 'Stop all the clocks, cut off the telephone, ...', 'W. H. Auden', 'Funeral Blues', ''
); 
What happens here is that only three of the four values are inserted into the database – the primary key is left up to the database engine to be defined and no Notes are added. Study the syntax – the SQL language has been made as naturally readable as possible so it shouldn’t be too much of a problem.

Now, let’s suppose we have inserted some 10 or 15 records into the table and are now keen on retrieving them. In particular, we want to retrieve the poem Funeral Blues by W. H. Auden. Here’s how we do that:

SELECT

First case: we want to see the entire record:
SELECT * FROM `Poems` 
WHERE `Author` LIKE 'W. H. Auden' AND `Name` LIKE ‘Funeral Blues’;
Simple isn’t it? The asterisk denotes that all of the data in the specified record will be returned. If we want to retrieve all of the data in the table, we could just omit the WHERE clause.

Second case: we only want to see the poem itself, no author and other info:
SELECT `Poem` FROM `Poems` 
WHERE `Author` LIKE 'W. H. Auden' AND `Name` LIKE ‘Funeral Blues’;
Select may be simple, but it is very powerful. In conjunction with INNER JOIN, UNION and other keywords, one can create mind boggling queries. However, this is well beyond the scope of this tutorial.

Now, let’s suppose we don’t want any of W. H. Auden’s poems to exist in your database because we find them to be too morose. This is where we use the DELETE keyword:

DELETE
DELETE FROM `Poems` 
WHERE `Author` 
LIKE 'W. H. Auden';
Et voila! We have created our own database, added a table to it, added a record to that table and deleted it. We could now use DROP `Poems`; to get rid of the table as well. Now let’s see how to do all this semi-automatically in a MySQL database.

If the above syntax looks intimidating, don’t fret – in the next section we will learn how to automate this process!

The MySQL database kit

Retrieve the kit at [1] under the downloads section. We’ll leave setting it up to you – there is a very comprehensive online manual for the product. MySQL is of course under GPL, meaning it is freely available to use for everything. It can be used for both non-commercial and commercial purposes. From my personal experience, it doesn’t take a rocket scientist to get it working – in fact it’s rather straightforward. Nevertheless, let’s go over the procedures described in the previous section.

Once having set up the engine and a new database for yourself, click the Structure tab and from there enter the name for the new table and the number of fields you want to appear in it (follow the instructions on the page). Once you click Go you will be prompted to enter the fields’ descriptions. This is where you will meet the BIGINT and other MySQL specific data types. Refer to [1] for a more comprehensive explanation on what each of them means. For the time being, however, simply make the fields as described in Table 1. Create all of the fields except for the primary key (set the appropriate radio button) as FULLTEXT. Once you’ve done that, you can click ‘Insert’ in the ‘Actions’ section next to the table name under the Structure tab. You can run select queries by entering a more comprehensive menu by clicking ‘Settings’ just next the ‘Add’ button, and from there choosing the ‘Browse’ tab. The rest I will leave up to you to figure out – it’s time to move on to the real topic of this tutorial. VERY IMPORTANT: please do NOT e-mail me questions about setting up the MySQL database engine – this has got nothing to do with the tutorial – it is simply an unfortunate necessity that needs to be tackled. The MySQL homepage will provide you with all the information in the world!

Getting to the topic: untangling the myth

The myth is about screensavers. Rumor has it a screensavers are something completely different from normal executables. And in a way they are. Then again, they bear no difference at all. In the following section we will try and create a brief overview of what we need to know about a screensaver.

First off, let’s imagine how a normal Win32 application functions: we have WinMain() which is used as the entry point of the program and is responsible for the initialization and normal termination of the application. It’s the same for a screensaver. In a video-oriented application, such as a game, a window is then created and a loop run that keeps calling some draw function. It’s the same for a screensaver! When creating a project in the IDE, we specify a Win32 application as the target for a normal program. We do the same for a screensaver, the only difference being the fact that we actually specify the native screensaver extension .scr as the wildcard instead of the traditional .exe. So far not too many differences are there?

Here’s the catch, though: a normal application is run only whenever the user explicitly runs the .exe or it is run by some scheduler or some batch file. While these conditions also hold true for a screensaver, these are not the normal means of execution for our case. There are four distinct modes a screensaver can be run in. These are: normal, preview, configuration and password modes. Next comes a little explanation on each of them, how and when they’re executed:
  1. Normal mode: this is the mode we generally see a screensaver running in – such as the time saver on your favorite Windows platform with the current time flying around on the screen. This mode is entered by simply executing the .scr file.
  2. Preview mode: this mode is run by Windows itself when we open the Display Properties dialog->Screensaver tab. The preview is run inside the small window in the middle of the dialog if the screensaver is selected in the list on the left. Here’s the rub, however: how can we tell what the handle of the little window is in order to write your preview code? The answer is something, I bet, you wouldn’t have expected – namely the handle is passed to the screensaver via the command line. As we all know, a window handle by the type HWND is essentially a simple pointer that is well known to be 32 bits in size. This means that Windows passes our screensaver a 4-byte numeric value (an address) that we can use as the preview window handle.
  3. Password mode: generally a screensaver runs on a computer when its user has been away for a while. This delay can be set from the Display Properties dialog. Now, when the user leaves the computer unattended for a long time, he may unknowingly make it easy for some random guy to tamper with his/her work/things. The password mode is used to prevent this from happening. Oddly enough, the screensaver is run again by Windows when it senses that a password needs to be asked. Note that we do not need to run the graphics interface when in the password mode. Furthermore, the above only holds true for non-NT Windows’ (Win95,Win98 and WinME). NT handles password authentication automatically and there is no actual need for the screensaver to worry about it.
  4. Configuration mode: a screensaver can be tweaked just like a WinAmp plug-in to do different stuff at different times it is run, or to specify some variables used by the saver. This is done via the ‘Settings...’ button on the Display Settings dialog. As in the case of the password mode, we do not need to run the graphics interface, but rather display a configuration screen and exit when it is closed.
Now remains the question as to where to keep the screensaver and what other files it must have in order to function properly. The default folder for screensavers in Windows 2000 is: %systemroot%/system32. This means that the .scr file must reside in the system32 folder in the Windows’ root directory. In this instance, there are three other files that need to reside in the same directory (no other way around it). These are: opengl.dll, glu.dll and libmySQL.dll. All of these can freely be used for non-commercial and commercial purposes and all of them are provided with the tutorial code. Now back to the topic... again :)

The command line

"But how do I know in which mode to run when the saver is executed?", you might ask. The answer is simple – again, based on command line flags. We will now start going through some of the essential code from the top. The first and most important part is the command line parser. It can look a little messy, but let’s talk our way through it. So, the first thing we do in WinMain(), is parse the command line to learn what we must do next:
	// skip over the path
	char* p = strtok(GetCommandLine(), " ");

	p = strtok(NULL, " ");
I decided to refrain from the traditional line-by-line commenting since there’s a lot of code to cover and things might become fuzzy. Instead we will explain sections such as this by dissecting them separately.

First off, we’re using a very useful standard library function called strtok() here. The name itself stands for "string tokenizer" and it allows us to split a string into smaller sections while changing the delimiter fter each split. We’re not using this feature here since our delimiter is a whitespace in all cases. Here’s how strtok() operates: the first time we call it we pass it the string that we’re about to tokenize. We also pass it the first delimiter at which we want the function to give us our first portion of the string. The next time we call strtok() and we want to continue tokenizing the same string, we are passing it NULL as the first argument. Since strtok() effectively ruins the pointer we give to it there is no way we could call GetCommandLine() again after this piece of code and get the correct result!! Then again, we won’t need to call it again, so we can do whatever we want with it. If you don’t understand why the string is ruined, start approaching the problem from the point of view of memory addresses (pointers) instead of text strings.
	if(p != NULL)
		{
		// there's a backslash or a dash here - skip over it (and any other such stuff)
		while(!isalpha(*p))
   			p++;
This does nothing more than skips over all non-alphabetic characters in the last token returned by strtok(). If you’re not familiar with pointer arithmetic then this can seem slightly awkward – actually it’s very useful: we keep incrementing the pointer by blocks of size byte and after each increment check if the current byte is alphabetical or not.
	//check for the argument flag
	switch(*p)
		{
		case 's': case 'S':
        		ScrMode =ModeNormal;
                	goto End;
		case 'p': case 'P':
		case 'l': case 'L':
			ScrMode = ModePreview;
			goto GetHWND;
		case 'c': case 'C':
			ScrMode =ModeCfg;
	         	goto GetHWND;
		case 'a': case 'A':
			ScrMode =ModePwd;
	         	goto GetHWND;
	      	}
This is the core of the whole parser – here we decide which mode we have to run our saver in. These flags are predefined and passed to the screensaver by Windows. ModeNormal means that the screensaver should switch to fullscreen and conduct its honorary business of cheering us up. The rest should be clear by now. For three of those modes we potentially have to acquire some kind of a parent window to build on. To cut down code size, we use the goto statement – quite useful in this case. Here’s a listing of the command line flags as given at [3]:
  • /c, /c ####, or no arguments at all - in response to any of these the saver should pop up its configuration dialog. If there are no arguments then NULL should be used as the parent: this will end up happening if the user clicks on the saver in the Explorer. With /c as an argument, the dialog should use GetForegroundWindow() as its parent. With /c #### the saver should treat #### as the decimal representation of an HWND, and use this as its parent.
  • /s - this indicates that the saver should run itself as a full-screen saver.
  • /p ####, or /l #### - here the saver should treat the #### as the decimal representation of an HWND, should pop up a child window of this HWND, and should run in preview mode inside that window.
  • /a #### - this command-line argument is only ever used in '95 and Plus! The saver should pop up a password-configuration dialog as a child of ####.
    	  	GetHWND:
   			// skip over any non-digits
			while(!isdigit(*p))
      	 			p++;

       	  		//now acquire the window into which we're going to be drawing later on
			Handle = (p == NULL) ? GetForegroundWindow() : (HWND)atol(p);	// <--
      		End:
We jump to the label GetHWND if we’re expecting a window handle and to End, if not. So, what does the arrowed line do? If we’re given a valid window handle, we convert it to a 32-bit value and use it, otherwise we acquire the foremost window via GetForegroundWindow(). For the sake of the size of this tutorial, we will not be implementing the "scrprev" preview functionality. "scrprev" allows the screensaver to automatically acquire a Preview Window. This is a built-in functionality of Windows to aid us in the debugging process. All of this can be done by running the screensaver in the designated preview window on the Display Options dialog. If you feel like taking a stab at it yourself, refer to [2] – it’s not that difficult, but it hardly provides any additional functionality and most distributable screensavers have it either removed or disabled.
		}
	else
		ScrMode = smConfig;
If the screensaver was run without arguments, we presume configuration mode.
Initializing in the proper mode

Now, let’s go over some theory again for a change. Namely, the requirements posed on a screensaver are quite a bit different than those that apply to most normal programs. While a screensaver that crashes normally doesn’t do much harm, it can be very annoying since these programs are run automatically by the OS. Therefore it is important to think through as to what the user should be notified of, if at all. A screensaver should, in my opinion at least, never pop up an error message or crash (since this can actually take the entire OS down with it – in theory at least). Since we set our aims on "no pop-ups", we must be able to either handle everything internally or write very clean code. The latter is often not an option, but we’ll try :).

Another factor we have to keep our eye on is the number of files that is necessary to run the screensaver – that’s why we won’t be using any textures and all of the settings will be stored in the system registry rather than a configuration file. This means that we have to store the default settings internally and update the registry when the screensaver is run for the first time.
	ReadRegistry();
We try to read all of the settings from the registry – see ReadRegistry() below for a comprehensive walkthrough.

Next we call a weird function that I believe you might not be familiar with:
	InitCommonControls();
What it does is self-explanatory, but why we call it is not at all that clear. Namely, if we run the screensaver in configuration mode, we can see a slider bar control. This is a common Windows control, but isn’t readily available for an application to use before explicitly evoked. That’s what InitCommonControls() does. It actually enables us to use a lot of other neat controls as well, such as tab controls and treeviews, etc, but we won’t be dealing with these.

Next comes the initialization of the screensaver itself. Based on the choices made earlier, we now run the appropriate portion of the program. Note that for a lengthier explanation on the TGLWindow and other classes see the end of this tutorial.
switch(Mode)
	{
	// if we're in preview mode, we want to create the saver in a
	// smaller window (for which we have stored the handle in Handle).
	case ModePreview:
		RECT rc;
		// Since we don't know the precise size of the preview window,
        	// we must retrieve it
   		GetWindowRect(Handle, &rc);
        	// see the TGLWindow class for specs on this function call
		Window = new TGLWindow(Handle, NULL, ScreenSaverWindowProc,
   			"ScreenSaverPreview","SaverPreview", 0, 0, rc.right - rc.left,
                	rc.bottom - rc.top, 32, false);
		break;
	// in normal mode we do the same, but also switch to fullscreen.
	// GetSystemMetrics() with the appropriate arguments retrieves
	// the current desktop resolution
	case ModeNormal:
		Window = new TGLWindow(Handle, NULL, ScreenSaverWindowProc,
   			"Poetic Saver", "SaverMain", 0, 0, GetSystemMetrics(SM_CXSCREEN),
                	GetSystemMetrics(SM_CYSCREEN), 32, true);
		break;
	// if we must initialize in the Configuration mode, we simply
	// create the dialog box for which we have given the ID
	// DLG_SCRNSAVECONFIGURE, stored in the resource file. DialogBox()
	// is a standar WinAPI call - check it out on MSDN
	case ModeCfg:
		ConfigDlg = DialogBox(hInstance, MAKEINTRESOURCE(DLG_SCRNSAVECOFIGURE), Handle, ScreenSaverConfigureDialog);	// <-   
   	   	break;
	// we don't handle password stuff! For NT Windowses, the OS does
	// it for us. For non-NT OS'es, the password must be disabled. 
	case ModePwd:
		return 0; 
	break;
	}
Pretty much everything is explained by the comments, except for one thing – the line denoted by the arrow comment. So what’s up with that? What is the last argument we’re passing to DialogBox()? If you’re not familiar with system callback functions, here’s the place to get you up to speed. So, what are these callback functions? These are specific function prototypes provided by the OS so that it can keep track of stuff. Ideally, every window has a callback function – most of the time they’re hidden from us, though (such as in the case of a button or an edit box). However, for each custom window, we must handle it. You’ve actually done that repeatedly when creating the main window for your favorite application (not necessarily OpenGL). In fact, we have another one in this very tutorial! Can’t see it, can you? That’s because we’ve hidden it in the TGLWindow class as a static member function. "But if it is hidden in the window class, then how can I access it for each individual window", you might ask. In this tutorial you can’t! You’d need to pass the window class a pointer to a public function that is then called by the window class’ callback function. Confusing, isn’t it. Relax – you don’t have to get this for the sake of this tutorial :). This is actually what subclassing is all about (we will look at it briefly near the end) – things are actually slightly more complicated than they seem at the first glance. And, surprise-surprise! There’s yet another system callback function here! It’s called ScreenSaverWindowProc() and will also be explained later on.

So with all of that aside, we can think of the ScreenSaverConfigureDialog callback as a function that is called by Windows every time our configuration dialog needs to be sent a message. We will look at it more closely later on. For now, let’s move on with WinMain().
// if we're not in configuration or password mode
if(Mode != ModeCfg && Mode != ModePwd)
	{
	// tell our main window that we're doing our drawing stuff in the
	// DrawGLScene function
	Window->BindDrawFunc(DrawGLScene);
	// capture the rendering context
	Window->Capture();
This place should be the first one to actually need explaining. The above two function calls can be very easily understood if you think of two completely different things: how GLUT works and multi-window support. Let’s elaborate: if you haven’t used GLUT then this might be new to you, otherwise you should be familiar with the concept of passing pointers to functions to some class/API. Namely, drawing a frame in a window requires three steps: we must make the rendering context bound to the window we currently want to draw in (wglMakeCurrent()), we can then draw into that rendering context, and finally we need to call SwapBuffers() to paste the image onto the screen. Now, imagine having a big window with a lot of smaller child windows that all use OpenGL to draw their contents and we want to loop through them, drawing them one by one. Using a class-based approach, in this case, is godly and lifesaving. Based on the above, we need to call wglMakeCurrent() before drawing into each of these windows, then call some draw function and then SwapBuffers(). Calling these functions requires a small list of parameters, though – parameters that are safely stored within the TGLWindow class. So, what we’ve done here is that we’ve made wglMakeCurrent() a parameterless member function of the window class called Capture() and we’re letting TGLWindow also manage the SwapBuffers() call. Now, by calling Window->Draw(), we’re internally calling the function we passed a pointer for to the TGLWindow class. After we return from DrawGLScene(), TGLWindow also calls SwapBuffers(). Still confused? Be not alarmed, function pointers require a little bit of getting used to at first. There may be one thing that’s bothering you at this point, though. Why not let TGLWindow also handle the call to wglMakeCurrent() internally? This wouldn’t be smart because we might want to do stuff between drawing and capturing the rendering context. If you feel like calling SwapBuffers() automatically is also a violation of this freedom, remove it from the window class – I like it there, though :).
	// create a timer and reset it
	Timer = new TTimer();
	Timer->Push();
The timer class has a couple of new features that will be used throughout this project and one very important peculiarity. The features are Push() and Pop() member functions. Push() sets the instance it is called as the zero offset for the counter and Pop() returns the offset since the last call to Push(). All numbers are in milliseconds. The peculiarity of the timer class is scope. Namely, the timer only returns a viable figure when it is called within the same module (file) as it is created in. This means that initializing a TTimer class in main.cpp and calling Timer->Pop() in another.cpp will yield wrong results. Why? That’s because the entire timer class is defined in a header. This saves us from including another .cpp file, but forces us to take imaginative action to synchronize time between all of the modules. This "imaginative action" manifests itself in the form of the function GetAppTime().
	// we're using a custom font
	BuildFont();
If you need to refresh yourself on font creation, check out NeHe’s tutorial 13.
	// try to retrieve a poem from the online database
	char* Text = RetrieveText();

	// if something went wrong and we don't have a poem, use the built-in one
	if(Text == NULL)
		Text = Poem;

	// set it read to to be drawn - build a TSCText class to hold the entire
	// poem text
	Txt = new TSCText(Window, Font, Timer);
	// the poem text is formatted unsuitably for direct displaying, so parse it
	Txt->Parse(Text);

	// we want to use two different effects, so register them
	Txt->AddKeyframe(SwoopIn);
	Txt->AddKeyframe(SwoopOut);
	// run the text!
	Txt->Start();
	}
Everything related to the TSCText and the underlying TSCString and TSCCharacter classes is not a part of this tutorial and will be left for you to explore on your own. They’re all commented and if you need further explanation or have some suggestions, e-mail me. VERY IMPORTANT: if you want to exclude all of the overhead these classes bring to the saver code, just remove all reference to them and edit DrawGLScene() to draw your favorite spinning cube! It’s that simple.

The main loop

Now for the main program loop:
// you should be familiar with first part of this loop
bool done = false;
while(!done)
	{
	if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
		if(msg.message == WM_QUIT)
			done = TRUE;
		else
			{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			}
		}
	else
		{
		// this is new. this means that, if we're not in config or password mode,
		// we should draw in the main window
		if(Mode != ModeCfg && Mode != ModePwd)
			Window->Draw();
		// give the cpu some slack
		Sleep(5);
		}
This is where we’re executing all of the might of object-oriented programming. Our call to Window->Draw() unleashes an avalanche of calls tightly packed into just a few lines. Even though you might say everything up to this point can be done procedurally (without OO), object-orientedness still has its clear advantages, one them being encapsulation. Can you see any arrays for anything floating around in main.cpp? I can’t :). That’s because we’ve hidden them all. Decrypting these two calls, Window->Draw() calls DrawGLScene() (can you see how?) and Sleep(5) causes the application to give the CPU five milliseconds of rest after each frame. Now why would we want to do that? A personal example: if I leave my computer unattended and the screensaver kicks in, I want other processes running in the background (such as Prime95, or for some of you, some Cure For Cancer or SETI application) to get more CPU time. The screensaver doesn’t need 99% of CPU usage - at this point there isn’t much wisdom in creating a low priority thread for it either. So, let’s simply skip a couple of milliseconds each frame. Now what are the drawbacks in this case? One very major factor is frame rate: for sleep time = 5 ms, maximum theoretical frame rate cannot be higher than 1000 / 5 = 200 fps. Period. Increase this value further to give up more CPU time.
	}

// we're exiting - let's destroy all the evidence!
if(ConfigDlg)
	EndDialog(Handle, ConfigDlg);
This is standard procedure for dialog boxes – EndDialog() is a native WinAPI call. This also concludes our WinMain() function.

RetrieveText() – connecting to the MySQL database

We will be moving on with some of the functions called from WinMain() that were not given much consideration. First comes RetrieveText() – this will force us to deal with the only practical section of SQL in the entire screensaver! It’s simple, I promise :). The source code may be slightly under commented, so we’ll try to make up for it here.
#include "include/mysql.h"
Let’s be sure to include the appropriate header provided with the MySQL kit described at the beginning of this tutorial. Also keep in mind that we have to include the appropriate library, libmysql.lib!
// RetrieveText() connects to a remote database and tries to retrieve a
// poem text. If it fails NULL is returned.
char* RetrieveText()
	{
	char* Text = NULL;

	// create an SQL object
	MYSQL* MySQLObject;
	// create a database record object
	MYSQL_ROW Row;
See [4] for the root directory of the online documentation. The MYSQL object is the fundamental component of the database access. We use MYSQL_ROW to retrieve one record from the query.
	// try to initialize the SQL object
	if(!(MySQLObject = mysql_init((MYSQL*)0)))
		{
	   	WinError("Cannot initialize MySQL client");
	      	return NULL;
		}
This should be pretty much self-explanatory. Just in case: we’re initializing the MySQLObject object (more precisely, the MySQL library does that). WinError() is a macro defined as a shortcut for the common MessageBox() WinAPI function call. See main.h.
	// try to connect to a remote database
	if(!mysql_real_connect(MySQLObject, ServerIP, Username, Password, Databasename, ServerPort, NULL, 0))
		{
		delete MySQLObject;
		WinError("Cannot connect to database");
		return NULL;
		}
This is where connecting to the database takes place. The parameters for mysql_real_connect() are taken from the configuration info. Edit them in the configuration mode and allow the saver to store the parameters in Windows Registry if you want to connect to your own database.
	int Length;

	// submit the query
	if(!mysql_query(MySQLObject, QueryText))
		{
This executes the query. If it returns NULL, we’re in the clear and can start going through the returned results.
		// acquire the record from the returned query results
		Row = mysql_fetch_row(mysql_store_result(MySQLObject));
We only need one record from MySQLObject (even if it contains more), so we only fetch one. The result is stored in Row.
		// get the length of the record
		Length = strlen(*Row);
MySQL stores the returned results in string format. A remark is in order here: the MYSQL_ROW variable type is essentially a char**, or a pointer to pointer to char which means that it holds a list of strings in it. We have to keep this in mind when creating the query. For instance, if we were to retrieve more than one field from the database and we wanted to use all of them, we could only access the first field as *Row (a dereferenced double pointer becomes a simple pointer to a char, eg a string). If we need to access any succeeding string in the string list, we could just point to it like this: *(Row + 1) or do it the traditional way: Row[n], where n is the number of the string we wish to access.
		// get the length and allocate memory for the text
		if(Length > 0)
			Text = new char[Length];

		// copy the entire record into Text
		for(unsigned i = 0, j = 0; i < Length; i++)
			{
			// if there's a field change in the record, skip the newline
			if((unsigned char)*(*Row + i + 1) == '\n')
				continue;
			else
				// otherwise just copy the character
				Text[j++] = (char)*(*Row +  i);
			}
Now, why do we need this loop? In fact, we don’t if we only want to make the program work. We added it to the saver code to add one very important thing to the final product: flexibility. Namely, when displaying emotional text on the screen, such as poems can often be, one must take into account certain things that human readers often do naturally. For all creative arts, pauses are one very important factor – imagine a really beautiful poem scrolling on the screen without any dynamic variance whatsoever – not much fun to read, is it? For the format used in this tutorial program, we have added the possibility of adding fixed-length pauses by inserting the ‘^’ character into the poem text. The reason is simple: we need a newline at the end of each line, but we also need a longer pause after each verse (that’s where you’d normally insert an extra newline). This is what TSCText->Parse() takes care of. If you can’t understand what (char)*(*Row + i), does then here’s a little explanation: Row is dereferenced, making it a standard string. Then, an offset within the string is specified by adding i to it. Finally, the offset at which we arrived by adding i to the initial address of the string, is dereferenced again, making it a simple character, which is then assigned to the next character in the array we called Text. If you don’t understand how this works, try reading a couple of tutorials on pointers and pointer arithmetic.
		}
	else
		WinError("  Cannot execute the specified query. Please check the syntax and semantics.\n"
			 " Possible causes: the query is invalid, the database and/or table does not exis\n"
			 "                  exist at the specified IP address/port.                       ");
}
The else block simply notifies the user if the query cannot be completed successfully. This brings us to a very important thing that should be kept in mind when creating a screensaver. Remember, we earlier made note of the fact that in the user should never be prompted for any kind of input by the screensaver. Well, this is one place where we have ignored this rule: we’re calling WinError() on three distinct occasions in this function which means that we make absolutely sure that the user is notified if anything goes wrong. This was added for debugging reasons – when writing an end product, you should handle these cases smartly, but not bother the user!

This concludes the SQL-related stuff needed for this tutorial! The above code is very simple – it is simply something to get you started. If you need to use advanced stuff then MySQL online help is the way to go.

System callbacks and subclassing

We will now be moving on to the callback function for Configuration dialog, described earlier.
// this is the system callback (check out the return type - it has WINAPI in it!)
// for the screensaver. This callback is subclassed by the main window (TGLWindow).
// Either of these instances handle different system messages - see the TGLWindow
// class for a more comprehensive explanation.
LONG WINAPI ScreenSaverWindowProc(HWND	hWnd, UINT uMsg, WPARAM wParam, LPARAM	lParam)
{
	// exit if the user presses a key or Alt + F4 (or there's some other indication
	// that the program should be terminated)
	switch(uMsg)
		{
		case WM_CLOSE:
		case WM_KEYDOWN:
			PostQuitMessage(0);
			return 0;
		}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Okay, here’s where the confusion starts with the subclassing and the TGLWindow class. For a good tutorial on subclassing and writing a window wrapper class, check out [5]. To save some valuable time, let’s go over this briefly here: as mentioned, each window has a callback function that is maintained by Windows – a place that is both accessible to the programmer and to the internal workings of the operating system. That’s the place that is used to notify the programmer of changes that take place in or apply to a window. In some instances, you might want to have multiple instances of the same function, such as in this case. This means that you need to subclass the window’s callback function. This is done using the functions SetWindowLong() and GetWindowLong() – check out the source code, more specifically the TGLWindow::Create() function in TGLWindow.cpp for further explanation. This means that we pass a new memory address to Windows where we expect to catch the very same messages that are normally sent to the primary window callback. In this code example we have a very clear example of this: we handle all mouse input in TGLWindow:: GLWindowProc() (we really do handle them since we return 0. If we were to return CallWindowProc() as is done for all other messages that are sent to the user, these messages wouldn’t be handled) and only handle the termination conditions in ScreenSaverWindowProc(), even though these two are essentially the same. To get a feel of this, you could do the following experiment: add a MessageBox() call to one of the WM_MOUSExxx messages in TGLWindow:: GLWindowProc(), run the saver and press the mouse button you chose to handle. Then move the MessageBox() and the WM_MOUSExxx message handler to ScreenSaverWindowProc(), and run the saver again. Now click the mouse button and you’ll see! Now try returning 0 in one of these functions for that specific mouse message handler and see which of those functions is called first by Windows. It is important to note that handling all mouse input inside the TGLWindow class is absurd – it is there simply to show you how the concept of subclassing works.

The Configuration dialog

So, what next? The contents of configure.cpp – namely the configuration dialog. This will lead to (arguably) the messiest part of this tutorial – the Windows registry. So, without any further ado, let’s get a’crackin’! We will go over the entire contents of configure.cpp:
#include "main.h"
#include "def.rcd"
#include <commctrl> // we need commctrl.h for managing TBM_xxx messages
#include <windowsx> // we need windowsx.h for the GET_WM_COMMAND_ID() macro
commctrl.h will allow us to use tracker bar messages (synonymous to the slider bar in this context) that start with the TBM_ prefix. We have two of those controls on the configuration dialog.
// declare the handles for all of the controls in the dialog box
HWND SpeedHandle = NULL;		// tracker bar
HWND ScatterHandle = NULL;		// tracker bar
HWND ScatterLEDHandle = NULL;		// static text
HWND SpeedLEDHandle = NULL;		// static text
HWND ServerIPHandle = NULL;		// edit box
HWND ServerPortHandle = NULL;		// edit box
HWND DatabaseNameHandle = NULL;		// edit box
HWND UsernameHandle = NULL;		// edit box
HWND PasswordHandle = NULL;		// edit box
HWND SQLStatementHandle = NULL;		// edit box
Here are handles to all of the interactive controls on the Configuration dialog (there are four more static texts and two group boxes and three buttons, but we’re not using them interactively with the exception of the buttons – we’ll go over these below).
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
	// some needy variables
	int Value;
	char str[12];

	// handle the messages
	switch(message)
		{
	      	// upon creation of the dialog
      		case WM_INITDIALOG:
			// acquire the correct handles from the resource (see Screensaver.rc)
			SpeedHandle = GetDlgItem(hDlg, IDC_TEXT_SPEED);
			SpeedLEDHandle = GetDlgItem(hDlg, IDC_TEXT_SPEED_LED);
			ScatterHandle = GetDlgItem(hDlg, IDC_SCATTER);
			ScatterLEDHandle = GetDlgItem(hDlg, IDC_SCATTER_LED);
			ServerIPHandle = GetDlgItem(hDlg, IDC_SERVERIP);
			ServerPortHandle = GetDlgItem(hDlg, IDC_SERVERPORT);
			DatabaseNameHandle = GetDlgItem(hDlg, IDC_DATABASENAME);
			UsernameHandle = GetDlgItem(hDlg, IDC_USERNAME);
			PasswordHandle = GetDlgItem(hDlg, IDC_PASSWORD);
			SQLStatementHandle = GetDlgItem(hDlg, IDC_SQLSTATEMENT);
The above block does one very simple thing – it simply retrieves the proper handles for the controls on the Configuration dialog upon the initialization of the Configuration dialog.
    			// set the range of the text speed to [25, 250]
      			SendMessage(SpeedHandle, TBM_SETRANGE, true, MAKELONG(25, 250));
         		// set the step size when moving the slider on the tracker bar
         		// (it now increments by steps of 25)
      			SendMessage(SpeedHandle, TBM_SETPAGESIZE, 0, 25L);
         		// set the position of the slider to a predefined (preloaded)
         		// value (stored in T)
	      		SendMessage(SpeedHandle, TBM_SETPOS, true, T);
			// set the led to reflect the value
         		sprintf(str, "%i", T);
      			SendMessage(SpeedLEDHandle, WM_SETTEXT, 0, (LPARAM)str);
This and the following group of function calls are very similar and will be explained only once. First of all, we know that everything in Windows is done by sending/handling messages and notifications. To send a message to a control, WinAPI provides us with the function SendMessage(), that takes four parameters: the handle of the receiving control, the message itself and two additional parameters that can be used to specify or receive additional values. SendMessage() also returns LRESULT (a 32-bit value). By sending specific messages to a control, we can define its behavior rather precisely. In this case we want to make our slider bars obey certain limitations: we want to specify the range (25 – 250 for the text speed and 0 – 250 for the scatter value; note that we’re using a multiply of 10 of the used scatter value on the tracker bar; we do this by sending the TBM_SETRANGE message to the appropriate controls), the page or step size (25 for the text speed and 5 (or .5f in the actual used value) for scatter; this is done by sending the TBM_SETPAGESIZE message) and the current position of the slider on the tracker bar (note that these values are specified in ReadRegistry() in registry.cpp that is called from WinMain(); we do this by sending the TBM_SETPOS message – for the scatter value this means multiplying the actual value by 10!). Last, but not least we update the value indicators (controls that contain the word "LED" in their handle name) to reflect the current value in the static text boxes underneath the sliders. We do this by sending them the text using the WM_SETTEXT message (notice the WM_ prefix which makes this a generic window message – e g can be sent to most controls).
			// do the same for the scatter value, this time with a small exception
			// in mind: we will later on be using values ten times smaller than
			// those indicated by the slider bar. That is, if the slider is moved
			// all the way to the end (eg 250), we will be using the value 250 / 10
			// or 25.0 internally instead. Can you figure out why? Hint: see help
			// on the TBM_SETRANGE message
			SendMessage(ScatterHandle, TBM_SETRANGE, true, MAKELONG(0, 250));
			SendMessage(ScatterHandle, TBM_SETPAGESIZE, 0, 5L);
			SendMessage(ScatterHandle, TBM_SETPOS, true, Scatter * 10);
			sprintf(str, "%.2f", Scatter);
			SendMessage(ScatterLEDHandle, WM_SETTEXT, 0, (LPARAM)str);

			sprintf(str, "%i", ServerPort);
			SendMessage(ServerPortHandle, WM_SETTEXT, 0, (LPARAM)str);
			SendMessage(ServerIPHandle, WM_SETTEXT, 0, (LPARAM)ServerIP);
			SendMessage(DatabaseNameHandle, WM_SETTEXT, 0, (LPARAM)Databasename);
			SendMessage(SQLStatementHandle, WM_SETTEXT, 0, (LPARAM)QueryText);
			SendMessage(UsernameHandle, WM_SETTEXT, 0, (LPARAM)Username);
			SendMessage(PasswordHandle, WM_SETTEXT, 0, (LPARAM)Password);
This last section of WM_INITDIALOG sets the text values for all of the other controls on the dialog.
			return true;
		case WM_CLOSE:
			PostQuitMessage(0);
			break;
		case WM_COMMAND:
			switch(GET_WM_COMMAND_ID(wParam, lParam))
				{
				case IDCANCEL:
   		      			PostQuitMessage(0);
               				break;
         			case IDUPDATE:
            				UpdateRegistry();
   		      			PostQuitMessage(0);
               				break;
         			case IDREMOVEFROMREGISTRY:
            				RemoveRegistry();
   		      			PostQuitMessage(0);
               				break;
            			}
If we’re sent a WM_COMMAND message, we can expect input from any of the button controls that are children of the Configuration dialog. There are three of those: "Exit", "Update and exit" and "Remove all registry entries". The "update" in "Update and exit" insinuates that something will be updated – this something is the Windows registry. See UpdateRegistry() below. Upon pressing "Remove from registry", the one single key created by the saver is deleted from the registry – see RemoveRegistry() below. The GET_WM_COMMAND() macro conveniently combines the lParam and wParam and gives us the handle of the control which the message is coming from.
			return false;
		// if the user scrolled something (anything) on the dialog box, we're
		// notified through the WM_HSCROLL and WM_VSCROLL messages. We only
		// have horizontal scrollbars
		case WM_HSCROLL:
			// the handle to the tracker bar is passed to use as lParam, so all
			// we need to do, is match it
			if((HWND)lParam == SpeedHandle)
				{
				// get the position of the tracker bar
				Value = SendMessage(SpeedHandle, TBM_GETPOS, 0, 0);
				// make it a string
				sprintf(str, "%i", Value);
				// store the speed value in T (global variable)
				T = Value;
				// set the LED (the number underneath the scrollbar) to reflect
				// the current value
				SendMessage(SpeedLEDHandle, WM_SETTEXT, 0, (LPARAM)str);
				return false;
				}
			// do the same for the scatter parameter
			else if((HWND)lParam == ScatterHandle)
				{
				Value = SendMessage(ScatterHandle, TBM_GETPOS, 0, 0);
				// normalize the number by dividing it by 10 - makes the number
				// seem a little more user friendly
				sprintf(str, "%.2f", Value / 10.f);
				Scatter = Value / 10.f;
				SendMessage(ScatterLEDHandle, WM_SETTEXT, 0, (LPARAM)str);
				return false;
				}
The last message we handle is the WM_HSCROLL message sent when "something horizontal" takes place on the parent window. This can be when the user scrolls the window horizontally using the scrollbar or, as in this case, when the user moves the indicator on either of the tracker bars. Looking up the WM_HSCROLL message (on MSDN, for instance), will yield that the handle of the control is sent to us as lParam. Now, all we have to do is match the handle with the correct control handle that we already know and we also know what to do next. For either of the slider bars, we first retrieve the position of the slider by sending the control the TBM_GETPOS message. We then update the LED below the slider bar and store the value. Simple, isn’t it!
		}
	return false;
}
Tampering with the registry

The Windows Registry

Since we’re dealing with a screensaver and we don’t want to keep the settings we use in a config or ini file, we store them in the safest place imaginable – the Windows registry. Unfortunately, doing so requires some understanding of how things work.

You can open the registry using the Run... command in the Start menu. Just type "regedit" in the box and press OK. The registry is displayed. A word of caution: this is serious stuff and tampering with wrong variables can render your operating system crippled or unusable! We will only be dealing with one item in this tutorial.

When you first open the registry, you will notice that what we’re dealing with is essentially a simple tree. This is actually the best way to think of it – everything in it obeys to hierarchy. As the first thing, collapse all open nodes in the tree so that you’re left with only five visible braches. One of these branches is called HKEY_CURRENT_USER – this is the one we will be dealing with. As the name suggests, this branch is called a key – in fact all yellow folders in the tree are called keys. Each key can have several values attached to it. We will get to those later.

Now, expand HKEY_CURRENT_USER. A bunch of keys pop into the view. Seek out the one with the name "Control Panel" and expand it. Looking through the list that appeared, you should be able to find several keys with the words "Screen Saver." in them – this is where Windows screensavers keep their settings, so we will store our settings here, too (under the name "Screen Saver.OpenGL Poem"). If you expand any of these keys, you will be able to see several values on the right side. There are three columns: Name, Type and Data. The Name and Data fields are self-explanatory, but the Type field can be slightly confusing. More on this later.

Knowing this, we will go through registry.cpp top-down.
#include "main.h"

// takes a 4-byte string and converts it into a 32-bit decimal number using
// WinAPI macros
#define MAKEDEC(a) MAKELONG(MAKEWORD(a[0], a[1]), MAKEWORD(a[2], a[3]));
We will be using this macro later on – it utilizes standard WinAPI macros to create a 32-bit variable out of four 8-bit ones (a 4-byte string).

We will now look at how to write into the registry.

Creating a new key and adding values to it
void UpdateRegistry()
	{
	HKEY RootKey = NULL;
	HKEY ScreensaverKey = NULL;
In order to write into a key or create one, the parent of that key/value must exist and be opened with appropriate rights. Therefore, in order to create our screensaver’s key as the subkey of "Control Panel", the latter must be open. When opening a key successfully, we acquire and handle to it that is of type HKEY. We will be storing the handle to the parent ("Control Panel") in RootKey.
	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel", 0, KEY_ALL_ACCESS, &RootKey) != ERROR_SUCCESS)
		ShowError("UpdateRegistry() -> RegOpenKeyEx(RootKey)");
We test if we can open the key using RegOpenKeyEx(), the extended 32-bit version of RegOpenKey(). The first argument is a previously opened key handle or one of the five classes you can see in the Registry editor. The depth of the tree we’re opening here is only one level deep; therefore the second argument only has one level in it. For instance, we could also open three levels at a time like this: "Control Panel//Desktop//WindowMetrics". The third parameter is reserved and must be zero; the fourth specifies the access rights we want to have. You can read up on these on your own – we’re simply granting ourselves all possible access. The final parameter receives the handle for the opened key. We will be using this in the following calls.
	RegCreateKey(RootKey, "Screen Saver.OpenGL Poem", &ScreensaverKey);
We create a new key called "Screen Saver.OpenGL Poem" under RootKey and receive a handle for it in ScreensaverKey. If the key already exists, RegCreateKey() opens it instead.

Next we create a bunch of values under ScreensaverKey using RegSetValueEx(). Some of the code has been removed (look at the source code for the full listing) and replaced with three dots. This is done to avoid excessive repetition. Some notes on the following function calls, more precisely the fourth parameter: in this tutorial we’re only using two different data types (there are many more) for our variables in the registry – the string (REG_SZ) and the long (REG_DWORD) data types. This is the main reason why the scatter value is multiplied by 10 – to make it a valid DWORD that can be read directly from the registry. The first argument of RegSetValueEx() is the handle to the key whose values we want to modify; the second argument is name of the data field; the third argument is reserved and must be 0; the fourth one specifies the data type of the value and the fifth one specifies the value we want to set the value field to. The last parameter specifies the size of data in bytes that we’re storing in the registry.
	if(RegSetValueEx(ScreensaverKey, "Text speed", NULL, REG_DWORD, (const BYTE*)&T, sizeof(DWORD)) != ERROR_SUCCESS)
   		ShowError("UpdateRegistry() -> RegSetValueEx(Text speed)");

	DWORD Scat = Scatter * 10;

	if(RegSetValueEx(ScreensaverKey, "Scatter speed", NULL, REG_DWORD, (const BYTE*)&Scat, sizeof(DWORD)) != ERROR_SUCCESS)
		ShowError("UpdateRegistry() -> RegSetValueEx(Text speed)");

	char StringValue[256];

	ZeroMemory(StringValue, 256);
	SendMessage(ServerIPHandle, WM_GETTEXT, SendMessage(ServerIPHandle, WM_GETTEXTLENGTH, 0, 0) + 1, (DWORD)StringValue);
	if(RegSetValueEx(ScreensaverKey, "Server IP", NULL, REG_SZ, (const BYTE*)StringValue, strlen(StringValue)) != ERROR_SUCCESS)
		ShowError("UpdateRegistry() -> RegSetValueEx(Server IP)");
In order to get the proper string to store in the registry, we must retrieve it from the correct control (edit box) first. We’re using a neat convention here – calling embedded SendMessage() commands: the outer one to retrieve the text and the inner one to specify the outer one’s length argument. Note that we’re adding a 1 to make up for the trailing null character.
	… 

	RegCloseKey(RootKey);
When we’re done, it’s always nice to close the door.
	RegFlushKey(ScreensaverKey);
By calling RegFlushKey() we force the Windows registry to update. This is not something that should be done without thinking through the nature of the program first since it is a procedure that occupies a great deal of system resources. We don’t have any limitations on time here, but for larger registry modifications this could take up several seconds!
}
Reading from the registry

In this section we won’t be delving into the code that much since it only differs from the code described under the previous section by a marginal amount. Instead we will go over the key bits of the code (no pun intended) in ReadRegistry() in registry.cpp.

First of all, as in the case of writing, we need to open the key we want to read from using RegOpenKeyEx(). This will give us a handle to the registry key we’re about to read from. We store this key in RootKey. In the code, if we fail to open the key, we assume that there is no registry entry of the predefined name and we automatically assign some default values to the proper variables. Next comes the trickier part. The code looks like this:
	DataSize = 1024;      
	// read the text speed
	if(RegQueryValueEx(RootKey, "Text speed", NULL, &DataType, DataValue, &DataSize) != ERROR_SUCCESS)
		ShowError("ReadRegistry() -> RegQueryValueEx(Text speed)");
	T = MAKEDEC(DataValue);
We use RegQueryValueEx() to query a value from the key for which we have stored a handle in RootKey – that is the first parameter. The second parameter is the name of the value field and the fourth one must be null. The last three parameters are as follows: a buffer for the type of the value field, a buffer for the actual data and the size of the data returned. The catch here is to specify DataSize beforehand and pass RegQueryValueEx() a large enough value to actually hold the buffer. We’re not doing this in the current tutorial, but it would be a lot more correct to first run the query with DataType and DataValue as NULL and only query the size of the underlying data, and then call RegQueryValueEx() again as shown above. We’re assuming (something that’s definitely not a good thing) that DataSize = 1024 is large enough to hold the returned DataValue. After the function returns, DataSize holds the actual size of DataValue as opposed to the original 1024. Does that make sense?

The last line simply takes a string as its argument and converts the first four characters to a 32-bit number. Note that DataValue can contain any data type when RegQueryValueEx() returns – from an ASCII string to an unsigned long to pure binary data – therefore this argument has to be something very generic, e g a BYTE*.

Deleting a key from the registry

Again, this isn’t much different from writing into the registry – the key we want to delete has to be opened first using RegOpenKeyEx(). Here’s the code:
int RemoveRegistry()
{
	HKEY RootKey = NULL;

	char* SubKeyName = "Control Panel\\Screen Saver.OpenGL Poem";

	if(RegOpenKeyEx(HKEY_CURRENT_USER, SubKeyName, 0, KEY_ALL_ACCESS, &RootKey) == ERROR_SUCCESS)
		if(RegDeleteKey(RootKey, NULL) != ERROR_SUCCESS)
			{
			ShowError("Could not remove the registry entry");
			return -0x1;
			}

	return 0x1;
}
The key is deleted via the RegDeleteKey() function that takes two arguments: the handle to the key and the name of its subkey we’re about to delete. Therefore, we could easily have written it as:
	RegDeleteKey(HKEY_CURRENT_USER, SubKeyName);
It’s as simple as that.

This concludes our detour into the Windows registry – hopefully it has been useful and you’ll be taking advantage of its positive features (such as storing simple, but essential, values). Just don’t crowd the registry with senseless info – such as binary data for images or sounds – this will end up having an impact on the performance of the OS.

As the final part of this tutorial, we will have a look at the TGLFont and TGLWindow classes and how OpenGL fits into all of this.

TGLWindow, TGLFont == OOP, or, is this the way to go?

The name of this section suggests a fierce battle and most likely sends shrills down some of the patriots of procedural programming. This won’t be a section that will try to justify using a fully object-oriented approach in your OpenGL projects, but it will give a short overview of the benefits of this kind of modularization. No source code here, just some thoughts…

If you have been feeding off of simple tutorials that only use minimal code to create a window and draw some rotating quad or something, then this isn’t something you should be too concerned about just now. However, if you have been trying to create a larger project and become totally entangled in the ever-growing amount of source code, OOP is definitely something to consider.

The key word always used when describing the term OOP is encapsulation. The other terms that should cross your mind when somebody mentions OOP are: reusability, inheritance and polymorphism. Consider [6] or [7] (Thinking in C++, 2nd ed.) for reading up on these terms.

In the case of OpenGL, such kind of encapsulation also serves a slightly different purpose, though. Since OpenGL, just like DirectX, is an API that relies on the existence of a target HWND, it can be said that the window is effectively used to channel all audio/visual dataflow. This makes harnessing the HWND object a very powerful tool indeed. One of the great things about the TGLWindow class has already been addressed previously in this tutorial – the capturing of the rendering context and automatic buffer swapping. A far more powerful feature, however, hasn’t been addressed in this tutorial. Actually, this can be split up into two distinct problems, both of which can rather easily be solved with the help of OOP. These are:
  1. Have you ever tried to create a 3D modeling program with four views? How about n views?
  2. Have you ever tried destroying a full-screen window and re-creating it in windowed form without reloading all of the data bound to the rendering context of that window?
The important thing to remember here is that there isn’t one thing that can’t be done procedurally, it’s just the question of how messy the result will be.

Now a few words about the font class: in this case there is no actual benefit in creating the font class since it is not very large. You should, however, keep in mind that none of the classes enclosed with this tutorial are as large as they will ever get – in fact we have minimized their functionalities to leave room for growth and exploration. Some ideas that you might want to consider tackling on your own: what about a menu for the window? What about minimize/maximize/restore? Or automatic resizing of the OpenGL view when the user resizes the window (yes, this is not included!)? Browse through the source code for comments on topics not discussed here – there’s a lot that has been left for you to explore on your own.

The incorporation of OpenGL into all of this might seem vague, but then again, OpenGL is simply a tool – one of the many out there. It isn’t only the visual side and the neat special effects that make good software, but the blending of many features from many different fields. This tutorial tries to demonstrate that. It is only unfortunate that a lot of it is not thoroughly explained – that makes this tutorial an advanced one.

The conclusion

In conclusion, while this tutorial is largely based on NeHe’s previous tutorials, a lot has been changed and some very important things left out. I hope you have enjoyed this lengthy rundown of the essentials and hopefully picked up a few useful tips. If you have any suggestion, complaints or ideas (or you just wish to share your thoughts on the subject), my e-mail is christopheratkinson@hotmail.co.uk.

Resources

1 - http://www.mysql.com
2 - http://www.cs.unibo.it/~ljw1004/download/howtoscr.html#ScrPrevCode
3 - http://www.alexchirokov.narod.ru/HOWTOSCR.HTM
4 - http://www.mysql.com/doc/en
5 - http://www.gamedev.net/reference/articles/article1810.asp
6 - http://www.embedded.com/97/fe29712.htm
7 - http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html

* DOWNLOAD Visual C++ Code For This Lesson.

* DOWNLOAD Code Warrior 5.3 Code For This Lesson. ( Conversion by Scott Lupton )