![]() |
Pig! — Building a SkyOS Application in C++ |
|
|
Chapter 2 |
|
At the end of Chapter 1, Pig! displayed a plain grey window with non-functional menus, so the first order of business this round is to make the menus work. We'll also add a second 'Splash' window to test out the message handling and demonstrate how the window template class works.
The initial set of C++ classes that appeared in the last chapter has taken on a whole new look, as it has been split into two parts: the application and window classes unique to Pig! and a more robust set of generic classes that can be used (and re-used) over and over again. To distinguish between the two, I have prefixed all of the generic class names with an 'H', i.e., HApp, HRect, HWindow, and so on. It's still way too early to call this a 'class library'—and other SkyOS developers are pursuing that goal—but it does at least offer some ideas on creating reusable code for SkyOS. This set of generic classes will be collectively referred to as the Humble Framework, and will be documented separately from the Pig! application classes and code.
All of the source code and a precompiled binary for this chapter can be downloaded here: PigChapter02.zip.
As previously mentioned, the Humble framework consists of a set of generic C++ classes that are designed to help create native SkyOS applications more efficiently by re-using as much code as practical. Moreover, classes are provided that enhance several SkyOS primitive data types by adding better error checking and additional capabilities. Since the Humble framework has the potential to grow quite large—not to mention complex—I have started using the open-source Doxygen tool to generate a on-line documentation for the Humble framework. To briefly summarize the framework's contents:
HApp — a generic application class that includes the top-level application window
HWindow — a generic window template class that manages a single GUI window
HDebugLog — manages a log file for debugging information
HError — stores information about an error
HDib — wrapper around the Device Independent Bitmap (DIB) structure
HPoint — manages a point structure
HRect — wrapper around the s_region (rectangle) structure
HStr — a generic string template that uses a fixed-size buffer
For details or to download the latest version of the source code, please refer to the Humble Framework on-line documentation.
The single most important tool for creating our SkyOS application is the C++ compiler (gcc) which is available for a variety of platforms and operating systems from the GCC Home Page. It doesn't matter if you are developing under Windows, Linux, or SkyOS itself, but what does matter is that you must be using version 2.95.3-10 of gcc and it's accompanying libraries. Newer versions will compile the code, but you will not be able to successfully link them, because the C++ runtime libraries for SkyOS have not been updated yet. (Robert, are you listening?) The hardest part of building the first C++ application is getting the make files properly set up so that all of the necessary libraries and startup code are linked. We will be using two separate make files: rules.mak to define a global set of dependencies and makefile to define the dependencies unique to Pig!. The following example is for building Pig! on a Windows system, using the Cygwin version of GCC; if you are using a different development environment, you will have to tailor the make files somewhat. Your mileage may vary.
The only significant change to the rules.mak make file since the previous Chapter is the addition of a new symbol (DIR_HUMBLE) that points to the directory where the Humble class headers are stored.
The makefile file has been altered to reflect the new naming conventions, and now makes both application source files dependent on the entire set of Humble Framework headers. The Pig! application currently consists of only two (2) source files: Pig.cpp and WSplash.cpp, each of which has it's own header file as well.
|
# |
The Pig! application currently consists of two unique classes, each of which has an interface (header) file and an implementation (source) file. The classes are as follows:
PigApp — a subclass of the HApp framework class, this is the top-level application object and is stored in the global variable theApp
WSplash — a subclass of the HWindow framework template class, this is the 'splash window' displayed when the user selects "Help:About"
We'll start with the top-level class, which represents the entire application. This class—called App in Chapter 1 but now renamed PigApp—is declared in the header file Pig.h and is implemented in the source file Pig.cpp. Unlike the original version, PigApp is declared as a subclass of HApp, so it inherits all of the functionality embedded in the HApp class, which includes the top level window. To instantiate the PigApp application class, it is necessary to implement several functions that are declared as pure virtual by the base class:
Create() — inherited from the HWindow template class, this function creates the application window
Draw() — inherited from the HWindow template class, this function draws the client of the application window
GetAppName() — inherited from the HApp class, this function returns the application name as a readable string
The Create() method is almost identical to the original version, except that the last thing is does now is call the HApp::manageWindow() method. This turns the newly-created window over to the framework for management. The Draw() function is also unchanged, except that the rDirty parameter is now references an HRect object instead of a raw s_region. In addition to these mandatory functions, the PigApp class also overrides the Init() method so that it can automatically display a splash screen when the application starts up. It accomplishes this by sending itself a CMD_HELP_ABOUT command just before entering the main event loop in HApp::Run().
|
|
To make our non-functional menu bar actually work, two new command handlers have been added to the PigApp class: onAppExit() and onHelpAbout() which will receive the CMD_APP_EXIT and CMD_HELP_ABOUT commands. To connect the commands to their respective handlers, some new entries have been made to the message map in the class declaration:
|
|
Also new to the message map is the INCLUDE_MAP(base_class) statement, which routes any messages NOT handled by the PigApp class back to HApp for final disposition. This technique works for not only the application, but also for window classes derived from HWindow; (which is logical, because HApp is derived from HWindow as well). The generic template for a command handler is bool myCommandHandler(uint32, HRESULT &) which returns true if the command is handled, or false otherwise. The first parameter is the command identifier being handled, and the second is a success / failure flag; the flag defaults to S_OK, but should be set to any non-zero value if something went wrong during the command processing. The new command handler for the CMD_APP_EXIT—declared inline in the Pig.h header file—now looks like this:
|
|
The 'splash window' displayed by the Pig! application is a simple, unadorned window that displays a JPEG image read in from an external file. It is declared in the WSplash.h header file, and implemented in WSplash.cpp; and like virtually all windows in the Humble framework, it is sub-classed from the HWindow template class. This causes all of the standard window processing to be inherited, so the WSplash class must also implement the two required methods:
Create() — creates the window
Draw() — draws the contents of the window
The Create() method for the WSplash window accomplishes several tasks—with a surprisingly small amount of code—including (1) loading the JPEG image, (2) sizing the window to perfectly fit the image, and (3) centering the window on the screen. The reason the code is so small is because a bulk of the processing and error-checking takes place behind the scenes in the framework code.
|
|
Likewise, the Draw() method is also fairly compact, as it sets up and carries out a 'blit' of the JPEG preloaded image to the device context.
|
|
Since we don't want the user to have to stare at our beautiful
splash screen forever, the splash window will immediately close if the left
mouse button is clicked anywhere in the window's client area, or after 15
seconds has elapsed (whichever comes first). Detecting the mouse click
is fairly simple: we add the line
MAP_MSG(MSG_MOUSE_BUT1_RELEASED,
onMouseBut1Released)
to the message map, and add the message handler function, which need only call
the Destroy() method inherited from the
HWindow template class.
|
|
Having the window automatically self-destruct in 15 seconds takes a little more work, but not much. First of all, the WSplash class overrides the onWindowCreate() function so that when it is created, a timer can be started. It then waits for the MSG_TIMER_REACHED message to be received, whereupon it destroys the window in the same manner as if the user clicked the mouse button. Lastly, whenever the window is destroyed, the timer is killed as well; strictly speaking, the timer may already have expired, but it never hurts to clean up after yourself. Also notice how the onWindowCreate() and onWindowDestroy() methods both invoke the base class version as well:
|
|
One thing conspicuously absent from this new version of Pig! is the standard C/C++ main() function, previously implemented in the Main.cpp source file (which is also missing!). Since the Humble framework provides the HApp application class, it also provides the DECLARE_APP() macro, which is invoked at the top of the Pig.cpp source file:
|
|
This one line not only creates the global variable theApp, but also implements a complete main() function! Needless to say, forgetting to include this one line—or declaring it more than once—will result in all kinds of horrible linker errors.
There may not be a lot of changes on the outside, but there is a ton of improvement on the inside. As always, I highly recommend that you grab the Pig! source code and build your own version, but there is also a precompiled binary that can be loaded and run 'as is'. You'll also need to save the Splash JPEG to /boot/home/Splash.jpg to see the pretty new splash window. Either way, you will still be able to read the pig-debug.txt file and see how the program flow went, and what messages were routed where.
In Chapter 3, we'll look at saving (and restoring) application settings between sessions, using a user preferences file.
Until the next chapter…Tschüß!
,,,^..^,,,
|
|
|
2004.08.20-14:10