Introduction to LibGAlloc usage
Name
libgalloc-usage : Introduction to LibGAlloc usage
Synopsis
There are a few ways to use LibGAlloc; This chapter describes how to use the simplest forms of the API. LibGAlloc is a LibGGI extension, so in order to use it from an application you must ggiGAInit() the extension and ggiGAAttach() it to at least one open LibGGI visual. If you are writing an extension that uses LibGAlloc, you should make sure that LibGAlloc is initialized and attached to the visual when your extension is attached. That way, the user does not have to attach LibGAlloc themselves. Of course, you should also clean up by using ggiGADetach() and ggiGAExit() where appropriate. Internal LibGAlloc counters should take care of multiple calls to init/attach so it is safe not to worry about that.
Overview of typical LibGAlloc usage
There are two ways to go about using LibGAlloc. The recommended way is to try to set up all the requests for resources before attempting to activate the resources. The other way is to activate resources as you need them -- this is not recommended because LibGAlloc may not be able to organize the resources efficiently in this mode of operation, however it is supported to allow less sophisticated applications to be written in an intuitive way. There are occasions where it is necessary to allocate and deallocate resources after other resources have already been set up, so support for this second mode of operation is not an afterthought and can be expected to be coded with as high a quality as the recommended mode; it is an essential part of LibGAlloc even if it is not recommended.
Before we get into the differences in the way LibGAlloc application code is written versus regular LibGGI application code, we will look at the the way LibGGI applications set a video mode and the way LibGAlloc applications do, so that we are familiar with the functions. A normal LibGGI Application would set a mode like this (we have removed the error checking to keep the examples simple.) You should refer to the LibGGI documentation if you are new to LibGGI, to understand the below listing.
ggi_visual_t vis; ggi_mode mode; ggiInit(); /* Start up LibGGI */ vis = ggiOpen(NULL); /* Open the default display */ ggiCheckSimpleMode(vis, 640, 480, 640, 480, &mode); ggiSetMode(vis, &mode); /* We can now draw on the visual. */
The same code using LibGAlloc's recommended interface would look like this:
ggi_visual_t vis; ggiGA_resource_list reqlist; ggiGA_resource_handle mode; ggiInit(); vis = ggiOpen(NULL); ggiGAAttach(vis, NULL); ggiGAAddSimpleMode(vis, &reqlist, 640, 480, 640, 480, &mode); ggiGACheck(vis, reqlist, NULL); ggiGASet(vis, reqlist, NULL); /* We can now draw on the visual. */
In the above listing, we see that we have an additional step. The function ggiGAAddSimpleMode(3) is a lot like ggiSetSimpleMode, except that it puts a request for a video mode into a request list, rather than checking/setting it right away. There are also ggiGAAdd* functions that look like the other ggiSet* functions. The variable reqlist is what is then checked and set. It is sort of the same thing as the variable mode in the first listing, but it is much more powerful. To see why, we need to bring another extension into the picture.
An extension that uses LibGAlloc will provide one or more Add functions of its own. Suppose we have an extension that renders a dancing gerbil of any size on the screen, LibGerbil, which was written to take advantage of video cards with hardware accelerated dancing gerbils, and emulate them for people who are unfortunate enough to own a video card that does not. In addition to the API functions which move and position the dancing gerbil, LibGerbil would also provide a function GerbilAdd, which would add a request for a dancing gerbil to the reqlist variable, (If the gerbil was emulated, it would just pretend to add it.) It would look like this:
ggi_visual_t vis; ggiGA_resource_list reqlist; ggiGA_resource_handle gerbilres; gerbil_t gerry; ggiInit(); vis = ggiOpen(NULL); GerbilAttach(vis, NULL); /* This attaches LibGAlloc automatically */ ggiGAAddSimpleMode(vis, &reqlist, 640, 480, 640, GGI_AUTO, NULL); GerbilAdd(&reqlist, &gerbilres); ggiGACheck(vis, reqlist, NULL); ggiGASet(vis, reqlist, NULL); gerry = GerbilActivate(reqlist, gerbilres); GerbilSize(gerry, 50, 100); GerbilPosition(gerry, 100, 100); GerbilShow(gerry); GerbilBreakDance(gerry); sleep(200); GerbilBoogie(gerry);
Now, let us show you how that would look in the not-so-recommended ad hoc interface. The Gerbil extension would supply a function GerbilCreate. This function would be called to instantly create a dancing gerbil after the video mode had already been set:
ggi_visual_t vis; ggi_mode mode; gerbil_t gerry; ggiGA_request_list reqlist = NULL; ggiInit(); vis = ggiOpen(NULL); GerbilAttach(vis, reqlist); /* will attach LibGAlloc if not attached */ ggiCheckSimpleMode(vis, 640, 480, 640, GGI_AUTO, &mode); ggiSetMode(vis, &mode); gerry = GerbilCreate(); GerbilSize(gerry, 50, 100); GerbilPosition(gerry, 100, 100); GerbilShow(gerry); GerbilTango(gerry); sleep(200); GerbilMacarena(gerry);
So, you're saying to yourself now that this last one looks a lot easier to use and simpler, right? Well, it is. Consider, though, what happens if the Gerbil uses video RAM. In that case, the ggiSetMode call in the last example might eat all of the available video ram, and GerbilCreate will fail because there will be none left over. This last example cannot be rewritten to fix this problem, but the one before it can:
ggi_visual_t vis; ggiGA_resource_list reqlist; ggiGA_resource_handle gerbilres; gerbil_t gerry; ggiInit(); vis = ggiOpen(NULL); GerbilAttach(vis); /* will attach LibGAlloc if not attached */ GerbilAdd(&reqlist, gerbilres); ggiGAAddSimpleMode(vis, &reqlist, 640, 480, 640, GGI_AUTO, NULL); ggiGACheck(vis, reqlist, NULL); ggiGASet(vis, reqlist, NULL); gerry = GerbilActivate(reqlist, gerbilres); GerbilSize(gerry, 50, 100); GerbilPosition(gerry, 100, 100); GerbilShow(gerry); GerbilUTTauntPelvic(gerry); sleep(200); GerbilBellyDance(gerry);
All we had to do was put the request for the dancing gerbil first in the list. That way, the gerbil gets the video RAM that it needs, and the main mode uses up the rest of the RAM. By using a request list and the recommended LibGAlloc API, we can do a lot of wheeling and dealing to get the most out of our display targets.
Tips and Tricks
Special handling of "mode resources"
To LibGAlloc, the main video mode is just another feature to be negotiated along with the rest -- but there are two things that do make it special. The first is that LibGAlloc provides the ggiGAAddMode(3) function, instead of leaving that up to an extension like it does with all other resources. The second is that since it is a given that there can be only one main video mode, you are allowed to call ggiGAAddMode(3) several times to prioritize the main video mode among the features. That is to say, when you ggiGACheck(3) or ggiGASet(3) a request list, LibGAlloc will process the modes you requested in the order they appear in the list, discarding the last mode that was requested in favor of the next successful mode and adjusting all the features to fit the new mode. So, for example, you can guarantee yourself a 640x480 main video mode by calling ggiGAAddMode first to put a 640x480 mode first in the request list, then you can add some requests for features, and after them you can add a request for 800x600, and then ask for more features, and so on.
There is a way to do this for other resources, as well, and it is called a tag, but that is an advanced feature, left for discussion later in another chapter.
Tips for handling request and result lists
The external API tries to keep you safe from doing anything that will cause unexpected results, but there are a couple of places where some caution is in order, and, if you decide to use any of the gray ops, you must have a good understanding of how request and result lists relate to each other.
First, you should take care with your ggiGA_resource_list variables to always initialize them to NULL when they are first created, and to call ggiGAEmptyList on them before reusing them after that. If you put a previously used ggiGA_resource_list as the *result parameter of ggiGACheck, ggiGASet, or ggiGAGet, LibGAlloc will not empty the list automatically and memory may be leaked.
Second, you must understand that the only functions that ever alter the contents of the reqlist parameter are the Release and Add functions (either ggiGARelease/ggiGAAdd* or the equivalent functions supplied by an extension.) ggiGASet and ggiGACheck will leave reqlist completely untouched. So, if you are wondering how to find what values ggiGACheck is suggesting, you must use the reslist parameter to get a result list.
Third, handles you got by calling LibGGIAdd on reqlist only have a meaning when they are used with reqlist. Once you have a result list, you must get a new handle from the result list in order to see the results. Transferring handles is done through the function ggiGAHandle, which will find a matching result handle for a request handle. You can replace all your request handles with result handles like so:
reshandle3 = ggiGAHandle(reslist, reqlist, reqhandle3);
... and then empty the request list. Or, you can hang on to the request list and handles and use ggiGAHandle to get temporary result handles which you can use and discard as you need.
Fourth, just remember that your request handles are only valid so long as you have the request list they were created with, that result handles are only valid so long as you have the result list you got them from, and that once you alter a request list, you can no longer use it to get result handles from any previously generated result lists. To transfer a result handle correctly, the reslist must have been gotten from the reqlist with ggiGACheck or ggiGAGet and/or ggiGASet, and the handle must be transferred before the reqlist is altered. (By altering the reqlist, we mean adding any resources to it or using it to release in-use resources. Altering the properties of individual resources will not confuse ggiGAHandle.)
Similarly, you should not call ggiGARelease on a request list that has been altered since it was created by an API call. It is strongly advised to be sure your handles and lists are valid before calling ggiGARelease or ggiGASet, since these affect the running visual.
We said above that when you call ggiGACheck, you can get suggestions in the result list. These can take two forms. One is when any properties were set to GGI_AUTO or some other default setting in which case the defaults are now replaced with probed values. The other is when the properties failed to fit, and LibGAlloc found some alternative properties that do not meet the limits you asked for, but would work. You can tell if a resource has failed by calling ggiGAIsFailed on the result handle -- however you should know that only the first failed resource is flagged by ggiGACheck, and once it has failed, the resource stays flagged failed until it succeeds, which will not be until all the resources above it in the list succeed. In any case, though, you can tell whether LibGAlloc has made suggestions by using the function ggiGAIsModified.
You can take the results list and feed it back into ggiGACheck after modifying properties. Request and result lists are really no different. However, when a call to ggiGASet is successful, the result list is different in one small way -- all the resources in that list are actually allocated and LibGAlloc assumes that they are in use. If you change or add to this list and feed it back into ggiCheckMode then only those changes that do not affect the in use resources will succeed.
Once resources are in use, the ggiGARelease command can be used to free them; it is usually not called directly but is called by an extension library function named something like GerbilRelease instead, so that other things can be deallocated along with the resource. When ggiGARelease is (successfully) called, the resource will be deleted from the request list, the handle passed will be set to NULL, and any other handle to that resource will become invalid.