Thursday, January 24, 2008

First Extension: gluttonCloseRequestedFunc

As mentioned in my last post, I've decided to create a glut-like library called glutton, based upon the remarkable freeglut library. I am simply in awe of it from nearly every viewpoint, including how easy it is to perform surgery on it.

The first extension I am adding is one I've decided to call gluttonCloseRequestedFunc. It is a function that allows you to set a callback that glutton will call when the end user tries to close the window.

Here is its interface:


FGAPI void FGAPIENTRY gluttonCloseRequestedFunc
( void (* callback)( int* ) );
enum
{ gluttonCloseRequestedDefault
, gluttonCloseRequestedIgnore };



When you call this function, pass a function matching this signature:


void gluttonCloseRequested( int* Action );



The gluttonCloseRequested callback function will be called by glutton when the user attempts to close the window, e.g., if you click the "x" button in the window's title bar on MS Windows Systems.

Inside your callback, you have the option of presenting new UI, checking flags, etc., and making a determination as to whether the request should proceed or be ignored. The default is to proceed, and is signified by *Action having the value gluttonCloseRequestedDefault. To ignore the request (that is, to keep the window open even though the user has tried to close it), do this:


*Action = gluttonCloseRequestedIgnore;



How did I implement this extension?

Well, first, I added the interface to GL/freeglut_ext.h. This makes the declaration of the function gluttonCloseRequestedFunc visible to code that includes "GL/glut.h"

Next, I added an implementation in freeglut_callbacks.c:


#ifdef GLUTTON
void FGAPIENTRY gluttonCloseRequestedFunc( void (*callback)( int* ) )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "gluttonCloseRequestedFunc" );
SET_CALLBACK( GluttonCloseRequested );
}
#endif



This implementation is fairly straightforward - it takes the callback parameter, and stores it for later use. Of course, the location it is stored has to be created.

That involves adding this to freeglut_internal.h:



#ifdef GLUTTON
typedef void (* FGCBGluttonCloseRequested )( int* );
#endif

...

#ifdef GLUTTON
CB_GluttonCloseRequested,
#endif

...



Finally, we have to make it actually work in freeglut_main.c:



case SC_CLOSE :
/* Followed very closely by a WM_CLOSE message */
#ifdef GLUTTON
{
int Action = gluttonCloseRequestedDefault;
INVOKE_WCB( *window , GluttonCloseRequested , ( &Action ) );
switch( Action )
{
case gluttonCloseRequestedDefault: break; // allow the window manager (Windows) to deal with it.
case gluttonCloseRequestedIgnore: return 1; // tell windows we've dealt with it.
}
}
#endif
break ;



You might notice that this change only affects Windows. If so, you're very astute - it only affects the Windows message handler for the SC_CLOSE variation of the WM_SYSCOMMAND message. It should not be very difficult to get this to work for X11, or even with OSX (although my OSX skills are a bit rusty. I need to see about getting a Mac.) For X11, I've managed to get the behavior I'm looking for in straight X11 programs, so I know it is possible. If it isn't possible in OSX, I'm not sure how I will handle the discrepancy. That actually brings up an interesting question for writing portable code... What should you do when a feature isn't available on all platforms? I'll probably answer that one on a case-by-case basis. In this one, I believe it's available, because I've seen OSX programs do what I'm trying to allow with this callback.

In my next installment, I'm going to cover how to make freeglut handle keystrokes differently. After that, I intend to revisit this extension on X11.

No comments: