My SDL_WM_ToggleFullScreen() lookalike

As we were discussing earlier, SDL_WM_ToggleFullScreen() is a no-op on
platforms where there’s no guarantee that the surface’s pointers won’t
change.

I’ve hacked together some generic code to flip the screen surface using
SDL_SetVideoMode() that doesn’t promise anything about the state of the
pointers. It seems to work pretty well on Linux (x11 driver) and WinNT 4.0
(windib driver). It compiles with gcc, cygwin, and Watcom C, and maybe
Visual C.

There are certain atributes of the surface that might not be set up right
under certain circumstances, I’m sure. Please comment. Also, feel free to
use this code in your program.

Thoughts are welcome. Thankye.

–ryan.

----- snip. -----

#include "SDL.h"

/**
 * Attempt to flip the video surface to fullscreen or windowed mode.
 *  Attempts to maintain the surface's state, but makes no guarantee
 *  that pointers (i.e., the surface's pixels field) will be the same
 *  after this call.*
 * Caveats: Your surface pointers will be changing; if you have any other
 *           copies laying about, they are invalidated.
 *
 *          Do NOT call this from an SDL event filter on Windows. You can
 *           call it based on the return values from SDL_PollEvent, etc, just
 *           not during the function you passed to SDL_SetEventFilter().
 *
 *          Thread safe? Likely not.
 *
 *   @param surface pointer to surface ptr to toggle. May be different
 *                  pointer on return. MAY BE NULL ON RETURN IF FAILURE!
 *   @param flags   pointer to flags to set on surface. The value pointed
 *                  to will be XOR'd with SDL_FULLSCREEN before use. Actual
 *                  flags set will be filled into pointer. Contents are
 *                  undefined on failure. Can be NULL, in which case the
 *                  surface's current flags are used.
 *  @return non-zero on success, zero on failure.    
 */
int attempt_fullscreen_toggle(SDL_Surface **surface, Uint32 *flags)
{
    long framesize = 0;
    void *pixels = NULL;
    SDL_Color *palette = NULL;
    SDL_Rect clip;
    int ncolors = 0;
    Uint32 tmpflags = 0;
    int w = 0;
    int h = 0;
    int bpp = 0;

    if ( (!surface) || (!(*surface)) )  // don't bother if there's no surface.
        return(0);

    tmpflags = (*surface)->flags;
    w = (*surface)->w;
    h = (*surface)->h;
    bpp = (*surface)->format->BitsPerPixel;

    if (flags == NULL)  // use the surface's flags.
        flags = &tmpflags;

    SDL_GetClipRect(*surface, &clip);

        // save the contents of the screen.
    framesize = (w * h) * ((*surface)->format->BytesPerPixel);
    pixels = malloc(framesize);
    if (pixels == NULL)
        return(0);
    memcpy(pixels, (*surface)->pixels, framesize);

    if ((*surface)->format->palette != NULL)
    {
        ncolors = (*surface)->format->palette->ncolors;
        palette = malloc(ncolors * sizeof (SDL_Color));
        if (palette == NULL)
        {
            free(pixels);
            return(0);
        } // if
        memcpy(palette, (*surface)->format->palette->colors,
               ncolors * sizeof (SDL_Color));
    } // if

    *surface = SDL_SetVideoMode(w, h, bpp, (*flags) ^ SDL_FULLSCREEN);

    if (*surface != NULL)
        *flags ^= SDL_FULLSCREEN;

    else  // yikes! Try to put it back as it was...
    {
        *surface = SDL_SetVideoMode(w, h, bpp, tmpflags);
        if (*surface == NULL)  // completely screwed.
        {
            free(pixels);
            return(0);
        } // if
    } // if

    memcpy((*surface)->pixels, pixels, framesize);
    free(pixels);

    if (palette != NULL)
    {
            // !!! FIXME : No idea if that flags param is right.
        SDL_SetPalette(*surface, SDL_LOGPAL, palette, 0, ncolors);
        free(palette);
    } // if

    SDL_SetClipRect(*surface, &clip);

    return(1);
} // attempt_fullscreen_toggle