Friday, February 1, 2008

New extension: gluttonGetOption

In GLUT, there is a handy-dandy function for setting various global options:

void glutSetOption( GLenum eWhat , int value );


This is great, but what if you want to change things temporarily and you don't know what the original settings are? This could happen, for example, if you're writing a library function that sits between GLUT and the rest of your application.

What you need is a reading analogue to glutSetOption, something like a glutGetOption. Unfortunately, there is no glutGetOption.

Fortunately, there is a gluttonGetOption in the extended GLUT-like library I'm working on, glutton.

To make this function, I first added the declaration in freeglut_ext.h (glutton is based upon the marvelous freeglut library):

int FGAPIENTRY gluttonGetOption( GLenum eWhat );


Then, in freeglut_state.c, I copied the glutSetOption implementation and reversed the data flow:

int FGAPIENTRY gluttonGetOption( GLenum eWhat )
  {
  FREEGLUT_EXIT_IF_NOT_INITIALISED ( "gluttonGetOption" );
  
    switch( eWhat )
    {
    case GLUT_INIT_WINDOW_X:
        return fgState.Position.X;
        break;

    case GLUT_INIT_WINDOW_Y:
        return fgState.Position.Y;
        break;

    case GLUT_INIT_WINDOW_WIDTH:
        return fgState.Size.X;
        break;

    case GLUT_INIT_WINDOW_HEIGHT:
        return fgState.Size.Y;
        break;

    case GLUT_INIT_DISPLAY_MODE:
        return (int)fgState.DisplayMode;
        break;

    case GLUT_ACTION_ON_WINDOW_CLOSE:
        return fgState.ActionOnWindowClose;
        break;

    case GLUT_RENDERING_CONTEXT:
        return fgState.UseCurrentContext;
        break;

    case GLUT_DIRECT_RENDERING:
        return fgState.DirectContext;
        break;

    case GLUT_WINDOW_CURSOR:
        return fgStructure.CurrentWindow->State.Cursor;
        break;

    case GLUT_AUX:
      return fgState.AuxiliaryBufferNumber;
      break;

    case GLUT_MULTISAMPLE:
      return fgState.SampleNumber;
      break;

    default:
      fgWarning( "glutGetOption(): missing enum handle %d", eWhat );
      return 0xDeadBeef;
      break;
    }
  }


It doesn't get much easier than that.

Thursday, January 31, 2008

Extension Revisited For Linux: gluttonCloseRequestedFunc

A few days ago I posted that I had written a glutton extension for the GLUT api called gluttonCloseRequestedFunc. The intended purpose of this function is to provide a callback function that is called when a user has decided to close a window. During this function, the application has the opportunity to do things like display a dialog box to ask the user to confirm the close. In the event the callback function determines that the close should be canceled, it can set the parameter "*Action" to the value gluttonCloseRequestedIgnore, and glutton will then ignore the close request rather than close the window.

At the time, I had only implemented the extension for Windows, and promised to revisit this one for Linux. As it turns out, implementing it on Linux was extremely simple, because the Linux implementation of freeglut already had 99% of what was necessary to get this to work. This means that it is very likely that this will work for other *nix/X11 implementations with no additional work, but I don't have access to such environments, so I cannot tell with certainty.

Anyway, as I said, the implementation is very simple. I merely copied the Windows portion that does the callback into glutMainLoopEvent's handler for ClientMessage events. More specifically, for fgDisplay.DeleteWindow ClientMessage events:



