- Creating an application should be trivial. All I should need to do is ask for a window to be created (with some simple parameters), get some result back that allows me to access it.
- Register a callback to handle some general-case behaviours
- The Window has been resized.
- We want to change fullscreen modes (start up fullscreen, switch to windowed).
- Have an 'update', giving us a delta time between updates.
- Clean up upon closing the window.
So, what do I want this to look like? Pseudo code, to the rescue!
Yes, I know. Greatly oversimplifying the process, I know. But why shouldn't it be that simple? We can add as many handlers as we need to the Window class to get our functionality.
With that in mind, let's implement it!
The very first thing that I want to do is generate that Window class. The problem becomes, how do I end up setting the framework up so that creating an appropriate 'MS Windows' or 'OS X' version is trivial? The answer, surprisingly enough, is to leave it to the linker.
Here's the deal. We're going to define a Window class using a header file. ALL the implementation details will be in a .CPP file. One for Windows, one for OS X, one for what ever other platform we're interested in. We leave the details of which one to use up to the linker. Right now, I'm going to use the development environment to set those, but we should be able to set the linker options for which file to link in via appropriate #pragma options.
Anyway, what we've done is create a 'window.h' file inside the platform folder (wanton\source\platform). For Windows, we create a 'windows.cpp' file inside the SPECIFIC platform folder (wanton\source\platform\mswin). We then explicitly compile and link that file and voila, we end up with a transparent method of dealing with a very platform specific system. At some point in the near future, when I get an OS X version of this ready, I'll be creating an implementation of that inside the OS X folder (wanton\source\platform\osx).
This isn't a perfect solution; it requires a lot of work in order to keep up multiple platforms. I can see using some third-party tools for generating the appropriate build files (cmake comes to mind), but you'd need something that allows you a fair degree of customizability in order to exclude folders based on the target platform. Not saying it can't be done (I know it can) but that's outside the scope of this article (for now).
Anyway, getting back on track, this is what we have:
I won't go into detail about the implementation of the MS Windows version of this class just yet (I haven't written it, TBH). However, you should see that I've got a new #include: delegate.h. This is a cool little library that makes adding delegates to your application fairly easily.
What is a delegate, you may ask? Most who read my blog already know, but in case you are new, a delegate is functionally equivalent to a Function Pointer in C. But you can use methods in classes, as well as regular function pointers. And this implementation is fast. About as fast as you can get. This is courtesy of the code project: http://www.codeproject.com/Articles/13287/Fast-C-Delegate and it's a great little read. These delegates are going to allow me to create the callbacks that I mentioned earlier. You know, once I get the window class actually working!
One other thing to mention. In getting ready for making this work across multiple platforms, I've added yet another new file to the project, a build_config.h that allows me to set on a per-platform basis, specific configuration options. Here's the contents of the file:
That should look very simple to you, until you note that I define a preprocessor define as either being 'ON' or 'OFF'. Those are defined, currently, inside util.h (until I can find a better place). It's worth it to take a look at it:
I like it. I like it a lot. You can find the original article at http://www.codersnotes.com/notes/easy-preprocessor-defines.
I don't know if I would call them 'easy preprocessor defines', but I would call them 'sensible preprocessor defines'. The win in readability alone makes it totally worthwhile.
Now that I have that generated, I've stubbed out the windows implementation. It's totally non-functional, but allows us to do a simple test.
I'll be doing a proper implementation in a bit. But for now, I want to test the 'process' in getting a build actually functional.
However, at this point in time we actually have enough to allow us to generate an app. Not a fully functional app, but something that will run without crashing. Let's put a pin in it now and actually implement our winmain function.
If you take the code as it stands right at this instant, with these changes, you end up with a 'functional' application that will start, update and immediately exit. Without showing a window. Yeah, that's useless, I know, but it moves us along into the next phase, which is actually getting a fully functional window on screen.
However, at this point, I'm going to submit the code, again to have a common reference point along the tutorial.
As with last time, you can find the code on sourceforge here: https://code.google.com/p/wanton/. HEAD will always be a work in progress. I've put the build from this blog post into R01272013B.