diff src/video/glsdl/SDL_glsdl.c @ 1895:c121d94672cb

SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
author Sam Lantinga <slouken@libsdl.org>
date Mon, 10 Jul 2006 21:04:37 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/glsdl/SDL_glsdl.c	Mon Jul 10 21:04:37 2006 +0000
@@ -0,0 +1,2465 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2006 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/*
+ * glSDL "SDL-over-OpenGL" video driver implemented by
+ * David Olofson <david@olofson.net> and
+ * Stephane Marchesin <stephane.marchesin@wanadoo.fr>
+ */
+#include <math.h>
+
+#include "SDL.h"
+#include "SDL_error.h"
+#include "SDL_video.h"
+#include "SDL_mouse.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_pixels_c.h"
+
+#include "SDL_glsdl.h"
+
+#undef	DEBUG_GLSDL
+#undef	DEBUG_GLSDL_CHOP
+#define	FAKE_MAXTEXSIZE	256
+#undef GLSDL_GRAPHICAL_DEBUG
+
+/* Initialization/Query functions */
+
+/* Hardware surface functions */
+static int glSDL_SetColors(_THIS, int firstcolor, int ncolors,
+                           SDL_Color * colors);
+static int glSDL_AllocHWSurface(_THIS, SDL_Surface * surface);
+static int glSDL_LockHWSurface(_THIS, SDL_Surface * surface);
+static int glSDL_FlipHWSurface(_THIS, SDL_Surface * surface);
+static void glSDL_UnlockHWSurface(_THIS, SDL_Surface * surface);
+static void glSDL_FreeHWSurface(_THIS, SDL_Surface * surface);
+static int glSDL_FillHWRect(_THIS, SDL_Surface * dst, SDL_Rect * rect,
+                            Uint32 color);
+static int glSDL_CheckHWBlit(_THIS, SDL_Surface * src, SDL_Surface * dst);
+static int glSDL_SetHWColorKey(_THIS, SDL_Surface * surface, Uint32 key);
+static int glSDL_SetHWAlpha(_THIS, SDL_Surface * surface, Uint8 alpha);
+static int glSDL_VideoInit(_THIS, SDL_PixelFormat * vformat);
+static SDL_Rect **glSDL_ListModes(_THIS, SDL_PixelFormat * format,
+                                  Uint32 flags);
+static void glSDL_VideoQuit(_THIS);
+static void glSDL_UpdateRects(_THIS, int numrects, SDL_Rect * rects);
+static SDL_Surface *glSDL_SetVideoMode(_THIS, SDL_Surface * current,
+                                       int width, int height, int bpp,
+                                       Uint32 flags);
+
+#define	IS_GLSDL_SURFACE(s)	((s) && glSDL_GetTexInfo(s))
+
+#define	LOGIC_W(s)	( IS_GLSDL_SURFACE(this,s) ? TEXINFO(s)->lw : (s)->w )
+#define	LOGIC_H(s)	( IS_GLSDL_SURFACE(this,s) ? TEXINFO(s)->lh : (s)->h )
+
+#define	GLSDL_NOTEX	(~0)
+
+/*
+ * Special version for glSDL, which ignores the fake SDL_HWSURFACE
+ * flags, so we don't have SDL calling us back whenever we want to
+ * do some internal blitting...
+ */
+static void
+glSDL_SoftBlit(SDL_Surface * src, SDL_Rect * srcrect,
+               SDL_Surface * dst, SDL_Rect * dstrect)
+{
+    SDL_BlitInfo info;
+
+    if (srcrect)
+        if (!srcrect->w || !srcrect->h)
+            return;
+
+    /* Check to make sure the blit mapping is valid */
+    if ((src->map->dst != dst) ||
+        (src->map->dst->format_version != src->map->format_version))
+        if (SDL_MapSurface(src, dst) < 0)
+            return;
+
+    /* Set up the blit information */
+    if (srcrect) {
+        info.s_pixels = (Uint8 *) src->pixels +
+            (Uint16) srcrect->y * src->pitch +
+            (Uint16) srcrect->x * src->format->BytesPerPixel;
+        info.s_width = srcrect->w;
+        info.s_height = srcrect->h;
+    } else {
+        info.s_pixels = (Uint8 *) src->pixels;
+        info.s_width = src->w;
+        info.s_height = src->h;
+    }
+    info.s_skip = src->pitch - info.s_width * src->format->BytesPerPixel;
+    if (dstrect) {
+        info.d_pixels = (Uint8 *) dst->pixels +
+            (Uint16) dstrect->y * dst->pitch +
+            (Uint16) dstrect->x * dst->format->BytesPerPixel;
+        /*
+         * NOTE: SDL_SoftBlit() uses the 'dstrect' for this!
+         *       This version is more like SDL_BlitSurface().
+         */
+        info.d_width = srcrect->w;
+        info.d_height = srcrect->h;
+    } else {
+        info.d_pixels = (Uint8 *) dst->pixels;
+        info.d_width = dst->w;
+        info.d_height = dst->h;
+    }
+    info.d_skip = dst->pitch - info.d_width * dst->format->BytesPerPixel;
+    info.aux_data = src->map->sw_data->aux_data;
+    info.src = src->format;
+    info.table = src->map->table;
+    info.dst = dst->format;
+
+    src->map->sw_data->blit(&info);
+}
+
+
+/* 
+ * Another special version. Doesn't lock/unlock, and doesn't mess
+ * with flags and stuff. It just converts the surface, period.
+ * Does not convert into palletized formats.
+ */
+static SDL_Surface *
+glSDL_ConvertSurface(SDL_Surface * surface,
+                     SDL_PixelFormat * format, Uint32 flags)
+{
+    SDL_Surface *convert;
+    Uint32 colorkey = 0;
+    Uint8 alpha = 0;
+    Uint32 surface_flags;
+    SDL_Rect bounds;
+
+    /* Create a new surface with the desired format */
+    convert = SDL_CreateRGBSurface(flags,
+                                   surface->w, surface->h,
+                                   format->BitsPerPixel, format->Rmask,
+                                   format->Gmask, format->Bmask,
+                                   format->Amask);
+    if (convert == NULL) {
+        return (NULL);
+    }
+
+    /* Save the original surface color key and alpha */
+    surface_flags = surface->flags;
+    if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) {
+        /* Convert colourkeyed surfaces to RGBA if requested */
+        if ((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY && format->Amask) {
+            surface_flags &= ~SDL_SRCCOLORKEY;
+        } else {
+            colorkey = surface->format->colorkey;
+            SDL_SetColorKey(surface, 0, 0);
+        }
+    }
+    if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
+        /* Copy over the alpha channel to RGBA if requested */
+        if (format->Amask) {
+            surface->flags &= ~SDL_SRCALPHA;
+        } else {
+            alpha = surface->format->alpha;
+            SDL_SetAlpha(surface, 0, 0);
+        }
+    }
+
+    /* Copy over the image data */
+    bounds.x = 0;
+    bounds.y = 0;
+    bounds.w = surface->w;
+    bounds.h = surface->h;
+    glSDL_SoftBlit(surface, &bounds, convert, &bounds);
+
+    /* Clean up the original surface, and update converted surface */
+    if (convert != NULL) {
+        SDL_SetClipRect(convert, &surface->clip_rect);
+    }
+    if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) {
+        Uint32 cflags = surface_flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK);
+        if (convert != NULL) {
+            Uint8 keyR, keyG, keyB;
+
+            SDL_GetRGB(colorkey, surface->format, &keyR, &keyG, &keyB);
+            SDL_SetColorKey(convert, cflags | (flags & SDL_RLEACCELOK),
+                            SDL_MapRGB(convert->format, keyR, keyG, keyB));
+        }
+        SDL_SetColorKey(surface, cflags, colorkey);
+    }
+    if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
+        Uint32 aflags = surface_flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
+        if (convert != NULL) {
+            SDL_SetAlpha(convert, aflags | (flags & SDL_RLEACCELOK), alpha);
+        }
+        if (format->Amask) {
+            surface->flags |= SDL_SRCALPHA;
+        } else {
+            SDL_SetAlpha(surface, aflags, alpha);
+        }
+    }
+
+    /* We're ready to go! */
+    return (convert);
+}
+
+
+/*----------------------------------------------------------
+  Some OpenGL function wrappers
+  ----------------------------------------------------------*/
+
+static struct
+{
+    int do_blend;
+    int do_texture;
+    GLuint texture;
+    GLenum sfactor, dfactor;
+} glstate;
+
+static void
+glSDL_reset(void)
+{
+    glstate.do_blend = -1;
+    glstate.do_blend = -1;
+    glstate.texture = GLSDL_NOTEX;
+    glstate.sfactor = 0xffffffff;
+    glstate.dfactor = 0xffffffff;
+}
+
+static __inline__ void
+glSDL_do_blend(_THIS, int on)
+{
+    if (glstate.do_blend == on)
+        return;
+
+    if (on)
+        this->glEnable(GL_BLEND);
+    else
+        this->glDisable(GL_BLEND);
+    glstate.do_blend = on;
+}
+
+static __inline__ void
+glSDL_do_texture(_THIS, int on)
+{
+    if (glstate.do_texture == on)
+        return;
+
+    if (on)
+        this->glEnable(GL_TEXTURE_2D);
+    else
+        this->glDisable(GL_TEXTURE_2D);
+    glstate.do_texture = on;
+}
+
+static __inline__ void
+glSDL_blendfunc(_THIS, GLenum sfactor, GLenum dfactor)
+{
+    if ((sfactor == glstate.sfactor) && (dfactor == glstate.dfactor))
+        return;
+
+    this->glBlendFunc(sfactor, dfactor);
+
+    glstate.sfactor = sfactor;
+    glstate.dfactor = dfactor;
+}
+
+static __inline__ void
+glSDL_texture(_THIS, GLuint tx)
+{
+    if (tx == glstate.texture)
+        return;
+
+    this->glBindTexture(GL_TEXTURE_2D, tx);
+    glstate.texture = tx;
+}
+
+
+
+
+/*----------------------------------------------------------
+  glSDL specific data types
+  ----------------------------------------------------------*/
+
+typedef enum
+{
+    GLSDL_TM_SINGLE,
+    GLSDL_TM_HORIZONTAL,
+    GLSDL_TM_VERTICAL,
+    GLSDL_TM_HUGE
+} GLSDL_TileModes;
+
+
+typedef struct private_hwdata
+{
+    /* Size of surface in logic screen pixels */
+    int lw, lh;
+
+    int textures;
+    GLuint *texture;
+    int texsize;                /* width/height of OpenGL texture */
+    GLSDL_TileModes tilemode;
+    int tilew, tileh;           /* At least one must equal texsize! */
+    int tilespertex;
+    SDL_Rect virt;              /* Total size of assembled surface */
+
+    /* Area of surface to upload when/after unlocking */
+    SDL_Rect invalid_area;
+
+    int temporary;              /* Throw away after one use. */
+
+    SDL_Surface *next;          /* The next Surface in our linked list of hardware surfaces ; == NULL if first surface */
+    SDL_Surface *prev;          /* The prev Surface in our linked list of hardware surfaces ; == NULL if last surface */
+} private_hwdata;
+
+/* some function prototypes */
+static void glSDL_Invalidate(SDL_Surface * surface, SDL_Rect * area);
+static void glSDL_SetLogicSize(_THIS, SDL_Surface * surface, int w, int h);
+static private_hwdata *glSDL_UploadSurface(_THIS, SDL_Surface * surface);
+static private_hwdata *glSDL_GetTexInfo(SDL_Surface * surface);
+static void glSDL_init_formats(_THIS);
+static private_hwdata *glSDL_AddTexInfo(_THIS, SDL_Surface * surface);
+static void glSDL_RemoveTexInfo(_THIS, SDL_Surface * surface);
+static void glSDL_UnloadTexture(_THIS, private_hwdata * txi);
+static int glSDL_BlitGL(_THIS, SDL_Surface * src,
+                        SDL_Rect * srcrect, SDL_Rect * dstrect);
+
+/* some variables */
+static GLint maxtexsize = -1;
+static SDL_PixelFormat *RGBfmt = NULL;
+static SDL_PixelFormat *RGBAfmt = NULL;
+static void *mirrorbuf = NULL;
+/* the raw 888 opengl surface, hidden from the application */
+SDL_Surface *OpenGL_Surface;
+
+/* pointer to the beggining of the list used for memory allocation */
+SDL_Surface *first = NULL;
+
+#ifdef DEBUG_GLSDL
+static __inline__ int
+GLERET(const char *txt)
+{
+    fprintf(stderr, "glSDL ERROR: '%s'\n", txt);
+    return -1;
+}
+static __inline__ void
+GLERR(const char *txt)
+{
+    fprintf(stderr, "glSDL ERROR: '%s'\n", txt);
+}
+#else
+#define	GLERET(x)	(-1)
+#define	GLERR(x)
+#endif
+
+static SDL_VideoDevice underlying_device;
+static int old_screen_flags;
+
+/* 
+ * List of video drivers known to support OpenGL 
+ * The purpose of this is to make glSDL "portable" across
+ * all video backends that support OpenGL
+ */
+static VideoBootStrap *opengl_bootstrap =
+#if SDL_VIDEO_DRIVER_QUARTZ
+    &QZ_bootstrap;
+#elif SDL_VIDEO_DRIVER_X11
+    &X11_bootstrap;
+#elif SDL_VIDEO_DRIVER_WINDIB
+    &WINDIB_bootstrap;
+#elif SDL_VIDEO_DRIVER_BWINDOW
+    &BWINDOW_bootstrap;
+#elif SDL_VIDEO_DRIVER_TOOLBOX
+    &TOOLBOX_bootstrap;
+#elif SDL_VIDEO_DRIVER_CYBERGRAPHICS
+    &CGX_bootstrap;
+#elif SDL_VIDEO_DRIVER_PHOTON
+    &ph_bootstrap;
+#elif SDL_VIDEO_DRIVER_DC
+    &DC_bootstrap;
+#else
+    NULL;
+#endif
+
+static int
+glSDL_Available(void)
+{
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "available\n");
+#endif
+    if (opengl_bootstrap == NULL)
+        return 0;
+    return (opengl_bootstrap->available());
+}
+
+static void
+glSDL_DeleteDevice(SDL_VideoDevice * device)
+{
+    SDL_free(device->hidden);
+    SDL_free(device);
+}
+
+/* Create a glSDL device */
+static SDL_VideoDevice *
+glSDL_CreateDevice(int devindex)
+{
+    SDL_VideoDevice *device;
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "entering createdevice\n");
+#endif
+
+    /* Create the device with the underlying driver */
+    device = opengl_bootstrap->create(devindex);
+
+    /* Save the video device contents for future use */
+    SDL_memcpy(&underlying_device, device, sizeof(SDL_VideoDevice));
+
+    /* Hook glSDL on the video device */
+    device->VideoInit = glSDL_VideoInit;
+    device->ListModes = glSDL_ListModes;
+    device->VideoQuit = glSDL_VideoQuit;
+    device->UpdateRects = glSDL_UpdateRects;
+    device->FillHWRect = glSDL_FillHWRect;
+    device->SetHWColorKey = glSDL_SetHWColorKey;
+    device->SetHWAlpha = glSDL_SetHWAlpha;
+    device->AllocHWSurface = glSDL_AllocHWSurface;
+    device->LockHWSurface = glSDL_LockHWSurface;
+    device->UnlockHWSurface = glSDL_UnlockHWSurface;
+    device->FlipHWSurface = glSDL_FlipHWSurface;
+    device->FreeHWSurface = glSDL_FreeHWSurface;
+    device->CheckHWBlit = glSDL_CheckHWBlit;
+    device->SetColors = glSDL_SetColors;
+    device->SetVideoMode = glSDL_SetVideoMode;
+    device->info.hw_available = 1;
+    device->info.blit_hw = 1;
+    device->info.blit_hw_CC = 1;
+    device->info.blit_hw_A = 1;
+    device->info.blit_sw = 1;
+    device->info.blit_sw_CC = 1;
+    device->info.blit_sw_A = 1;
+    device->info.blit_fill = 1;
+
+    /* These functions are not supported by glSDL, so we NULLify them */
+    device->SetGamma = NULL;
+    device->GetGamma = NULL;
+    device->SetGammaRamp = NULL;
+    device->GetGammaRamp = NULL;
+    device->ToggleFullScreen = NULL;
+
+    device->free = glSDL_DeleteDevice;
+
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "leaving createdevice\n");
+#endif
+
+    return device;
+}
+
+/* Our bootstraping structure */
+VideoBootStrap glSDL_bootstrap = {
+    "glSDL", "glSDL - SDL over OpenGL",
+    glSDL_Available, glSDL_CreateDevice
+};
+
+static int
+glSDL_VideoInit(_THIS, SDL_PixelFormat * vformat)
+{
+    int r;
+    printf("glSDL videoinit\n");
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "videoinit\n");
+#endif
+    r = underlying_device.VideoInit(this, vformat);
+    this->info.hw_available = 1;
+    this->info.blit_hw = 1;
+    this->info.blit_hw_CC = 1;
+    this->info.blit_hw_A = 1;
+    this->info.blit_sw = 1;
+    this->info.blit_sw_CC = 1;
+    this->info.blit_sw_A = 1;
+    this->info.blit_fill = 1;
+
+    return r;
+}
+
+SDL_Rect **
+glSDL_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags)
+{
+    return ((SDL_Rect **) - 1);
+}
+
+static void
+glSDL_VideoQuit(_THIS)
+{
+    SDL_Surface *scr;
+
+    /* free all hwdata structures */
+    while (first != NULL)
+        glSDL_RemoveTexInfo(this, first);
+
+    SDL_free(mirrorbuf);
+    mirrorbuf = NULL;
+
+    SDL_FreeFormat(RGBfmt);
+    SDL_FreeFormat(RGBAfmt);
+    RGBfmt = RGBAfmt = NULL;
+
+    SDL_FreeFormat(this->displayformatalphapixel);
+    this->displayformatalphapixel = NULL;
+
+    SDL_FreeSurface(OpenGL_Surface);
+    OpenGL_Surface = NULL;
+
+    /* restore the flags to gracefully exit from fullscreen */
+    this->screen->flags = old_screen_flags;
+
+    /* keep the screen */
+    scr = this->screen;
+
+    /* we cleaned up our stuff, now restore the underlying video driver */
+    SDL_memcpy(this, &underlying_device, sizeof(SDL_VideoDevice));
+
+    this->screen = scr;
+
+    /* call the underlying video driver's VideoQuit function */
+    this->VideoQuit(this);
+}
+
+static SDL_Surface *
+glSDL_SetVideoMode(_THIS, SDL_Surface * current, int width, int height,
+                   int bpp, Uint32 flags)
+{
+    SDL_Surface *hooked_screen;
+    int i;
+    int flag_doublebuf = 0;
+
+    if (opengl_bootstrap == NULL) {
+        GLERR("No bootstrap for glSDL compiled in !\n");
+        return NULL;
+    }
+
+    /* we don't have OpenGL */
+    if ((flags & SDL_INTERNALOPENGL) == SDL_INTERNALOPENGL) {
+        GLERR("OpenGL video modes are not supported by glSDL !\n");
+        return (NULL);
+    }
+
+    /* 
+     * Adjust the flags
+     */
+    flags &= ~SDL_HWPALETTE;
+    flags |= SDL_INTERNALOPENGL;
+
+    /* remember whether the user requested DOUBLEBUF */
+
+    if (flags & SDL_DOUBLEBUF) {
+        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+        flag_doublebuf = 1;
+    } else {
+        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
+        flag_doublebuf = 0;
+    }
+
+    hooked_screen =
+        underlying_device.SetVideoMode(this, current, width, height, 0,
+                                       flags);
+
+    if (!hooked_screen) {
+        GLERR("Unable to open an OpenGL window !\n");
+        return (NULL);
+    }
+
+    /* save the screen flags for restore time */
+    old_screen_flags = hooked_screen->flags;
+
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "got %d bpp\n", bpp);
+#endif
+
+    /* setup the public surface format
+     * glSDL always returns the bpp its asked
+     */
+    switch (bpp) {
+    case 32:
+        this->is_32bit = 1;
+        this->screen = SDL_CreateRGBSurface(flags, width, height, bpp,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                                            0x00FF0000,
+                                            0x0000FF00, 0x000000FF, 0x00000000
+#else
+                                            0x0000FF00,
+                                            0x00FF0000, 0xFF000000, 0x00000000
+#endif
+            );
+        break;
+    case 24:
+        this->is_32bit = 0;
+        this->screen = SDL_CreateRGBSurface(flags, width, height, bpp,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                                            0x00FF0000,
+                                            0x0000FF00, 0x000000FF, 0x00000000
+#else
+                                            0x0000FF00,
+                                            0x00FF0000, 0xFF000000, 0x00000000
+#endif
+            );
+        break;
+    case 16:
+        this->is_32bit = 0;
+        this->screen = SDL_CreateRGBSurface(flags, width, height, bpp,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                                            0x0000F800,
+                                            0x000007E0, 0x0000001F, 0x00000000
+#else
+                                            0x0000001F,
+                                            0x000007E0, 0x0000F800, 0x00000000
+#endif
+            );
+        break;
+    case 15:
+        this->is_32bit = 0;
+        this->screen = SDL_CreateRGBSurface(flags, width, height, bpp,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                                            0x00007C00,
+                                            0x000003E0, 0x0000001F, 0x00000000
+#else
+                                            0x0000001F,
+                                            0x000003E0, 0x00007C00, 0x00000000
+#endif
+            );
+        break;
+    case 8:
+    default:
+        this->is_32bit = 0;
+        this->screen =
+            SDL_CreateRGBSurface(flags, width, height, bpp, 0, 0, 0, 0);
+        /* give it a default palette if 8 bpp
+         * note : SDL already takes care of the palette for 4 bits & 1 bit surfaces 
+         */
+/*			if (bpp==8)
+			{
+				this->screen->format->palette->ncolors=255;
+				SDL_DitherColors(this->screen->format->palette->colors,bpp);
+			}*/
+        break;
+    }
+
+    /* also, we add SDL_HWSURFACE all the time, and let SDL create a shadow surface accordingly */
+    this->screen->flags =
+        hooked_screen->flags | SDL_HWSURFACE | SDL_INTERNALOPENGL;
+    /* add SDL_DOUBLEBUF if it was requested */
+    if (flag_doublebuf)
+        this->screen->flags |= SDL_DOUBLEBUF;
+
+    /* Tell SDL the alpha pixel format we'd like to have */
+    this->displayformatalphapixel = SDL_AllocFormat(32,
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+                                                    0xFF000000,
+                                                    0x00FF0000,
+                                                    0x0000FF00, 0x000000FF
+#else
+                                                    0x000000FF,
+                                                    0x0000FF00,
+                                                    0x00FF0000, 0xFF000000
+#endif
+        );
+
+    /* Now create the raw OpenGL surface */
+    OpenGL_Surface = SDL_CreateRGBSurface(flags, width, height, 24,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                                          0x000000FF,
+                                          0x0000FF00, 0x00FF0000, 0x00000000
+#else
+                                          0xFF000000,
+                                          0x00FF0000, 0x0000FF00, 0x00000000
+#endif
+        );
+
+    /* Here we have to setup OpenGL funcs ourselves */
+#ifndef __QNXNTO__
+#define SDL_PROC(ret,func,params) \
+	do { \
+		this->func = SDL_GL_GetProcAddress(#func); \
+			if ( ! this->func ) { \
+				SDL_SetError("Couldn't load GL function: %s\n", #func); \
+					return(NULL); \
+			} \
+	} while ( 0 );
+#else
+#define SDL_PROC(ret,func,params) this->func=func;
+#endif /* __QNXNTO__ */
+#include "../SDL_glfuncs.h"
+#undef SDL_PROC
+
+    if (this->GL_MakeCurrent(this) < 0)
+        return (NULL);
+#define SDL_PROC(ret,func,params) \
+	do { \
+		this->func = SDL_GL_GetProcAddress(#func); \
+			if ( ! this->func ) { \
+				SDL_SetError("Couldn't load GL function: %s\n", #func); \
+					return(NULL); \
+			} \
+	} while ( 0 );
+#include "../SDL_glfuncs.h"
+#undef SDL_PROC
+
+
+#ifdef	FAKE_MAXTEXSIZE
+    maxtexsize = FAKE_MAXTEXSIZE;
+#else
+    this->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize);
+#endif
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL: Max texture size: %d\n", maxtexsize);
+#endif
+
+    glSDL_init_formats(this);
+
+    if (flag_doublebuf)
+        this->glDrawBuffer(GL_BACK);
+    else
+        this->glDrawBuffer(GL_FRONT);
+
+    this->glDisable(GL_DITHER);
+
+    if (glSDL_AddTexInfo(this, this->screen) < 0) {
+        GLERR("HookDevice() failed to add info to screen surface!");
+        return NULL;
+    }
+
+    glSDL_SetLogicSize(this, this->screen, this->screen->w, this->screen->h);
+
+    glSDL_do_texture(this, 0);
+    glSDL_do_blend(this, 0);
+
+    for (i = 0; i < 1 + flag_doublebuf; ++i) {
+        this->glBegin(GL_TRIANGLE_FAN);
+        this->glColor3ub(0, 0, 0);
+        this->glVertex2i(0, 0);
+        this->glVertex2i(this->screen->w, 0);
+        this->glVertex2i(this->screen->w, this->screen->h);
+        this->glVertex2i(0, this->screen->h);
+        this->glEnd();
+        if (!i)
+            this->GL_SwapBuffers(this);
+    }
+
+    mirrorbuf = SDL_malloc(this->screen->h * this->screen->pitch);
+    if (!mirrorbuf) {
+        GLERR("HookDevice() failed to allocate temp buffer for mirroring!");
+        return NULL;
+    }
+
+    return this->screen;
+}
+
+static int
+glSDL_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color * colors)
+{
+    /* We don't need to fill this one */
+    return 0;
+}
+
+
+#ifdef DEBUG_GLSDL
+static void
+glSDL_print_glerror(_THIS, int point)
+{
+    const char *err = "<unknown>";
+    switch (this->glGetError()) {
+    case GL_NO_ERROR:
+        return;
+    case GL_INVALID_ENUM:
+        err = "GL_INVALID_ENUM";
+        break;
+    case GL_INVALID_VALUE:
+        err = "GL_INVALID_VALUE";
+        break;
+    case GL_INVALID_OPERATION:
+        err = "GL_INVALID_OPERATION";
+        break;
+    case GL_STACK_OVERFLOW:
+        err = "GL_STACK_OVERFLOW";
+        break;
+    case GL_STACK_UNDERFLOW:
+        err = "GL_STACK_UNDERFLOW";
+        break;
+    case GL_OUT_OF_MEMORY:
+        err = "GL_OUT_OF_MEMORY";
+    default:
+        break;
+    }
+    fprintf(stderr, "OpenGL error \"%s\" at point %d.\n", err, point);
+}
+#endif
+
+/* Get texinfo for a surface. */
+static __inline__ private_hwdata *
+glSDL_GetTexInfo(SDL_Surface * surface)
+{
+    if (!surface)
+        return NULL;
+    return surface->hwdata;
+}
+
+
+/* Allocate a "blank" texinfo for a suface. */
+static private_hwdata *
+glSDL_AllocTexInfo(SDL_Surface * surface)
+{
+    private_hwdata *txi;
+    if (!surface)
+        return NULL;
+
+    txi = glSDL_GetTexInfo(surface);
+    if (txi)
+        return txi;             /* There already is one! --> */
+
+    /* ...and hook a new texinfo struct up to it. */
+    txi = (private_hwdata *) SDL_calloc(1, sizeof(private_hwdata));
+    if (!txi) {
+        GLERR("AllocTexInfo(): Failed allocating TexInfo struct!");
+        return NULL;
+    }
+    txi->temporary = 1;
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL: Allocated TexInfo %p.\n", txi);
+#endif
+    return txi;
+}
+
+
+static void
+glSDL_FreeTexInfo(_THIS, private_hwdata * txi)
+{
+    if (!txi)
+        return;
+
+    glSDL_UnloadTexture(this, txi);
+    SDL_free(txi->texture);
+    SDL_free(txi);
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL: Freed TexInfo %p.\n", txi);
+#endif
+}
+
+
+/* Detach and free the texinfo of a surface. */
+static void
+glSDL_RemoveTexInfo(_THIS, SDL_Surface * surface)
+{
+    SDL_Surface *next, *prev;
+    if (!glSDL_GetTexInfo(surface))
+        return;
+
+    /* maintain our doubly linked list */
+    next = surface->hwdata->next;
+    prev = surface->hwdata->prev;
+    if (prev != NULL) {
+        prev->hwdata->next = next;
+    } else {
+        first = next;
+    }
+    if (next != NULL) {
+        next->hwdata->prev = prev;
+    }
+
+    glSDL_FreeTexInfo(this, surface->hwdata);
+    surface->hwdata = NULL;
+}
+
+
+/*
+ * Calculate chopping/tiling of a surface to
+ * fit it into the smallest possible OpenGL
+ * texture.
+ */
+static int
+glSDL_CalcChop(private_hwdata * txi)
+{
+    int rows, vw, vh;
+    int vertical = 0;
+    int texsize;
+    int lastw, lasth, minsize;
+
+    vw = txi->virt.w;
+    vh = txi->virt.h;
+
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "w=%d, h=%d ", vw, vh);
+#endif
+    if (vh > vw) {
+        int t = vw;
+        vw = vh;
+        vh = t;
+        vertical = 1;
+#ifdef DEBUG_GLSDL_CHOP
+        fprintf(stderr, "(vertical) \t");
+#endif
+    }
+
+    /*
+     * Check whether this is a "huge" surface - at least one dimension
+     * must be <= than the maximum texture size, or we'll have to chop
+     * in both directions.
+     */
+#ifdef DEBUG_GLSDL
+    if (maxtexsize < 0)
+        return GLERET("glSDL_CalcChop() called before OpenGL init!");
+#endif
+    if (vh > maxtexsize) {
+        /*
+         * Very simple hack for now; we just tile
+         * both ways with maximum size textures.
+         */
+        texsize = maxtexsize;
+
+        txi->tilemode = GLSDL_TM_HUGE;
+        txi->texsize = texsize;
+        txi->tilew = texsize;
+        txi->tileh = texsize;
+        txi->tilespertex = 1;
+
+        /* Calculate number of textures needed */
+        txi->textures = (vw + texsize - 1) / texsize;
+        txi->textures *= (vh + texsize - 1) / texsize;
+        txi->texture = SDL_malloc(txi->textures * sizeof(int));
+        SDL_memset(txi->texture, -1, txi->textures * sizeof(int));
+#ifdef DEBUG_GLSDL
+        fprintf(stderr, "two-way tiling; textures=%d\n", txi->textures);
+#endif
+        if (!txi->texture) {
+            fprintf(stderr, "glSDL: INTERNAL ERROR: Failed to allocate"
+                    " texture name table!\n");
+            return -3;
+        }
+        return 0;
+    }
+
+    /* Calculate minimum size */
+    rows = 1;
+    lastw = vw;
+    lasth = vh;
+    minsize = lastw > lasth ? lastw : lasth;
+    while (1) {
+        int w, h, size;
+        ++rows;
+        w = vw / rows;
+        h = rows * vh;
+        size = w > h ? w : h;
+        if (size >= minsize) {
+            --rows;
+            break;
+        }
+        lastw = w;
+        lasth = h;
+        minsize = size;
+    }
+    if (minsize > maxtexsize) {
+        /* Handle multiple textures for very wide/tall surfaces. */
+        minsize = maxtexsize;
+        rows = (vw + minsize - 1) / minsize;
+    }
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "==> minsize=%d ", minsize);
+    fprintf(stderr, "(rows=%d) \t", rows);
+#endif
+
+    /* Recalculate with nearest higher power-of-2 width. */
+    for (texsize = 1; texsize < minsize; texsize <<= 1);
+    txi->texsize = texsize;
+    rows = (vw + texsize - 1) / texsize;
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "==> texsize=%d (rows=%d) \t", texsize, rows);
+#endif
+
+    /* Calculate number of tiles per texture */
+    txi->tilespertex = txi->texsize / vh;
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "tilespertex=%d \t", txi->tilespertex);
+#endif
+
+    /* Calculate number of textures needed */
+    txi->textures = (rows + txi->tilespertex - 1) / txi->tilespertex;
+    txi->texture = (GLuint *) SDL_malloc(txi->textures * sizeof(GLuint));
+    SDL_memset(txi->texture, GLSDL_NOTEX, txi->textures * sizeof(GLuint));
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "textures=%d, ", txi->textures);
+#endif
+    if (!txi->texture)
+        return GLERET("Failed to allocate texture name table!");
+
+    /* Set up tile size. (Only one axis supported here!) */
+    if (1 == rows) {
+        txi->tilemode = GLSDL_TM_SINGLE;
+        if (vertical) {
+            txi->tilew = vh;
+            txi->tileh = vw;
+        } else {
+            txi->tilew = vw;
+            txi->tileh = vh;
+        }
+    } else if (vertical) {
+        txi->tilemode = GLSDL_TM_VERTICAL;
+        txi->tilew = vh;
+        txi->tileh = texsize;
+    } else {
+        txi->tilemode = GLSDL_TM_HORIZONTAL;
+        txi->tilew = texsize;
+        txi->tileh = vh;
+    }
+
+#ifdef DEBUG_GLSDL_CHOP
+    fprintf(stderr, "tilew=%d, tileh=%d\n", txi->tilew, txi->tileh);
+#endif
+    return 0;
+}
+
+
+/* Create a temporary TexInfo struct for an SDL_Surface */
+static private_hwdata *
+glSDL_CreateTempTexInfo(_THIS, SDL_Surface * surface)
+{
+    private_hwdata *txi;
+    if (!surface) {
+        GLERR("CreateTempTexInfo(); no surface!");
+        return NULL;
+    }
+    if (IS_GLSDL_SURFACE(surface))
+        return glSDL_GetTexInfo(surface);       /* Do nothing */
+
+    txi = glSDL_AllocTexInfo(surface);
+    if (!txi) {
+        GLERR("CreateTempTexInfo(); Could not alloc TexInfo!");
+        return NULL;
+    }
+    txi->virt.w = txi->lw = surface->w;
+    txi->virt.h = txi->lh = surface->h;
+
+    if (glSDL_CalcChop(txi) < 0) {
+        glSDL_FreeTexInfo(this, txi);
+        GLERR("CreateTempTexInfo(); CalcChop() failed!");
+        return NULL;
+    }
+
+    return txi;
+}
+
+/* Add a glSDL_TexInfo struct to an SDL_Surface */
+static private_hwdata *
+glSDL_AddTexInfo(_THIS, SDL_Surface * surface)
+{
+    private_hwdata *txi = glSDL_CreateTempTexInfo(this, surface);
+    if (!txi)
+        return NULL;
+
+    /* Connect the surface to the new TexInfo. */
+    txi->temporary = 0;
+    surface->hwdata = txi;
+
+    /* add this new surface in front of the list of hw surfaces */
+    txi->next = first;
+    txi->prev = NULL;
+    first = surface;
+    if (txi->next != NULL) {
+        txi->next->hwdata->prev = surface;
+    }
+
+    SDL_SetClipRect(surface, &txi->virt);
+    return txi;
+}
+
+
+/* Create a surface of the prefered OpenGL RGB texture format */
+/*static SDL_Surface *glSDL_CreateRGBSurface(int w, int h)
+{
+	SDL_Surface *s;
+	Uint32 rmask, gmask, bmask;
+	int bits = 24;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+	rmask = 0x000000FF;
+	gmask = 0x0000FF00;
+	bmask = 0x00FF0000;
+#else
+	rmask = 0x00FF0000;
+	gmask = 0x0000FF00;
+	bmask = 0x000000FF;
+#endif
+	s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+			bits, rmask, gmask, bmask, 0);
+	if(s)
+		s->flags |= SDL_HWACCEL;
+
+	return s;
+}
+*/
+
+/* Create a surface of the prefered OpenGL RGBA texture format */
+static SDL_Surface *
+glSDL_CreateRGBASurface(int w, int h)
+{
+    SDL_Surface *s;
+    Uint32 rmask, gmask, bmask, amask;
+    int bits = 32;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+    rmask = 0x000000FF;
+    gmask = 0x0000FF00;
+    bmask = 0x00FF0000;
+    amask = 0xFF000000;
+#else
+    rmask = 0xFF000000;
+    gmask = 0x00FF0000;
+    bmask = 0x0000FF00;
+    amask = 0x000000FF;
+#endif
+    s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+                             bits, rmask, gmask, bmask, amask);
+    if (s)
+        s->flags |= SDL_HWACCEL;
+
+    return s;
+}
+
+
+static void
+glSDL_init_formats(_THIS)
+{
+    RGBfmt = SDL_AllocFormat(24,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                             0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+#else
+                             0x00FF0000, 0x0000FF00, 0x000000FF, 0);
+#endif
+    RGBAfmt = SDL_AllocFormat(32,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                              0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
+#else
+                              0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
+#endif
+}
+
+
+static int
+glSDL_FormatIsOk(SDL_Surface * surface)
+{
+    SDL_PixelFormat *pf;
+    if (!surface)
+        return 1;               /* Well, there ain't much we can do anyway... */
+
+    pf = surface->format;
+
+    /* Colorkeying requires an alpha channel! */
+    if (surface->flags & SDL_SRCCOLORKEY)
+        if (!pf->Amask)
+            return 0;
+
+    /* We need pitch == (width * BytesPerPixel) for glTex[Sub]Image2D() */
+    if (surface->pitch != (surface->w * pf->BytesPerPixel))
+        return 0;
+
+    if (pf->Amask) {
+        if (pf->BytesPerPixel != RGBAfmt->BytesPerPixel)
+            return 0;
+        if (pf->Rmask != RGBAfmt->Rmask)
+            return 0;
+        if (pf->Gmask != RGBAfmt->Gmask)
+            return 0;
+        if (pf->Bmask != RGBAfmt->Bmask)
+            return 0;
+        if (pf->Amask != RGBAfmt->Amask)
+            return 0;
+    } else {
+        if (pf->BytesPerPixel != RGBfmt->BytesPerPixel)
+            return 0;
+        if (pf->Rmask != RGBfmt->Rmask)
+            return 0;
+        if (pf->Gmask != RGBfmt->Gmask)
+            return 0;
+        if (pf->Bmask != RGBfmt->Bmask)
+            return 0;
+    }
+    return 1;
+}
+
+static void
+glSDL_key2alpha(SDL_Surface * surface)
+{
+    int x, y;
+    Uint32 ckey = surface->format->colorkey;
+
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL_key2alpha()\n");
+#endif
+    for (y = 0; y < surface->h; ++y) {
+        Uint32 *px =
+            (Uint32 *) ((char *) surface->pixels + y * surface->pitch);
+        for (x = 0; x < surface->w; ++x)
+            if (px[x] == ckey)
+                px[x] = 0;
+    }
+}
+
+
+
+/*----------------------------------------------------------
+  SDL style API
+  ----------------------------------------------------------*/
+
+static int
+glSDL_FlipHWSurface(_THIS, SDL_Surface * surface)
+{
+#ifdef GLSDL_GRAPHICAL_DEBUG
+    this->glDisable(GL_TEXTURE_2D);
+    this->glBegin(GL_LINE_LOOP);
+    this->glColor4ub(0, 0, 255, 128);
+    this->glVertex2i(0, 0);
+    this->glVertex2i(surface->w, 0);
+    this->glVertex2i(surface->w, surface->h);
+    this->glVertex2i(0, surface->h);
+    this->glEnd();
+    this->glEnable(GL_TEXTURE_2D);
+#endif
+    if (this->screen->flags & SDL_DOUBLEBUF)
+        this->GL_SwapBuffers(this);
+    else
+        this->glFinish();
+    return 0;
+}
+
+
+static void
+glSDL_UpdateRects(_THIS, int numrects, SDL_Rect * rects)
+{
+#ifdef GLSDL_GRAPHICAL_DEBUG
+    int i;
+    this->glDisable(GL_TEXTURE_2D);
+    for (i = 0; i < numrects; i++) {
+        this->glColor4ub(255, 0, 0, 128);
+        this->glBegin(GL_LINE_LOOP);
+        this->glVertex2i(rects[i].x, rects[i].y);
+        this->glVertex2i(rects[i].x + rects[i].w, rects[i].y);
+        this->glVertex2i(rects[i].x + rects[i].w, rects[i].y + rects[i].h);
+        this->glVertex2i(rects[i].x, rects[i].y + rects[i].h);
+        this->glEnd();
+    }
+    this->glEnable(GL_TEXTURE_2D);
+#endif
+    if (this->screen->flags & SDL_DOUBLEBUF)
+        this->GL_SwapBuffers(this);
+    else
+        this->glFinish();
+}
+
+
+static int
+glSDL_AllocHWSurface(_THIS, SDL_Surface * surface)
+{
+    surface->flags |= (SDL_HWSURFACE | SDL_HWACCEL);
+
+    surface->pixels = SDL_malloc(surface->h * surface->pitch);
+    if (surface->pixels == NULL) {
+        SDL_FreeSurface(surface);
+        SDL_OutOfMemory();
+        return (-1);
+    }
+    SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
+    return 0;
+}
+
+
+static void
+glSDL_FreeHWSurface(_THIS, SDL_Surface * surface)
+{
+    if (!surface)
+        return;
+    glSDL_RemoveTexInfo(this, surface);
+}
+
+
+static int
+glSDL_LockHWSurface(_THIS, SDL_Surface * surface)
+{
+    int y;
+
+    if (!surface)
+        return -1;
+
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL: Lock Surface.\n");
+#endif
+
+    if (SDL_VideoSurface == surface) {
+        glSDL_Invalidate(surface, NULL);
+        this->glPixelStorei(GL_UNPACK_ROW_LENGTH,
+                            surface->pitch / surface->format->BytesPerPixel);
+        this->glReadPixels(0, 0, OpenGL_Surface->w, OpenGL_Surface->h,
+                           GL_RGB, GL_UNSIGNED_BYTE, OpenGL_Surface->pixels);
+        for (y = 0; y < OpenGL_Surface->h / 2; ++y) {
+            void *upper = (Uint8 *) OpenGL_Surface->pixels +
+                OpenGL_Surface->pitch * y;
+            void *lower = (Uint8 *) OpenGL_Surface->pixels +
+                OpenGL_Surface->pitch * (OpenGL_Surface->h - y - 1);
+            SDL_memcpy(mirrorbuf, upper, OpenGL_Surface->pitch);
+            SDL_memcpy(upper, lower, OpenGL_Surface->pitch);
+            SDL_memcpy(lower, mirrorbuf, OpenGL_Surface->pitch);
+        }
+        /* the mapping has to be invalidated on 8bpp video surfaces in case of a hw palette change. 
+         * Now if someone could tell me why this is not handled by SDL... */
+        if (SDL_VideoSurface->format->BitsPerPixel == 8)
+            SDL_InvalidateMap(OpenGL_Surface->map);
+
+        /* convert this raw surface to the application-requested format 
+         * FIXME this is sometimes overkill, we could use glPixelStore smartly
+         * But this would be slow anyway :) */
+
+        glSDL_SoftBlit(OpenGL_Surface, NULL, SDL_VideoSurface, NULL);
+    } else
+        glSDL_Invalidate(surface, NULL);
+
+    return 0;
+}
+
+
+static void
+glSDL_UnlockHWSurface(_THIS, SDL_Surface * surface)
+{
+    private_hwdata *txi;
+
+    if (!surface)
+        return;
+
+    /* upload this surface ONLY if this is a glSDL surface
+     * because sometimes (during displayformating for ex.) surfaces are unlocked that aren't glSDL
+     */
+    if (!IS_GLSDL_SURFACE(surface))
+        return;
+
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "glSDL: Unlock Surface.\n");
+#endif
+
+    txi = glSDL_UploadSurface(this, surface);
+
+    if (!txi) {
+        GLERR("glSDL_UnlockHWSurface() failed to upload surface!");
+        return;
+    }
+    if (txi->temporary) {
+        GLERR
+            ("Weirdness... glSDL_UnlockHWSurface() got a temporary TexInfo.");
+        return;
+    }
+    if (surface == SDL_VideoSurface)
+        glSDL_BlitGL(this, SDL_VideoSurface, NULL, NULL);
+}
+
+
+static int
+glSDL_SetHWColorKey(_THIS, SDL_Surface * surface, Uint32 key)
+{
+    /*
+     * If an application does this *after* SDL_DisplayFormat,
+     * we're basically screwed, unless we want to do an
+     * in-place surface conversion hack here.
+     *
+     * What we do is just kill the glSDL texinfo... No big
+     * deal in most cases, as glSDL only converts once anyway,
+     * *unless* you keep modifying the surface.
+     */
+    if (IS_GLSDL_SURFACE(surface))
+        glSDL_RemoveTexInfo(this, surface);
+    return 0;
+}
+
+
+static int
+glSDL_SetHWAlpha(_THIS, SDL_Surface * surface, Uint8 alpha)
+{
+    /*
+     * If an application does this *after* SDL_DisplayFormat,
+     * we're basically screwed, unless we want to do an
+     * in-place surface conversion hack here.
+     *
+     * What we do is just kill the glSDL texinfo... No big
+     * deal in most cases, as glSDL only converts once anyway,
+     * *unless* you keep modifying the surface.
+     */
+    if (IS_GLSDL_SURFACE(surface))
+        glSDL_RemoveTexInfo(this, surface);
+    return 0;
+}
+
+static SDL_bool
+glSDL_SetClipRect(_THIS, SDL_Surface * surface, SDL_Rect * rect)
+{
+    SDL_bool res;
+    if (!surface)
+        return SDL_FALSE;
+
+    res = SDL_SetClipRect(surface, rect);
+    if (!res)
+        return SDL_FALSE;
+
+    rect = &surface->clip_rect;
+
+    if (surface == SDL_VideoSurface) {
+        SDL_Rect r;
+        float xscale, yscale;
+        private_hwdata *txi;
+
+        r.x = rect->x;
+        r.y = rect->y;
+        r.w = rect->w;
+        r.h = rect->h;
+        SDL_SetClipRect(surface, rect);
+
+        txi = glSDL_GetTexInfo(surface);
+        if (!txi)
+            return GLERET("SetClipRect(): Could not get TexInfo!");
+
+        this->glViewport(rect->x,
+                         surface->h - (rect->y + rect->h), rect->w, rect->h);
+        /*
+         * Note that this projection is upside down in
+         * relation to the OpenGL coordinate system.
+         */
+        this->glMatrixMode(GL_PROJECTION);
+        this->glLoadIdentity();
+        xscale = (float) txi->lw / (float) surface->w;
+        yscale = (float) txi->lh / (float) surface->h;
+        this->glOrtho(xscale * (float) rect->x,
+                      xscale * (float) (rect->w + rect->x),
+                      yscale * (float) (rect->h + rect->y),
+                      yscale * (float) rect->y, -1.0, 1.0);
+        return SDL_TRUE;
+    }
+    return res;
+}
+
+static int
+glSDL_BlitFromGL(_THIS, SDL_Rect * srcrect,
+                 SDL_Surface * dst, SDL_Rect * dstrect)
+{
+    SDL_Rect sr, dr;
+
+    /* In case the destination has an OpenGL texture... */
+    glSDL_Invalidate(dst, dstrect);
+
+    /* Abuse the fake screen buffer a little. */
+    this->glPixelStorei(GL_UNPACK_ROW_LENGTH, SDL_VideoSurface->pitch /
+                        SDL_VideoSurface->format->BytesPerPixel);
+    if (srcrect)
+        this->glReadPixels(srcrect->x,
+                           OpenGL_Surface->h - (srcrect->y + srcrect->h - 1),
+                           srcrect->w, srcrect->h, GL_RGB, GL_UNSIGNED_BYTE,
+                           OpenGL_Surface->pixels);
+    else
+        this->glReadPixels(0, 0, OpenGL_Surface->w, OpenGL_Surface->h,
+                           GL_RGB, GL_UNSIGNED_BYTE, OpenGL_Surface->pixels);
+    sr = *srcrect;
+    dr = *dstrect;
+    glSDL_SoftBlit(OpenGL_Surface, &sr, dst, &dr);
+    return 0;
+}
+
+static __inline__ void
+glSDL_BlitGL_single(_THIS, private_hwdata * txi,
+                    float sx1, float sy1, SDL_Rect * dst, unsigned char alpha)
+{
+    float sx2, sy2, texscale;
+    if (!txi->textures)
+        return;
+    if (-1 == txi->texture[0])
+        return;
+    glSDL_texture(this, txi->texture[0]);
+
+    texscale = 1.0 / (float) txi->texsize;
+    sx2 = (sx1 + (float) dst->w) * texscale;
+    sy2 = (sy1 + (float) dst->h) * texscale;
+    sx1 *= texscale;
+    sy1 *= texscale;
+
+#ifdef GLSDL_GRAPHICAL_DEBUG
+    this->glDisable(GL_TEXTURE_2D);
+    this->glBegin(GL_LINE_LOOP);
+    this->glColor4ub(0, 255, 0, 128);
+    this->glVertex2i(dst->x, dst->y);
+    this->glVertex2i(dst->x + dst->w, dst->y);
+    this->glVertex2i(dst->x + dst->w, dst->y + dst->h);
+    this->glVertex2i(dst->x, dst->y + dst->h);
+    this->glEnd();
+    this->glEnable(GL_TEXTURE_2D);
+#endif
+
+    this->glBegin(GL_TRIANGLE_FAN);
+    this->glColor4ub(255, 255, 255, alpha);
+    this->glTexCoord2f(sx1, sy1);
+    this->glVertex2i(dst->x, dst->y);
+    this->glTexCoord2f(sx2, sy1);
+    this->glVertex2i(dst->x + dst->w, dst->y);
+    this->glTexCoord2f(sx2, sy2);
+    this->glVertex2i(dst->x + dst->w, dst->y + dst->h);
+    this->glTexCoord2f(sx1, sy2);
+    this->glVertex2i(dst->x, dst->y + dst->h);
+    this->glEnd();
+}
+
+
+static void
+glSDL_BlitGL_htile(_THIS, private_hwdata * txi,
+                   float sx1, float sy1, SDL_Rect * dst, unsigned char alpha)
+{
+    int tex;
+    float tile, sx2, sy2, yo;
+    float texscale = 1.0 / (float) txi->texsize;
+    float tileh = (float) txi->tileh * texscale;
+    sx2 = (sx1 + (float) dst->w) * texscale;
+    sy2 = (sy1 + (float) dst->h) * texscale;
+    sx1 *= texscale;
+    sy1 *= texscale;
+    tile = floor(sx1);
+    tex = (int) tile / txi->tilespertex;
+    yo = ((int) tile % txi->tilespertex) * tileh;
+
+    if (tex >= txi->textures)
+        return;
+    if (-1 == txi->texture[tex])
+        return;
+    glSDL_texture(this, txi->texture[tex]);
+
+    while (tile < sx2) {
+        int tdx1 = dst->x;
+        int tdx2 = dst->x + dst->w;
+        float tsx1 = sx1 - tile;
+        float tsx2 = sx2 - tile;
+
+        /* Clip to current tile */
+        if (tsx1 < 0.0) {
+            tdx1 -= tsx1 * txi->texsize;
+            tsx1 = 0.0;
+        }
+        if (tsx2 > 1.0) {
+            tdx2 -= (tsx2 - 1.0) * txi->texsize;
+            tsx2 = 1.0;
+        }
+
+        /* Maybe select next texture? */
+        if (yo + tileh > 1.0) {
+            ++tex;
+            if (tex >= txi->textures)
+                return;
+            if (-1 == txi->texture[tex])
+                return;
+            glSDL_texture(this, txi->texture[tex]);
+            yo = 0.0;
+        }
+#ifdef GLSDL_GRAPHICAL_DEBUG
+        this->glDisable(GL_TEXTURE_2D);
+        this->glBegin(GL_LINE_LOOP);
+        this->glColor4ub(0, 255, 0, 128);
+        this->glVertex2i(tdx1, dst->y);
+        this->glVertex2i(tdx2, dst->y);
+        this->glVertex2i(tdx2, dst->y + dst->h);
+        this->glVertex2i(tdx1, dst->y + dst->h);
+        this->glEnd();
+        this->glEnable(GL_TEXTURE_2D);
+#endif
+
+        this->glBegin(GL_TRIANGLE_FAN);
+        this->glColor4ub(255, 255, 255, alpha);
+        this->glTexCoord2f(tsx1, yo + sy1);
+        this->glVertex2i(tdx1, dst->y);
+        this->glTexCoord2f(tsx2, yo + sy1);
+        this->glVertex2i(tdx2, dst->y);
+        this->glTexCoord2f(tsx2, yo + sy2);
+        this->glVertex2i(tdx2, dst->y + dst->h);
+        this->glTexCoord2f(tsx1, yo + sy2);
+        this->glVertex2i(tdx1, dst->y + dst->h);
+        this->glEnd();
+        tile += 1.0;
+        yo += tileh;
+    }
+}
+
+
+static void
+glSDL_BlitGL_vtile(_THIS, private_hwdata * txi,
+                   float sx1, float sy1, SDL_Rect * dst, unsigned char alpha)
+{
+    int tex;
+    float tile, sx2, sy2, xo;
+    float texscale = 1.0 / (float) txi->texsize;
+    float tilew = (float) txi->tilew * texscale;
+    sx2 = (sx1 + (float) dst->w) * texscale;
+    sy2 = (sy1 + (float) dst->h) * texscale;
+    sx1 *= texscale;
+    sy1 *= texscale;
+    tile = floor(sy1);
+    tex = (int) tile / txi->tilespertex;
+    xo = ((int) tile % txi->tilespertex) * tilew;
+
+    if (tex >= txi->textures)
+        return;
+    if (-1 == txi->texture[tex])
+        return;
+    glSDL_texture(this, txi->texture[tex]);
+
+    while (tile < sy2) {
+        int tdy1 = dst->y;
+        int tdy2 = dst->y + dst->h;
+        float tsy1 = sy1 - tile;
+        float tsy2 = sy2 - tile;
+
+        /* Clip to current tile */
+        if (tsy1 < 0.0) {
+            tdy1 -= tsy1 * txi->texsize;
+            tsy1 = 0.0;
+        }
+        if (tsy2 > 1.0) {
+            tdy2 -= (tsy2 - 1.0) * txi->texsize;
+            tsy2 = 1.0;
+        }
+
+        /* Maybe select next texture? */
+        if (xo + tilew > 1.0) {
+            ++tex;
+            if (tex >= txi->textures)
+                return;
+            if (-1 == txi->texture[tex])
+                return;
+            glSDL_texture(this, txi->texture[tex]);
+            xo = 0.0;
+        }
+#ifdef GLSDL_GRAPHICAL_DEBUG
+        this->glDisable(GL_TEXTURE_2D);
+        this->glBegin(GL_LINE_LOOP);
+        this->glColor4ub(0, 255, 0, 128);
+        this->glVertex2i(dst->x, tdy1);
+        this->glVertex2i(dst->x + dst->w, tdy1);
+        this->glVertex2i(dst->x + dst->w, tdy2);
+        this->glVertex2i(dst->x, tdy2);
+        this->glEnd();
+        this->glEnable(GL_TEXTURE_2D);
+#endif
+
+        this->glBegin(GL_TRIANGLE_FAN);
+        this->glColor4ub(255, 255, 255, alpha);
+        this->glTexCoord2f(xo + sx1, tsy1);
+        this->glVertex2i(dst->x, tdy1);
+        this->glTexCoord2f(xo + sx2, tsy1);
+        this->glVertex2i(dst->x + dst->w, tdy1);
+        this->glTexCoord2f(xo + sx2, tsy2);
+        this->glVertex2i(dst->x + dst->w, tdy2);
+        this->glTexCoord2f(xo + sx1, tsy2);
+        this->glVertex2i(dst->x, tdy2);
+        this->glEnd();
+
+        tile += 1.0;
+        xo += tilew;
+    }
+}
+
+
+static void
+glSDL_BlitGL_hvtile(_THIS, SDL_Surface * src, private_hwdata * txi,
+                    float sx1, float sy1, SDL_Rect * dst, unsigned char alpha)
+{
+    int x, y, last_tex, tex;
+    float sx2, sy2;
+    float texscale = 1.0 / (float) txi->texsize;
+    int tilesperrow = (src->w + txi->tilew - 1) / txi->tilew;
+    sx2 = (sx1 + (float) dst->w) * texscale;
+    sy2 = (sy1 + (float) dst->h) * texscale;
+    sx1 *= texscale;
+    sy1 *= texscale;
+
+    last_tex = tex = floor(sy1) * tilesperrow + floor(sx1);
+    if (tex >= txi->textures)
+        return;
+    if (-1 == txi->texture[tex])
+        return;
+    glSDL_texture(this, txi->texture[tex]);
+
+    for (y = floor(sy1); y < sy2; ++y) {
+        int tdy1 = dst->y;
+        int tdy2 = dst->y + dst->h;
+        float tsy1 = sy1 - y;
+        float tsy2 = sy2 - y;
+
+        /* Clip to current tile */
+        if (tsy1 < 0.0) {
+            tdy1 -= tsy1 * txi->texsize;
+            tsy1 = 0.0;
+        }
+        if (tsy2 > 1.0) {
+            tdy2 -= (tsy2 - 1.0) * txi->texsize;
+            tsy2 = 1.0;
+        }
+        for (x = floor(sx1); x < sx2; ++x) {
+            int tdx1 = dst->x;
+            int tdx2 = dst->x + dst->w;
+            float tsx1 = sx1 - x;
+            float tsx2 = sx2 - x;
+
+            /* Clip to current tile */
+            if (tsx1 < 0.0) {
+                tdx1 -= tsx1 * txi->texsize;
+                tsx1 = 0.0;
+            }
+            if (tsx2 > 1.0) {
+                tdx2 -= (tsx2 - 1.0) * txi->texsize;
+                tsx2 = 1.0;
+            }
+
+            /* Select texture */
+            tex = y * tilesperrow + x;
+            if (tex != last_tex) {
+                if (tex >= txi->textures)
+                    return;
+                if (-1 == txi->texture[tex])
+                    return;
+                glSDL_texture(this, txi->texture[tex]);
+                last_tex = tex;
+            }
+#ifdef GLSDL_GRAPHICAL_DEBUG
+            this->glDisable(GL_TEXTURE_2D);
+            this->glBegin(GL_LINE_LOOP);
+            this->glColor4ub(0, 255, 0, 128);
+            this->glVertex2i(tdx1, tdy1);
+            this->glVertex2i(tdx2, tdy1);
+            this->glVertex2i(tdx2, tdy2);
+            this->glVertex2i(tdx1, tdy2);
+            this->glEnd();
+            this->glEnable(GL_TEXTURE_2D);
+#endif
+
+            this->glBegin(GL_TRIANGLE_FAN);
+            this->glColor4ub(255, 255, 255, alpha);
+            this->glTexCoord2f(tsx1, tsy1);
+            this->glVertex2i(tdx1, tdy1);
+            this->glTexCoord2f(tsx2, tsy1);
+            this->glVertex2i(tdx2, tdy1);
+            this->glTexCoord2f(tsx2, tsy2);
+            this->glVertex2i(tdx2, tdy2);
+            this->glTexCoord2f(tsx1, tsy2);
+            this->glVertex2i(tdx1, tdy2);
+            this->glEnd();
+        }
+    }
+}
+
+/*
+ * Calculate the actual blit rectangle and source offset
+ * for a blit from a rectangle in a surface with specified
+ * size to a surface with a cliprect.
+ *
+ * In:	rect	source rectangle
+ *	w, h	source surface size
+ *	(x, y)	destination coordinate
+ *	clip	destination clip rectangle
+ *
+ * Out:	(x, y)	source top-left offset
+ *	rect	destination rectangle
+ *
+ * Returns 1 if the result is visible, otherwise 0.
+ */
+static __inline__ int
+blitclip(SDL_Rect * rect, int w, int h, int *x, int *y, SDL_Rect * clip)
+{
+    int sx1, sy1, sx2, sy2;
+    int dx1, dy1, dx2, dy2;
+
+    /* Get source and destination coordinates */
+    sx1 = rect->x;
+    sy1 = rect->y;
+    sx2 = sx1 + rect->w;
+    sy2 = sy1 + rect->h;
+    dx1 = *x;
+    dy1 = *y;
+
+    /* Keep source rect inside source surface */
+    if (sx1 < 0) {
+        dx1 -= sx1;
+        sx1 = 0;
+    }
+    if (sy1 < 0) {
+        dy1 -= sy1;
+        sy1 = 0;
+    }
+    if (sx2 > w)
+        sx2 = w;
+    if (sy2 > h)
+        sy2 = h;
+
+    /* Cull blits from void space */
+    if (sx1 >= sx2 || sy1 >= sy2)
+        return 0;
+
+    /* Calculate destination lower-right */
+    dx2 = dx1 + (sx2 - sx1);
+    dy2 = dy1 + (sy2 - sy1);
+
+    /* Clip to destination cliprect */
+    if (dx1 < clip->x) {
+        sx1 += clip->x - dx1;
+        dx1 = clip->x;
+    }
+    if (dy1 < clip->y) {
+        sy1 += clip->y - dy1;
+        dy1 = clip->y;
+    }
+    if (dx2 > clip->x + clip->w)
+        dx2 = clip->x + clip->w;
+    if (dy2 > clip->y + clip->h)
+        dy2 = clip->y + clip->h;
+
+    /* Cull nop/off-screen blits */
+    if (dx1 >= dx2 || dy1 >= dy2)
+        return 0;
+
+    *x = sx1;
+    *y = sy1;
+    rect->x = dx1;
+    rect->y = dy1;
+    rect->w = dx2 - dx1;
+    rect->h = dy2 - dy1;
+    return 1;
+}
+
+static int
+glSDL_BlitGL(_THIS, SDL_Surface * src, SDL_Rect * srcrect, SDL_Rect * dstrect)
+{
+    private_hwdata *txi;
+    float x1, y1;
+    unsigned char alpha;
+    SDL_Rect d;
+    int x, y;
+    SDL_Rect r;
+
+    if (!src)
+        return GLERET("BlitGL(): No src surface!");
+
+    /* Get source and destination coordinates */
+    if (srcrect)
+        r = *srcrect;
+    else {
+        r.x = r.y = 0;
+        r.w = src->w;
+        r.h = src->h;
+    }
+    if (dstrect) {
+        x = dstrect->x;
+        y = dstrect->y;
+    } else
+        x = y = 0;
+
+    /* Clip! */
+    if (!blitclip(&r, src->w, src->h, &x, &y, &this->screen->clip_rect)) {
+        if (dstrect)
+            dstrect->w = dstrect->h = 0;
+        return 0;
+    }
+
+    /* Write back the resulting cliprect */
+    if (dstrect)
+        *dstrect = r;
+
+    /* Make sure we have a source with a valid texture */
+    txi = glSDL_UploadSurface(this, src);
+    if (!txi)
+        return GLERET("BlitGL(): Could not get a TexInfo!");
+
+    /* Set up blending */
+    if (src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY)) {
+        glSDL_blendfunc(this, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glSDL_do_blend(this, 1);
+    } else
+        glSDL_do_blend(this, 0);
+
+    /* Enable texturing */
+    glSDL_do_texture(this, 1);
+
+    /* Calculate texcoords */
+    if (!srcrect)
+        srcrect = &txi->virt;
+    x1 = (float) srcrect->x;
+    y1 = (float) srcrect->y;
+
+    /* Calculate screen coords. */
+    if (dstrect) {
+        d.x = dstrect->x;
+        d.y = dstrect->y;
+        d.w = (int) (srcrect->w * (float) txi->lw / (float) txi->virt.w);
+        d.h = (int) (srcrect->h * (float) txi->lh / (float) txi->virt.h);
+    } else {
+        d.x = 0;
+        d.y = 0;
+        d.w = (int) (srcrect->w * (float) txi->lw / (float) txi->virt.w);
+        d.h = (int) (srcrect->h * (float) txi->lh / (float) txi->virt.h);
+    }
+
+    /*
+     * Note that we actually *prevent* the use of "full surface alpha"
+     * and alpha channel in combination - to stay SDL 2D compatible.
+     */
+    if ((src->flags & SDL_SRCALPHA) && (src->format->Amask))
+        alpha = 255;
+    else
+        alpha = src->format->alpha;
+
+    /* Render! */
+    switch (txi->tilemode) {
+    case GLSDL_TM_SINGLE:
+        glSDL_BlitGL_single(this, txi, x1, y1, &d, alpha);
+        break;
+    case GLSDL_TM_HORIZONTAL:
+        glSDL_BlitGL_htile(this, txi, x1, y1, &d, alpha);
+        break;
+    case GLSDL_TM_VERTICAL:
+        glSDL_BlitGL_vtile(this, txi, x1, y1, &d, alpha);
+        break;
+    case GLSDL_TM_HUGE:
+        glSDL_BlitGL_hvtile(this, src, txi, x1, y1, &d, alpha);
+        break;
+    }
+
+    if (txi->temporary)
+        glSDL_FreeTexInfo(this, txi);
+
+    return 0;
+}
+
+
+static int
+glSDL_HWAccelBlit(SDL_Surface * src, SDL_Rect * srcrect,
+                  SDL_Surface * dst, SDL_Rect * dstrect)
+{
+    SDL_Surface *vs;
+
+    if (!src)
+        return GLERET("HWAccelBlit(): No src surface!");
+    if (!dst)
+        return GLERET("HWAccelBlit(): No dst surface!");
+
+    /*
+     * Figure out what to do:
+     *      screen->screen:         glSDL_BlitFromGL() + glSDL_BlitGL()
+     *      surface->screen:        glSDL_BlitGL()
+     *      screen->surface:        glSDL_BlitFromGL()
+     *      surface->surface:       glSDL_SoftBlit()
+     */
+    vs = SDL_VideoSurface;
+    if (src == vs) {
+        if (dst == vs) {
+            /*
+               FIXME: Try glCopyPixels() instead...
+             */
+            glSDL_BlitFromGL(current_video, srcrect, vs, dstrect);
+            return glSDL_BlitGL(current_video, vs, srcrect, dstrect);
+        } else {
+            return glSDL_BlitFromGL(current_video, srcrect, dst, dstrect);
+        }
+    } else {
+        if (dst == vs) {
+            return glSDL_BlitGL(current_video, src, srcrect, dstrect);
+        } else {
+            glSDL_Invalidate(dst, dstrect);
+            glSDL_SoftBlit(src, srcrect, dst, dstrect);
+            return 0;
+        }
+    }
+}
+
+
+static int
+glSDL_FillHWRect(_THIS, SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color)
+{
+    SDL_Surface *vs = SDL_VideoSurface;
+    int dx1, dy1, dx2, dy2;
+    Uint32 r, g, b;
+    Uint8 br, bg, bb;
+
+    /*
+     * Some ugly reverse conversion for compatibility...
+     * (We must do this before losing the dst pointer,
+     * as the pixel formats of the screen and
+     * SDL_VideoSurface may differ!)
+     */
+
+    if (dst->format->palette) {
+        /* this a paletted color */
+        SDL_GetRGB(color, dst->format, &br, &bg, &bb);
+    } else {
+        /* this a RGB color */
+        r = color & dst->format->Rmask;
+        r = r >> dst->format->Rshift;
+        r = r << dst->format->Rloss;
+        br = r;
+
+        g = color & dst->format->Gmask;
+        g = g >> dst->format->Gshift;
+        g = g << dst->format->Gloss;
+        bg = g;
+
+        b = color & dst->format->Bmask;
+        b = b >> dst->format->Bshift;
+        b = b << dst->format->Bloss;
+        bb = b;
+    }
+
+    if (vs != dst) {
+        /* draw a rect offscreen */
+        glSDL_Invalidate(dst, dstrect);
+        /* software-fill the surface by faking it as a SW_SURFACE */
+        dst->flags &= ~SDL_HWSURFACE;
+        SDL_FillRect(dst, dstrect, color);
+        dst->flags |= SDL_HWSURFACE;
+    } else {
+        /* draw a rect onscreen */
+        glSDL_do_texture(this, 0);
+        glSDL_do_blend(this, 0);
+
+        dx1 = dstrect->x;
+        dy1 = dstrect->y;
+        dx2 = dx1 + dstrect->w;
+        dy2 = dy1 + dstrect->h;
+
+        this->glBegin(GL_TRIANGLE_FAN);
+        this->glColor3ub(br, bg, bb);
+        this->glVertex2i(dx1, dy1);
+        this->glVertex2i(dx2, dy1);
+        this->glVertex2i(dx2, dy2);
+        this->glVertex2i(dx1, dy2);
+        this->glEnd();
+    }
+    return 0;
+}
+
+static int
+glSDL_CheckHWBlit(_THIS, SDL_Surface * src, SDL_Surface * dst)
+{
+    src->flags |= SDL_HWACCEL;
+    src->map->hw_blit = glSDL_HWAccelBlit;
+    return 1;
+}
+
+
+static SDL_Surface *
+glSDL_DisplayFormat(SDL_Surface * surface)
+{
+    SDL_Surface *tmp;
+    int use_rgba = (surface->flags & SDL_SRCCOLORKEY) ||
+        ((surface->flags & SDL_SRCALPHA) && surface->format->Amask);
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "#### glSDL_DisplayFormat()\n");
+#endif
+    if (use_rgba)
+        tmp = glSDL_ConvertSurface(surface, RGBAfmt, SDL_SWSURFACE);
+    else
+        tmp = glSDL_ConvertSurface(surface, RGBfmt, SDL_SWSURFACE);
+    if (!tmp) {
+        GLERR("glSDL_DisplayFormat() could not convert surface!");
+        return NULL;
+    }
+    SDL_SetAlpha(tmp, 0, 0);
+
+    if (surface->flags & SDL_SRCCOLORKEY) {
+        /*
+         * We drop colorkey data here, but we have to,
+         * or we'll run into trouble when converting,
+         * in particular from indexed color formats.
+         */
+        SDL_SetColorKey(tmp, SDL_SRCCOLORKEY, surface->format->colorkey);
+        glSDL_key2alpha(tmp);
+        SDL_SetColorKey(tmp, 0, 0);
+    }
+
+    return tmp;
+}
+
+
+static SDL_Surface *
+glSDL_DisplayFormatAlpha(SDL_Surface * surface)
+{
+    SDL_Surface *s, *tmp;
+    tmp = glSDL_ConvertSurface(surface, RGBAfmt, SDL_SWSURFACE);
+#ifdef DEBUG_GLSDL
+    fprintf(stderr, "#### glSDL_DisplayFormatAlpha()\n");
+#endif
+    if (!tmp)
+        return NULL;
+
+    SDL_SetAlpha(tmp, 0, 0);
+    SDL_SetColorKey(tmp, 0, 0);
+    s = glSDL_CreateRGBASurface(surface->w, surface->h);
+    if (!s) {
+        SDL_FreeSurface(tmp);
+        return NULL;
+    }
+    glSDL_SoftBlit(tmp, NULL, s, NULL);
+    SDL_FreeSurface(tmp);
+
+    if (surface->flags & SDL_SRCCOLORKEY) {
+        SDL_SetColorKey(s, SDL_SRCCOLORKEY, surface->format->colorkey);
+        glSDL_key2alpha(s);
+        SDL_SetColorKey(s, 0, 0);
+    }
+
+    if (surface->flags & SDL_SRCALPHA)
+        SDL_SetAlpha(s, SDL_SRCALPHA, surface->format->alpha);
+    return s;
+}
+
+
+/*----------------------------------------------------------
+  glSDL specific API extensions
+  ----------------------------------------------------------*/
+
+static void
+glSDL_Invalidate(SDL_Surface * surface, SDL_Rect * area)
+{
+    private_hwdata *txi;
+    if (!surface)
+        return;
+    txi = glSDL_GetTexInfo(surface);
+    if (!txi)
+        return;
+    if (!area) {
+        txi->invalid_area.x = 0;
+        txi->invalid_area.y = 0;
+        txi->invalid_area.w = surface->w;
+        txi->invalid_area.h = surface->h;
+        return;
+    }
+    txi->invalid_area = *area;
+}
+
+
+static void
+glSDL_SetLogicSize(_THIS, SDL_Surface * surface, int w, int h)
+{
+    SDL_Rect r;
+    private_hwdata *txi;
+    if (!IS_GLSDL_SURFACE(surface))
+        return;
+
+    txi = glSDL_GetTexInfo(surface);
+
+    txi->lw = w;
+    txi->lh = h;
+
+    if (SDL_VideoSurface != surface)
+        return;
+
+    r.x = r.y = 0;
+    r.w = w;
+    r.h = h;
+    glSDL_SetClipRect(this, surface, &r);
+
+    this->glMatrixMode(GL_MODELVIEW);
+    this->glLoadIdentity();
+    this->glTranslated(0.0f, 0.0f, 0.0f);
+
+    this->glDisable(GL_DEPTH_TEST);
+    this->glDisable(GL_CULL_FACE);
+
+    glSDL_reset();
+}
+
+static int
+glSDL_InitTexture(_THIS, SDL_Surface * datasurf, private_hwdata * txi,
+                  int tex)
+{
+    this->glGenTextures(1, (GLuint *) & txi->texture[tex]);
+    this->glBindTexture(GL_TEXTURE_2D, txi->texture[tex]);
+    glstate.texture = txi->texture[tex];
+    this->glPixelStorei(GL_UNPACK_ROW_LENGTH, datasurf->pitch /
+                        datasurf->format->BytesPerPixel);
+    this->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    this->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    this->glTexImage2D(GL_TEXTURE_2D, 0,
+                       datasurf->format->Amask ? GL_RGBA8 : GL_RGB8,
+                       txi->texsize, txi->texsize, 0,
+                       datasurf->format->Amask ? GL_RGBA : GL_RGB,
+                       GL_UNSIGNED_BYTE, NULL);
+#ifdef DEBUG_GLSDL
+    glSDL_print_glerror(this, 1);
+#endif
+    return 0;
+}
+
+
+/* Image tiled horizontally (wide surface), or not at all */
+static int
+glSDL_UploadHoriz(_THIS, SDL_Surface * datasurf, private_hwdata * txi)
+{
+    int bpp = datasurf->format->BytesPerPixel;
+    int res;
+    int tex = 0;
+    int fromx = 0;
+    int toy = txi->texsize;     /* To init first texture */
+    while (1) {
+        int thistw = datasurf->w - fromx;
+        if (thistw > txi->tilew)
+            thistw = txi->tilew;
+        else if (thistw <= 0)
+            break;
+        if (toy + txi->tileh > txi->texsize) {
+            toy = 0;
+            res = glSDL_InitTexture(this, datasurf, txi, tex);
+            if (res < 0)
+                return res;
+            ++tex;
+        }
+        this->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, toy,
+                              thistw, txi->tileh,
+                              datasurf->format->Amask ? GL_RGBA : GL_RGB,
+                              GL_UNSIGNED_BYTE,
+                              (char *) datasurf->pixels + bpp * fromx);
+#ifdef DEBUG_GLSDL
+        glSDL_print_glerror(this, 2);
+#endif
+        fromx += txi->tilew;
+        toy += txi->tileh;
+    }
+    return 0;
+}
+
+
+/* Image tiled vertically (tall surface) */
+static int
+glSDL_UploadVert(_THIS, SDL_Surface * datasurf, private_hwdata * txi)
+{
+    int res;
+    int tex = 0;
+    int fromy = 0;
+    int tox = txi->texsize;     /* To init first texture */
+    while (1) {
+        int thisth = datasurf->h - fromy;
+        if (thisth > txi->tileh)
+            thisth = txi->tileh;
+        else if (thisth <= 0)
+            break;
+        if (tox + txi->tilew > txi->texsize) {
+            tox = 0;
+            res = glSDL_InitTexture(this, datasurf, txi, tex);
+            if (res < 0)
+                return res;
+            ++tex;
+        }
+        this->glTexSubImage2D(GL_TEXTURE_2D, 0, tox, 0,
+                              txi->tilew, thisth,
+                              datasurf->format->Amask ? GL_RGBA : GL_RGB,
+                              GL_UNSIGNED_BYTE,
+                              (char *) datasurf->pixels +
+                              datasurf->pitch * fromy);
+#ifdef DEBUG_GLSDL
+        glSDL_print_glerror(this, 3);
+#endif
+        fromy += txi->tileh;
+        tox += txi->tilew;
+    }
+    return 0;
+}
+
+
+/* Image tiled two-way (huge surface) */
+static int
+glSDL_UploadHuge(_THIS, SDL_Surface * datasurf, private_hwdata * txi)
+{
+    int bpp = datasurf->format->BytesPerPixel;
+    int res;
+    int tex = 0;
+    int y = 0;
+    while (y < datasurf->h) {
+        int x;
+        int thisth = datasurf->h - y;
+        if (thisth > txi->tileh)
+            thisth = txi->tileh;
+        x = 0;
+        while (x < datasurf->w) {
+            int thistw = datasurf->w - x;
+            if (thistw > txi->tilew)
+                thistw = txi->tilew;
+            res = glSDL_InitTexture(this, datasurf, txi, tex++);
+            if (res < 0)
+                return res;
+            this->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+                                  thistw, thisth,
+                                  datasurf->format->
+                                  Amask ? GL_RGBA : GL_RGB,
+                                  GL_UNSIGNED_BYTE,
+                                  (char *) datasurf->pixels +
+                                  datasurf->pitch * y + bpp * x);
+#ifdef DEBUG_GLSDL
+            fprintf(stderr,
+                    "glTexSubImage(x = %d, y = %d, w = %d, h = %d)\n", x,
+                    y, thistw, thisth);
+            glSDL_print_glerror(this, 4);
+#endif
+            x += txi->tilew;
+        }
+        y += txi->tileh;
+    }
+    return 0;
+}
+
+
+/* Upload all textures for a surface. */
+static int
+glSDL_UploadTextures(_THIS, SDL_Surface * datasurf, private_hwdata * txi)
+{
+    switch (txi->tilemode) {
+    case GLSDL_TM_SINGLE:
+    case GLSDL_TM_HORIZONTAL:
+        glSDL_UploadHoriz(this, datasurf, txi);
+        break;
+    case GLSDL_TM_VERTICAL:
+        glSDL_UploadVert(this, datasurf, txi);
+        break;
+    case GLSDL_TM_HUGE:
+        glSDL_UploadHuge(this, datasurf, txi);
+        break;
+    }
+    return 0;
+}
+
+
+/*
+ * IMPORTANT:
+ *	This function will try various ways of giving you
+ *	a TexInfo, and will succeed most of the time.
+ *
+ *	However, the TexInfo returned may be temporary,
+ *	(as opposed to connected to 'surface'). A temporary
+ *	TexInfo must be used only once and then thrown away,
+ *	since it means that glSDL cannot track changes in
+ *	the pixel data of 'texture'.
+ */
+static private_hwdata *
+glSDL_UploadSurface(_THIS, SDL_Surface * surface)
+{
+    int i;
+    int converted = 0;
+    private_hwdata *txi = glSDL_GetTexInfo(surface);
+
+    if (IS_GLSDL_SURFACE(surface)) {
+        /*
+         * Ok, this is a glSDL surface, and it *might* be
+         * in texture memory already. If so, it may need
+         * an update.
+         */
+        if (txi->invalid_area.w) {
+            glSDL_UnloadTexture(this, txi);
+        } else {
+            int missing = 0;
+            if (txi->textures) {
+                for (i = 0; i < txi->textures; ++i)
+                    if (GLSDL_NOTEX == txi->texture[i]) {
+                        missing = 1;
+                        break;
+                    }
+                if (!missing)
+                    return txi; /* They're already there! */
+            }
+        }
+    } else {
+        /*
+         * Nope, this isn't (yet) a glSDL surface. Let's
+         * try to either make it one, or set up a temporary
+         * TexInfo for it, valid for only one blit.
+         */
+        if ((surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
+            txi = glSDL_AddTexInfo(this, surface);
+            if (!txi) {
+                GLERR("UploadSurface(): Could not add TexInfo!");
+                return NULL;
+            }
+            surface->flags |= SDL_HWSURFACE;
+            surface->flags |= SDL_HWACCEL;
+        } else {
+            /* 
+             * FIXME
+             * here if the surface is small enough, it's a good 
+             * candidate for a blit using glDrawPixels instead 
+             * of a texture blit
+             */
+            txi = glSDL_CreateTempTexInfo(this, surface);
+            if (!txi) {
+                GLERR("UploadSurface(): Could not create temp TexInfo!");
+                return NULL;
+            }
+        }
+    }
+
+    if (txi->texsize > maxtexsize) {
+        /* This surface wasn't tiled properly... */
+        if (txi->temporary)
+            glSDL_FreeTexInfo(this, txi);
+        GLERR("UploadSurface(): Too large texture!");
+        return NULL;
+    }
+
+    /*
+     * Kludge: Convert if not of preferred RGB or RGBA format.
+     *
+     *      Conversion should only be done when *really* needed.
+     *      That is, it should rarely have to be done with OpenGL
+     *      1.2+.
+     *
+     *      Besides, any surface that's been SDL_DisplayFormat()ed
+     *      should already be in the best known OpenGL format -
+     *      preferably one that makes DMA w/o conversion possible.
+     */
+    if (!glSDL_FormatIsOk(surface)) {
+#ifdef DEBUG_GLSDL
+        fprintf(stderr, "glSDL: WARNING: On-the-fly conversion performed!\n");
+#endif
+        converted = 1;
+        /* NOTE: We forget about the original surface here. */
+        if (surface->format->Amask)
+            surface = glSDL_DisplayFormatAlpha(surface);
+        else
+            surface = glSDL_DisplayFormat(surface);
+        if (!surface) {
+            GLERR("UploadSurface(): Could not convert surface!");
+            if (txi->temporary)
+                glSDL_FreeTexInfo(this, txi);
+            return NULL;
+        }
+    }
+
+    glSDL_UploadTextures(this, surface, txi);
+
+    if (converted)
+        SDL_FreeSurface(surface);
+
+    return txi;
+}
+
+
+static void
+glSDL_UnloadTexture(_THIS, private_hwdata * txi)
+{
+    int i;
+    for (i = 0; i < txi->textures; ++i)
+        if (txi->texture[i] != GLSDL_NOTEX)
+            this->glDeleteTextures(1, &txi->texture[i]);
+    SDL_memset(&txi->invalid_area, 0, sizeof(txi->invalid_area));
+}
+
+/* vi: set ts=4 sw=4 expandtab: */