case ClientMessage:
/* Destroy the window when the WM_DELETE_WINDOW message arrives */
if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
{
GETWINDOW( xclient );
#ifdef GLUTTON
{
int Action = gluttonCloseRequestedDefault;
INVOKE_WCB( *window , GluttonCloseRequested , ( &Action ) );
switch( Action )
{
case gluttonCloseRequestedDefault: break;
/* proceed to close the window (fgDestroyWindow, below) */
case gluttonCloseRequestedIgnore: return;
/* ignore the message. */
}
}
#endif

fgDestroyWindow ( window );



I honestly didn't expect this to be as easy as it was. I was expecting to have to go through the pain of telling X that I wanted it to send a ClientMessage to glutton, and to deal with finding all of the "atoms" involved. Many thanks to the freeglut developers for relieving me of /that/ burden!

Next time: keystroke management for linux. [ Edit: No need - I cannot reproduce the keystroke bug on Fedora 8, so I won't be spending my time on trying to fix it :-) ]

Monday, January 28, 2008

Glutton Keystroke Management

Freeglut has had an open bug for about a year now,

http://sourceforge.net/tracker/index.php?func=detail&aid=1647258&group_id=1032&atid=101032

Basically, what happens is that freeglut takes all keystrokes and ignores them after sending them to the keystroke callback.

For most keys, this is perfectly acceptable. But on Windows, it is common for keyboard junkies to press Alt+F4 rather than undertake the tedium of moving the mouse over to the "x" in the title bar and clicking. Or pressing "Alt+space" to bring up the window's system menu in order to minimize or maximize. If the keystrokes are ignored, then those capabilities disappear!

In glutton, it seemed to me that a simple fix for this bug would be to add a function, "gluttonSetWindowOption", which presents a method to write to a set of "int" options associated with each window. At first, the only option is called "gluttonWindowOptionKeyboardPassToWM". If set to 1, then glutton will send every key stroke to the window manager. On Windows, this means that Alt+F4 will work again, as will Alt+Space.

Here's what I did to make this change. In freeglut_ext.h, I added the following



/*
* Set flags to control window behaviors.
*/
FGAPI void FGAPIENTRY gluttonSetWindowOption( int Option , int );
enum
{ gluttonWindowOptionKeyboardPassToWM
/*
* If 1, key presses will be passed to the window manager.
* If 0, key presses will not be passed to the window manger (old behavior).
*/
};



In freeglut_window.c:



void FGAPIENTRY gluttonSetWindowOption( int Option , int Value )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED( "glutSetWindowOption" );
FREEGLUT_EXIT_IF_NO_WINDOW( "glutSetWindowOption" );
switch( Option )
{
case gluttonWindowOptionKeyboardPassToWM:
fgStructure.CurrentWindow->AllKeysToWindowManager = Value;
break;
}
}



In freeglut_internal.h, I added this to the declaration of tagSFG_Window:



#ifdef GLUTTON
GLboolean AllKeysToWindowManager; /* Set to 1 if all keys should go to window manager */
#endif



Finally, in freeglut_main.c, in the message processing function, I added the following in strategic locations:



#ifdef GLUTTON
if ( window->AllKeysToWindowManager )
lRet = DefWindowProc( hWnd , uMsg , wParam , lParam );
#endif



This happens after any callbacks are made. There is currently no way to prevent a single keystroke from going to the window manager, and there's no reason to have such a method that I can see (right now), other than the desire to ignore Alt+F4 or Alt+Space. Such behavior would be very user-unfriendly IMHO, so I'm not implementing it at this time. But should the need arise, a simple strategy would be to add an option that could be set inside of the callback to withhold the current keystroke from the window manager. The if ( window->AllKeysToWindowManager ) statement would be changed to also check the new option, and an else clause would be added to reset the new option.

Of course, this extension doesn't address X yet. Since I'm getting ready to address X for my last extension (give the user a chance to cancel before closing a window), I'll look into implementing this one there as well. Food for 2 posts already, and I've only just started working on glutton. Nice!

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.

Glutton Project Begins

I'm currently working on a project where a cross-platform OpenGL-based GUI is necessary. I naturally started looking at GLUT, and then freeglut. Both are fairly nice, but lack some features that I view as necessary for my project.

I started trying to implement those features and work with the freeglut team to get them integrated into the main source tree. Unfortunately, after two weeks since my last message, I've received no response. I suppose they're busier on other projects - they did indicate that they consider freeglut as complete - but for my purposes I need faster turnaround times.

Rather than whine about the current state of affairs, I've decided to start up a fork of freeglut, which I will call glutton. Again, this action is not anything against the freeglut team; they have done a phenomenal job and deserve the utmost respect and praise for what they've created.

In any event, I've decided that as I develop glutton, I'm going to try writing a series of mini-articles here to help track my progress. With any luck, glutton will grow into a first-glass openGL library suitable for use inside applications on any platform.