/*
 * Direct3D GLX interface
 *
 * Copyright 2001,2003 TransGaming Technologies, Inc.
 */

#include "config.h"
#include "wine/port.h"

#include <unistd.h>
#include <signal.h>

#include "ts_xlib.h"
#include "x11drv.h"
#include "x11font.h"
#include "x11ddraw.h"
#include "glx.h"
#include "d3d.h"
#include "d3dhal.h"
#include "wine/wine_gl.h"
#include "wine/d3dhalgl.h"

#include "windef.h"
#include "wingdi.h"
#include "ddrawi.h"
#include "bitmap.h"
#include "win.h"
#include "wine/debug.h"
#include "options.h"

#include "glx_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(x11drv);

#define RENDER_TO_WINDOW
/* #define TRANSGAMING_LOGO */
/* #define NO_INDIRECT */
#define ENABLE_PBUFFER
#define ENABLE_BORKEN_MIGRATE
#define ENABLE_DDRAW_FLIP
#define NO_SCENE_LOCK
#define DEFER_RENDER_COERCE
#define INCREMENTAL_READ /* this one may need benchmarking */
#define SINGLEBUFFER_PBUFFER

#ifndef GL_VERTEX_PROGRAM_NV
#define GL_VERTEX_PROGRAM_NV              0x8620
#endif

#ifndef GL_ARB_vertex_program
#define GL_VERTEX_PROGRAM_ARB             0x8620
#endif

#ifndef GL_ARB_fragment_program
#define GL_FRAGMENT_PROGRAM_ARB           0x8804
#endif

extern void X11DRV_DD_Update(void);
extern void X11DRV_DD_PrimaryReset(void);

DWORD X11DRV_GLX_TLS = INVALID_HANDLE_VALUE;

static BOOL glx_available = FALSE;

void X11DRV_GLX_Init(void)
{
  int glx_event, glx_error, glx_major, glx_minor;

  if (glx_available) return; /* already initialized? */

  ENTER_GL();
  if (glXQueryExtension(gdi_display, &glx_event, &glx_error) &&
      glXQueryVersion(gdi_display, &glx_major, &glx_minor)) {

    TRACE("GLX extension detected and enabled\n");
    glx_available = TRUE;

    X11DRV_GLX_TLS = TlsAlloc();

    X11DRV_GLX_EB_Init();
  }
  LEAVE_GL();
}

void X11DRV_GLX_Cleanup(void)
{
  X11DRV_GLX_EB_Cleanup();
  X11DRV_GLX_Detach();
  if (TlsFree(X11DRV_GLX_TLS))
    X11DRV_GLX_TLS = INVALID_HANDLE_VALUE;
}

/* actual Direct3D HAL stuff */

static void X11DRV_GLX_CleanTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt);
static void X11DRV_GLX_DestroyTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt);
static void X11DRV_GLX_SwitchToTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt);

static void X11DRV_GLX_CleanTargets(LPD3DCONTEXT ctx);
static void X11DRV_GLX_DestroyTargets(LPD3DCONTEXT ctx);

static LPD3DTARGET X11DRV_GLX_FindTargetByDIB(LPD3DCONTEXT ctx, X11DRV_DIBSECTION* dib);
static DWORD X11DRV_GLX_IsTargetPixmap(LPD3DCONTEXT ctx, LPD3DTARGET tgt);

static void X11DRV_GLX_UpdateTexture(LPD3DCONTEXT ctx);

static void X11DRV_GLX_SetupLogo(LPD3DCONTEXT ctx);

#if defined( TRANSGAMING_LOGO )
static void X11DRV_GLX_RenderLogo(LPD3DCONTEXT ctx);
#endif

static inline LPD3DTCONTEXT X11DRV_GLX_GetCurrentT(void)
{
  return TlsGetValue(X11DRV_GLX_TLS);
}

static inline void X11DRV_GLX_SetCurrentT(LPD3DTCONTEXT ctx)
{
  TlsSetValue(X11DRV_GLX_TLS, ctx);
}

inline LPD3DCONTEXT X11DRV_GLX_GetCurrent(void)
{
  LPD3DTCONTEXT ctx = X11DRV_GLX_GetCurrentT();
  return ctx ? ctx->parent : NULL;
}

static unsigned X11DRV_GLX_Format(DIBSECTIONOBJ *dib, GLenum *fmt, GLenum *type)
{
  switch (dib->dsBmih.biBitCount) {
  case 15:
    *fmt=GL_BGRA; *type=GL_UNSIGNED_SHORT_1_5_5_5_REV;
    return 2;
  case 16:
    *fmt=GL_RGB; *type=GL_UNSIGNED_SHORT_5_6_5;
    return 2;
  case 24:
    *fmt=GL_BGR; *type=GL_UNSIGNED_BYTE;
    return 3;
  case 32:
    *fmt=GL_BGRA; *type=GL_UNSIGNED_BYTE;
    return 4;
  default:
    ERR("unhandled DIB depth %d\n", dib->dsBmih.biBitCount);
    *fmt=0; *type=0;
    return 0;
  }
}

static unsigned X11DRV_GLX_ZFormat(DIBSECTIONOBJ *dib, GLenum *fmt, GLenum *type)
{
  *fmt = GL_DEPTH_COMPONENT;
  switch (dib->dsBmih.biBitCount) {
  case 8:
    *type = GL_UNSIGNED_BYTE;
    return 1;
  case 15:
  case 16:
    *type = GL_UNSIGNED_SHORT;
    return 2;
  case 24:
  case 32:
    *type = GL_UNSIGNED_INT;
    return 4;
  default:
    ERR("unhandled DIB depth %d\n", dib->dsBmih.biBitCount);
    *fmt=0; *type=0;
    return 0;
  }
}

static BOOL X11DRV_GLX_Destroy(LPD3DCONTEXT ctx)
{
  BOOL ret;

  X11DRV_GLX_CleanTargets(ctx);
  ret = X11DRV_GLX_KillCurrent(ctx);
  X11DRV_GLX_DestroyTargets(ctx);
  XFree(ctx->vis);
  return ret;
}

static void X11DRV_GLX_FreeContext(LPD3DCONTEXT ctx)
{
  CloseHandle(ctx->p.thread);
  if (ctx->cant_copy) {
    close(ctx->sync_pipe[0]);
    close(ctx->sync_pipe[1]);
  }
  HeapFree(GetProcessHeap(), 0, ctx);
}

static inline RECTL* X11DRV_GLX_GetRect(LPD3DTARGET tgt)
{
  return (tgt->rect_lock || tgt->rect_area) ? &tgt->rect : NULL;
}

static inline BOOL X11DRV_GLX_Inverted(LPD3DCONTEXT ctx, LPD3DTARGET tgt)
{
  /* we need a render target to be flipped (upside down) if it
   * must be used by glCopyTexImage2D. */
  return (X11DRV_GLX_IsTargetPixmap(ctx, tgt) & FLAG_PBUFFER) ? TRUE : FALSE;
}

inline D3DTARGET* X11DRV_GLX_GetCurTgt(LPD3DCONTEXT ctx)
{
  return &ctx->cache[ctx->curp];
}

inline D3DTARGET* X11DRV_GLX_GetCurZbuf(LPD3DCONTEXT ctx)
{
  return &ctx->zcache[ctx->curp];
}

inline DWORD X11DRV_GLX_GetFlags(LPD3DCONTEXT ctx)
{
  return X11DRV_GLX_GetCurTgt(ctx)->flags;
}

BOOL X11DRV_GLX_StartDraw(LPD3DCONTEXT ctx, LPD3DTARGET tgt, GLbitfield mask, GLbitfield client_mask)
{
  unsigned w, h;
  BOOL invert = X11DRV_GLX_Inverted(ctx, tgt);
  int i;

  if (tgt->dib) {
    w = tgt->dib->dibSection.dsBm.bmWidth;
    h = tgt->dib->dibSection.dsBm.bmHeight;
  } else {
    Window root;
    int x, y;
    unsigned int width, height, border, depth;
    XGetGeometry(gdi_display, tgt->draw, &root, &x, &y, &width, &height, &border, &depth);
    w = width;
    h = height;
    TRACE("viewport: w=%d, h=%d\n", w, h);
  }

  /* Save GL state info. */
  glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_VIEWPORT_BIT | mask);
  if (client_mask) glPushClientAttrib(client_mask);

  /* Set up the transformation so that we can set the rasterpos appropriately */
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  if (invert) glOrtho(0.0, w, 0.0, h, -1, 1);
  else glOrtho(0.0, w, h, 0.0, -1, 1);
  glViewport(0, 0, w, h);

  if (HAVE_GL_EXT(ctx, ARB_vertex_program))
    glDisable(GL_VERTEX_PROGRAM_ARB);
  if (HAVE_GL_EXT(ctx, NV_vertex_program))
    glDisable(GL_VERTEX_PROGRAM_NV);
  if (HAVE_GL_EXT(ctx, ARB_vertex_blend))
    glDisable(GL_VERTEX_BLEND_ARB);
  if (HAVE_GL_EXT(ctx, EXT_vertex_weighting))
    glDisable(GL_VERTEX_WEIGHTING_EXT);

  /* Disable OpenGL features that might affect glDrawPixels or the FPS display. */
  glDisable(GL_ALPHA_TEST);
  glDisable(GL_BLEND);
  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_CULL_FACE);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_DITHER);
  glDisable(GL_FOG);
  glDisable(GL_LIGHTING);
  glDisable(GL_SCISSOR_TEST);
  glDisable(GL_STENCIL_TEST);
  if (mask & GL_TEXTURE_BIT) glMatrixMode(GL_TEXTURE);
  if (ctx->texunits > 1) {
    for (i=0; i<ctx->texunits; i++) {
      ctx->glActiveTextureARB(GL_TEXTURE0_ARB + i);
      glDisable(GL_TEXTURE_2D);
      if (HAVE_GL_EXT(ctx, ARB_texture_cube_map))
        glDisable(GL_TEXTURE_CUBE_MAP_ARB);

      if (client_mask & GL_CLIENT_VERTEX_ARRAY_BIT) {
        ctx->glClientActiveTextureARB(GL_TEXTURE0_ARB + i);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
      }

      if (mask & GL_TEXTURE_BIT) {
        glPushMatrix();
        glLoadIdentity();
        glDisable(GL_TEXTURE_GEN_S);
        glDisable(GL_TEXTURE_GEN_T);
        glDisable(GL_TEXTURE_GEN_R);
        glDisable(GL_TEXTURE_GEN_Q);
      }
    }
    ctx->glActiveTextureARB(GL_TEXTURE0_ARB);
    if (client_mask & GL_CLIENT_VERTEX_ARRAY_BIT)
      ctx->glClientActiveTextureARB(GL_TEXTURE0_ARB);
  }
  else {
    glDisable(GL_TEXTURE_2D);
    if (HAVE_GL_EXT(ctx, ARB_texture_cube_map))
      glDisable(GL_TEXTURE_CUBE_MAP_ARB);

    if (client_mask & GL_CLIENT_VERTEX_ARRAY_BIT)
      glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    if (mask & GL_TEXTURE_BIT) {
      glPushMatrix();
      glLoadIdentity();
      glDisable(GL_TEXTURE_GEN_S);
      glDisable(GL_TEXTURE_GEN_T);
      glDisable(GL_TEXTURE_GEN_R);
      glDisable(GL_TEXTURE_GEN_Q);
    }
  }

  if (client_mask & GL_CLIENT_VERTEX_ARRAY_BIT) {
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    if (HAVE_GL_EXT(ctx, EXT_secondary_color))
      glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
    if (HAVE_GL_EXT(ctx, EXT_fog_coord))
      glDisableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
  }

  if (HAVE_GL_EXT(ctx, EXT_secondary_color))
    ctx->glSecondaryColor3ubEXT(0,0,0);

  if (HAVE_GL_EXT(ctx, NV_register_combiners))
    glDisable(GL_REGISTER_COMBINERS_NV);

  if (HAVE_GL_EXT(ctx, ARB_vertex_program))
    glDisable(GL_VERTEX_PROGRAM_ARB);

  if (HAVE_GL_EXT(ctx, ARB_fragment_program))
    glDisable(GL_FRAGMENT_PROGRAM_ARB);

  return invert;
}

void X11DRV_GLX_EndDraw(LPD3DCONTEXT ctx, GLbitfield mask, GLbitfield client_mask)
{
  if (mask & GL_TEXTURE_BIT) {
    glMatrixMode(GL_TEXTURE);
    if (ctx->texunits > 1) {
      int i;
      for (i=0; i<ctx->texunits; i++) {
        ctx->glActiveTextureARB(GL_TEXTURE0_ARB + i);
        glPopMatrix();
      }
      ctx->glActiveTextureARB(GL_TEXTURE0_ARB);
    }
    else
    {
      glPopMatrix();
    }
  }

  /* Restore GL state info and finish */
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
  if (client_mask) glPopClientAttrib();
  glPopAttrib();
}

/* This function is called with GLX resources locked */
static void X11DRV_GLX_CopyDIB(LPD3DCONTEXT ctx, LPD3DTARGET tgt, DWORD rects, LPRECTL rectl)
{
  BITMAPOBJ *bmp;
  X11DRV_DIBSECTION *dib;
  GLenum fmt, type;
  unsigned w, h, p, bpp;
  RECTL* rect;
  RECTL def;
  LPBYTE bits;
  BOOL invert;

  TRACE("copying from %s DIBsection to GL buffer\n", (tgt->flags&FLAG_ZBUFFER)?"Zbuffer":"RGB");

  bmp = tgt->lockedBmp;
  if (!bmp)
    return;

  dib = (X11DRV_DIBSECTION *)bmp->dib;

  if (!dib)
    return;

  /* make the DIB readable for us */
  if (dib->status == DIB_Status_GdiMod) {
    TRACE("state is GdiMod, converting to InSync\n");
    glXWaitGL();
    X11DRV_DIB_Coerce(bmp, DIB_Status_InSync, FALSE);
    dib->p_status = DIB_Status_InSync;
    glXWaitX();
  }
  if (dib->status == DIB_Status_AuxMod) {
    /* this only happens when called from RenderArea */
    TRACE("state is AuxMod, converting to InSync\n");
    X11DRV_DIB_Coerce(bmp, DIB_Status_InSync, TRUE);
    dib->p_status = DIB_Status_InSync;
  }
  w = dib->dibSection.dsBm.bmWidth;
  h = dib->dibSection.dsBm.bmHeight;
  p = dib->dibSection.dsBm.bmWidthBytes;
  bits = dib->dibSection.dsBm.bmBits;
  if (tgt->flags & FLAG_ZBUFFER) {
    bpp = X11DRV_GLX_ZFormat(&dib->dibSection, &fmt, &type);
  } else {
    bpp = X11DRV_GLX_Format(&dib->dibSection, &fmt, &type);
  }
  TRACE("fmt=%x, type=%x, w=%d, h=%d, p=%d, bits=%p, state=%d\n", fmt, type, w, h, p, bits, dib->status);
  rect = X11DRV_GLX_GetRect(tgt);
  if (!rect) {
    def.left = 0;
    def.top = 0;
    def.right = w;
    def.bottom = h;
    rect = &def;
  } else {
    bits += rect->top * p + rect->left * bpp;
  }
  TRACE("main rect: (%ld,%ld)-(%ld,%ld)\n", rect->left, rect->top, rect->right, rect->bottom);

  /* glPixelZoom changes stuff in GL_PIXEL_MODE_BIT, but nobody else calls
   * glDrawPixels or glCopyPixels yet. */
  invert = X11DRV_GLX_StartDraw(ctx, tgt, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, 0);

  if (fmt == GL_DEPTH_COMPONENT) {
    glDepthFunc(GL_ALWAYS);
    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);
    glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
  }

  /* Transfer pixels. */
  if (fmt == GL_DEPTH_COMPONENT)
  {
      glPixelZoom(1, 1);

      if (invert) {
        glRasterPos2i(rect->left, rect->top);
        glDrawPixels(rect->right-rect->left, rect->bottom-rect->top, fmt, type, bits);
      } else {
        DWORD y, dw;
        /* This is much faster on nVidia and Mesa-3.5.
         * Don't know about others. */
        dw = rect->right - rect->left;
        for (y = rect->top; y < rect->bottom; y++)
        {
          glRasterPos2i(rect->left, y);
          glDrawPixels(dw, 1, fmt, type, bits);
          bits += p;
        }
      }
  }
  else
  {
      glPixelZoom(1, invert ? 1 : -1);
      glPixelStorei(GL_UNPACK_ROW_LENGTH, p/bpp); /* the unpack alignment takes care of p%bpp */
      if (rects) {
        LPBYTE dbits = dib->dibSection.dsBm.bmBits;
        DWORD u;
        for (u=0; u<rects; u++) {
          LPBYTE rbits = dbits + rectl[u].top * p + rectl[u].left * bpp;
          TRACE("rectl: (%ld,%ld)-(%ld,%ld)\n", rectl[u].left, rectl[u].top, rectl[u].right, rectl[u].bottom);
          glRasterPos2i(rectl[u].left, rectl[u].top);
          glDrawPixels(rectl[u].right-rectl[u].left, rectl[u].bottom-rectl[u].top, fmt, type, rbits);
        }
      } else {
        glRasterPos2i(rect->left, rect->top);
        glDrawPixels(rect->right-rect->left, rect->bottom-rect->top, fmt, type, bits);
      }
      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  }

  X11DRV_GLX_EndDraw(ctx, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, 0);

  CHECK_GL_ERROR("CopyDIB");

  dib->status = DIB_Status_AuxMod;
  TRACE("complete\n");
}

static void X11DRV_GLX_copy_aux(BITMAPOBJ*bmp, void*c, int req)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)c;
  X11DRV_DIBSECTION *dib = (X11DRV_DIBSECTION *)bmp->dib;
  LPD3DTARGET tgt = X11DRV_GLX_FindTargetByDIB(ctx, dib);
  LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
  LPDDRAWI_DDRAWSURFACE_LCL old = ctx->nsurf;
  GLenum fmt, type;
  unsigned w, h, p, bpp;
  RECTL* rect;
  RECTL def;
  LPBYTE bits;
  DWORD back;

  X11DRV_DIB_Coerce(bmp, DIB_Status_AppMod, TRUE);

  if (!tgt) {
    ERR("render target not found!\n");
    return;
  }

  w = dib->dibSection.dsBm.bmWidth;
  h = dib->dibSection.dsBm.bmHeight;
  p = dib->dibSection.dsBm.bmWidthBytes;
  bits = dib->dibSection.dsBm.bmBits;
  back = tgt->flags & FLAG_BACKBUF;
  if (tgt->flags & FLAG_ZBUFFER) {
    bpp = X11DRV_GLX_ZFormat(&dib->dibSection, &fmt, &type);
    back = !X11DRV_GLX_IsTargetPixmap(ctx, tgt);
    TRACE("copying from GL depth buffer to Zbuffer DIBsection\n");
  } else {
    bpp = X11DRV_GLX_Format(&dib->dibSection, &fmt, &type);
    /* check whether we have done a GLX flip since the last rendering */
    if ((tgt->draw == cur->draw) && (X11DRV_GLX_GetFlags(ctx) & FLAG_FLIPPED))
      back ^= FLAG_BACKBUF;
    TRACE("copying from %s buffer to RGB DIBsection\n", back?"GL_BACK":"GL_FRONT");
  }
  TRACE("fmt=%x, type=%x, w=%d, h=%d, bpp=%d, p=%d, bits=%p, state=%d\n", fmt, type, w, h, bpp, p, bits, dib->status);
  rect = X11DRV_GLX_GetRect(tgt);
  if (!rect) {
    def.left = 0;
    def.top = 0;
    def.right = w;
    def.bottom = h;
    rect = &def;
  } else {
    bits += rect->top * p + rect->left * bpp;
  }

  ENTER_GL();

  if (tgt->draw && tgt->draw != cur->draw) {
    X11DRV_GLX_SwitchToTarget(ctx, tgt);
    ctx->nsurf = NULL;
  }

  if (SUCCEEDED(X11DRV_GLX_MakeCurrent(ctx))) {
    DWORD y, dw;

    TRACE("rect: (%ld,%ld)-(%ld,%ld)\n", rect->left, rect->top, rect->right, rect->bottom);
    glFinish();
    glReadBuffer(back ? GL_BACK : GL_FRONT);
    if (X11DRV_GLX_Inverted(ctx, tgt)) {
      glPixelStorei(GL_PACK_ROW_LENGTH, p/bpp); /* the pack alignment takes care of p%bpp */
      glReadPixels(rect->left, rect->top, rect->right-rect->left, rect->bottom-rect->top, fmt, type, bits);
      glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    } else {
      /* because OpenGL's coordinates are upside-down relative to the DIBs DirectDraw uses,
       * we must read out pixel data line by line into app memory */
      dw = rect->right - rect->left;
      for (y = rect->top; y < rect->bottom; y++) {
        glReadPixels(rect->left, h-y-1, dw, 1, fmt, type, bits);
        bits += p;
      }
    }

    tgt->rect_area = FALSE;
  }
  else {
    ERR("failed to update DIB section from OpenGL frame buffer\n");
  }

  CHECK_GL_ERROR("copy_aux");

  if (tgt->draw && tgt->draw != cur->draw) {
    X11DRV_GLX_SwitchToTarget(ctx, cur);
    ctx->nsurf = old;
    if (cur->lockedBmp) X11DRV_GLX_MakeCurrent(ctx);
  }

  X11DRV_GLX_FlushCurrent(ctx);
  LEAVE_GL();
  TRACE("complete\n");
}

#define FONT_BASE 4200

BOOL X11DRV_GLX_write_aux(BITMAPOBJ *bmp, void*c, DC *dc, INT x, INT y, UINT flags,
                          const RECT *lprect, LPCWSTR wstr, UINT count,
                          const INT *lpDx)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)c;
  X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
  fontObject *pfo = XFONT_GetFontObject( physDev->font );
  XFontStruct *font = pfo->fs;
  int i;
  GLint rp[2];

  TRACE("hdc=%04x df=%04x %d,%d %s, %d  flags=%d lpDx=%p\n",
        dc->hSelf, (UINT16)(physDev->font), x, y,
        debugstr_wn (wstr, count), count, flags, lpDx);
  /* if an app draws onto a surface used for GLX rendering, it's probably
   * not very advanced text, we'll only handle common cases */
  /* hmm, we ought to be able to handle ETO_OPAQUE and ETO_CLIPPED? */
  if (flags) goto write_fallback;
  if (lpDx) goto write_fallback;
  /* we're not Unicode-aware yet */
  for (i=0; i<count; i++) if (wstr[i] >= 256) goto write_fallback;

  rp[0] = INTERNAL_XWPTODP( dc, x, y );
  rp[1] = INTERNAL_YWPTODP( dc, x, y ) + font->ascent+1;

  TRACE("pos: %d, %d, color: %08lx\n", rp[0], rp[1], dc->textColor);

  ENTER_GL();
  /* should we set the context? */
  if (SUCCEEDED(X11DRV_GLX_MakeCurrent(ctx))) {
    if (font->fid != ctx->lfont) {
      TRACE("generating gdi_display lists for font %ld\n", font->fid);
      glXUseXFont(font->fid, 0, 256, FONT_BASE);
      ctx->lfont = font->fid;
    }

    X11DRV_GLX_StartDraw(ctx, X11DRV_GLX_GetCurTgt(ctx), GL_LIST_BIT, 0);

    glColor3ub(GetRValue(dc->textColor),
               GetGValue(dc->textColor),
               GetBValue(dc->textColor));
    glRasterPos2iv(rp);

    glListBase(FONT_BASE);
    glCallLists(count, GL_UNSIGNED_SHORT, wstr);

    X11DRV_GLX_EndDraw(ctx, GL_LIST_BIT, 0);

    X11DRV_GLX_UpdateTexture(ctx);

    X11DRV_GLX_FlushCurrent(ctx);
    LEAVE_GL();
  }
  else {
    LEAVE_GL();
    goto write_fallback;
  }

  return TRUE;

write_fallback:
  {
    X11DRV_DIBSECTION *dib;
    BOOL ret;
    dib = (X11DRV_DIBSECTION *)bmp->dib;
    dib->write_aux = NULL;
    ret = X11DRV_ExtTextOut(dc, x, y, flags, lprect, wstr, count, lpDx);
    dib->write_aux = X11DRV_GLX_write_aux;
    return ret;
  }
}

static X11DRV_DIBSECTION *X11DRV_GLX_AttachAux(LPD3DCONTEXT ctx, HBITMAP hBmp)
{
   BITMAPOBJ *bmp;
   X11DRV_DIBSECTION *dib;

   /* attach aux handlers to DIB sections
    * (hoping no more than one context uses it) */
   bmp = (BITMAPOBJ *)GDI_GetObjPtr(hBmp, BITMAP_MAGIC);
   dib = (X11DRV_DIBSECTION *)bmp->dib;
   if (dib) {
     dib->aux_ctx = ctx;
     dib->copy_aux = X11DRV_GLX_copy_aux;
     dib->write_aux = X11DRV_GLX_write_aux;
   }
   GDI_ReleaseObj(hBmp);
   TRACE("aux handler attached to %p (%p)\n", bmp, dib);
   return dib;
}

static void X11DRV_GLX_MakeZbuffer(LPD3DCONTEXT ctx, LPDDRAWI_DDRAWSURFACE_LCL lsurf)
{
  LPDDRAWI_DDRAWSURFACE_GBL surf = lsurf ? lsurf->lpGbl : (LPDDRAWI_DDRAWSURFACE_GBL)NULL;
  HBITMAP hBmp = surf ? GET_LPDDRAWSURFACE_GBL_MORE(surf)->hKernelSurface : (HBITMAP)0;
  LPD3DTARGET zbuf = X11DRV_GLX_GetCurZbuf(ctx);
  GLXDrawable draw = X11DRV_GLX_GetCurTgt(ctx)->draw;
  if (zbuf->draw != draw) {
    X11DRV_GLX_CleanTarget(ctx, zbuf);
    X11DRV_GLX_DestroyTarget(ctx, zbuf);
    zbuf->draw = draw;
  }
  zbuf->flags |= FLAG_ZBUFFER;
  if (hBmp) {
    X11DRV_DIBSECTION *dib;
    TRACE("setting surf %p to ctx %p\n", lsurf, ctx);
    dib = X11DRV_GLX_AttachAux(ctx, hBmp);
    zbuf->dib = dib;
    zbuf->bmp = hBmp;
  }
  ctx->zsurf = lsurf;
}

static void X11DRV_GLX_LockTarget(LPDDRAWI_DDRAWSURFACE_LCL lsurf, BOOL lock)
{
  LPDDRAWI_DDRAWSURFACE_GBL surf = lsurf ? lsurf->lpGbl : (LPDDRAWI_DDRAWSURFACE_GBL)NULL;
  HBITMAP hBmp = surf ? GET_LPDDRAWSURFACE_GBL_MORE(surf)->hKernelSurface : (HBITMAP)0;
  if (!hBmp) return;
  if (lock) GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
  else GDI_ReleaseObj( hBmp );
}

static void X11DRV_GLX_CleanTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt)
{
  TRACE("(%p)\n", tgt);
  if (tgt->flags & FLAG_ZBUFFER) {
    X11DRV_CoerceDIBSection2(tgt->bmp, DIB_Status_InSync, FALSE);
  }
  else if (tgt->bmp && tgt->draw) {
    if (!(tgt->flags & FLAG_PIXMAP)) {
      X11DRV_CoerceDIBSection2(tgt->bmp, DIB_Status_InSync, FALSE);
    }
  }
}

static void X11DRV_GLX_DestroyTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt)
{
  TRACE("(%p)\n", tgt);
  if (tgt->bmp) {
    if (tgt->draw) {
      if (tgt->flags & FLAG_PIXMAP) {
        TRACE("destroying GLXpixmap %08lx\n", tgt->draw);
        glXDestroyGLXPixmap(gdi_display, tgt->draw);
      }
      else if (tgt->flags & FLAG_PBUFFER) {
        TRACE("destroying Pbuffer %08lx\n", tgt->draw);
        if (ctx->glXDestroyGLXPbufferSGIX) {
          ctx->glXDestroyGLXPbufferSGIX(gdi_display, tgt->draw);
        } else if (ctx->glx13_pbuffers) {
          ctx->glXDestroyPbuffer(gdi_display, tgt->draw);
        } else {
          ERR("no way to destroy Pbuffer\n");
        }
      }
    }
    tgt->draw = None;
    tgt->bmp = 0;
    tgt->dib = 0;
    if (tgt->lockedBmp) ERR("destroying captured target\n");
    tgt->lockedBmp = NULL;
    if (tgt->locks) ERR("destroying locked target\n");
    tgt->locks = 0;
    tgt->lockf = 0;
    tgt->rect_lock = FALSE;
    tgt->rect_area = FALSE;
  }
  tgt->flags &= FLAG_COMMON;
}

static void X11DRV_GLX_CleanTargets(LPD3DCONTEXT ctx)
{
  DWORD c;
  for (c=0; c<CACHESIZE; c++) {
    X11DRV_GLX_CleanTarget(ctx, &ctx->cache[c]);
    X11DRV_GLX_CleanTarget(ctx, &ctx->zcache[c]);
  }
}

static void X11DRV_GLX_DestroyTargets(LPD3DCONTEXT ctx)
{
  DWORD c;
  for (c=0; c<CACHESIZE; c++) {
    X11DRV_GLX_DestroyTarget(ctx, &ctx->cache[c]);
    X11DRV_GLX_DestroyTarget(ctx, &ctx->zcache[c]);
  }
}

static void X11DRV_GLX_SwitchToTarget(LPD3DCONTEXT ctx, LPD3DTARGET tgt)
{
  DWORD c = tgt - ctx->cache;
  LPD3DTARGET last;

  TRACE("(%p)\n", tgt);
  if (c>=CACHESIZE) {
    c = tgt - ctx->zcache;
    if (c>=CACHESIZE) {
      ERR("bad target %p (%p+%ld)\n", tgt, ctx->cache, c);
      return;
    }
  }
  if (c == ctx->curp) return;
  last = &ctx->cache[ctx->curp];
  /* migrate flags */
  tgt->flags = (tgt->flags&~FLAG_COMMON) | (last->flags&FLAG_COMMON);
  if (last->flags & FLAG_FLIPPED)
    last->flags = (last->flags^FLAG_BACKBUF)&~FLAG_FLIPPED;
  ctx->curp = c;
}

static LPD3DTARGET X11DRV_GLX_NewTarget(LPD3DCONTEXT ctx)
{
  DWORD c, n, next;
  LPD3DTARGET tgt;
  next = (ctx->curp+1)%CACHESIZE;
  for (n=ctx->curp,c=0; c<CACHESIZE; c++) {
    if (!ctx->cache[n].draw) {
      next = n;
      break;
    }
    n = (n+1)%CACHESIZE;
  }
  tgt = &ctx->cache[next];
  if (tgt->draw) {
    FIXME("reusing cached render target - increase cache size\n");
  }
  X11DRV_GLX_SwitchToTarget(ctx, tgt);
  /* for now, the caller must call CleanTarget and DestroyTarget */
  return tgt;
}

static LPD3DTARGET X11DRV_GLX_ReuseOffscreenTarget(LPD3DCONTEXT ctx, HBITMAP hbmp)
{
  BITMAPOBJ *bmp;
  DWORD c, n, next;
  LPD3DTARGET tgt;
  BOOL destroy = FALSE;

  if (!hbmp) return NULL;
  bmp = (BITMAPOBJ *)GDI_GetObjPtr( hbmp, BITMAP_MAGIC );

  next = (ctx->curp+1)%CACHESIZE;
  /* search for any existing target with the same size */
  for (n=ctx->curp,c=0; c<CACHESIZE; c++) {
    if (ctx->cache[n].draw && (ctx->cache[n].flags & FLAG_OFFSCREEN) &&
        ctx->cache[n].dib->dibSection.dsBm.bmWidth == bmp->bitmap.bmWidth &&
        ctx->cache[n].dib->dibSection.dsBm.bmHeight == bmp->bitmap.bmHeight) {
      next = n;
      break;
    }
    n = (n+1)%CACHESIZE;
  }

  if (c == CACHESIZE) {
    /* search for any existing target */
    for (n=ctx->curp,c=0; c<CACHESIZE; c++) {
      if (ctx->cache[n].draw && (ctx->cache[n].flags & FLAG_OFFSCREEN)) {
        next = n;
        destroy = TRUE;
        break;
      }
      n = (n+1)%CACHESIZE;
    }
  }

  GDI_ReleaseObj( hbmp );

  tgt = &ctx->cache[next];
  if (!tgt->draw) return NULL;
  X11DRV_GLX_CleanTarget(ctx, tgt);
  if (destroy) {
    X11DRV_GLX_DestroyTarget(ctx, tgt);
    /* at this point, the drawable we've just destroyed is still current
     * (because of CleanTarget), so we have to make sure to deselect it
     * before trying to allocate a new one, or we've gotten nowhere
     * (and some drivers even deadlock). Since we want to avoid deselecting
     * the context completely, just use any other existing target. */
    for (n=ctx->curp,c=0; c<CACHESIZE; c++) {
      if (ctx->cache[n].draw) {
        next = n;
        break;
      }
      n = (n+1)%CACHESIZE;
    }
    if (ctx->cache[next].draw) {
      X11DRV_GLX_SwitchToTarget(ctx, &ctx->cache[next]);
      X11DRV_GLX_MakeCurrent(ctx);
    } else {
      /* last resort */
      X11DRV_GLX_KillCurrent(ctx);
    }
  }
  X11DRV_GLX_SwitchToTarget(ctx, tgt);
  return tgt;
}

static DWORD X11DRV_GLX_IsTargetPixmap(LPD3DCONTEXT ctx, LPD3DTARGET tgt)
{
  DWORD c = tgt - ctx->cache;

  TRACE("(%p)\n", tgt);
  if (c>=CACHESIZE) {
    c = tgt - ctx->zcache;
    if (c>=CACHESIZE) {
      ERR("bad target %p (%p+%ld)\n", tgt, ctx->cache, c);
      return FALSE;
    }
  }
  return ctx->cache[c].flags & FLAG_OFFSCREEN;
}

static LPD3DTARGET X11DRV_GLX_FindTargetByHBMP(LPD3DCONTEXT ctx, HBITMAP hBmp)
{
  DWORD c;

  if (!hBmp) return NULL;
  for (c=0; c<CACHESIZE; c++) {
    if (ctx->cache[c].bmp == hBmp) return &ctx->cache[c];
    if (ctx->zcache[c].bmp == hBmp) return &ctx->zcache[c];
  }
  return NULL;
}

static LPD3DTARGET X11DRV_GLX_FindTargetByDIB(LPD3DCONTEXT ctx, X11DRV_DIBSECTION* dib)
{
  DWORD c;

  if (!dib) return NULL;
  for (c=0; c<CACHESIZE; c++) {
    if (ctx->cache[c].dib == dib) return &ctx->cache[c];
    if (ctx->zcache[c].dib == dib) return &ctx->zcache[c];
  }
  return NULL;
}

static LPD3DTARGET X11DRV_GLX_FindTargetByDraw(LPD3DCONTEXT ctx, GLXDrawable draw)
{
  DWORD c;

  if (!draw) return NULL;
  for (c=0; c<CACHESIZE; c++) {
    if (ctx->cache[c].draw == draw) return &ctx->cache[c];
  }
  return NULL;
}

static LPD3DTARGET X11DRV_GLX_FindTargetByHWND(LPD3DCONTEXT ctx, HWND hWnd)
{
  if (!hWnd) return NULL;
  return X11DRV_GLX_FindTargetByDraw(ctx, X11DRV_get_client_window(hWnd));
}

static LPD3DTARGET X11DRV_GLX_FindTarget(LPD3DCONTEXT ctx, LPDDRAWI_DDRAWSURFACE_LCL lsurf)
{
  HWND hWnd = lsurf ? (HWND)lsurf->lpSurfMore->lpDDRAWReserved : 0;
  LPDDRAWI_DDRAWSURFACE_GBL surf = lsurf ? lsurf->lpGbl : (LPDDRAWI_DDRAWSURFACE_GBL)NULL;
  HBITMAP hBmp = surf ? GET_LPDDRAWSURFACE_GBL_MORE(surf)->hKernelSurface : (HBITMAP)0;
  return hBmp ? X11DRV_GLX_FindTargetByHBMP(ctx, hBmp)
              : X11DRV_GLX_FindTargetByHWND(ctx, hWnd);
}

static BOOL X11DRV_GLX_CreateOffscreen(LPD3DCONTEXT ctx, LPD3DTARGET tgt, HBITMAP hbmp)
{
  BITMAPOBJ *bmp;
  GLXFBConfig config = None;
  Drawable draw;
  BOOL ret = TRUE;

  tgt->draw = 0;
  tgt->flags &= ~FLAG_OFFSCREEN;

  bmp = (BITMAPOBJ *)GDI_GetObjPtr( hbmp, BITMAP_MAGIC );

#ifndef SINGLEBUFFER_PBUFFER
  if (ctx->glXGetFBConfigFromVisualSGIX) {
    config = ctx->glXGetFBConfigFromVisualSGIX(gdi_display, ctx->vis);
  } else
#endif
  if (ctx->glXChooseFBConfigSGIX || ctx->glx13_pbuffers) {
    GLXFBConfig *configs;
    int attribs[16];
    int t, c = 0;
#ifndef SINGLEBUFFER_PBUFFER
    /* not sure if fbconfig id is the same as visual id */
    attribs[c++] = GLX_FBCONFIG_ID;
    attribs[c++] = ctx->vis->visualid;
#else
    /* try to be compatible with the glXChooseVisual in x11drv_main.c */
    attribs[c++] = GLX_RENDER_TYPE;
    attribs[c++] = GLX_RGBA_BIT;
    attribs[c++] = GLX_X_RENDERABLE;
    attribs[c++] = True;
    glXGetConfig(gdi_display, ctx->vis, GLX_DEPTH_SIZE, &t);
    attribs[c++] = GLX_DEPTH_SIZE;
    attribs[c++] = t;
    glXGetConfig(gdi_display, ctx->vis, GLX_ALPHA_SIZE, &t);
    if (t) {
      attribs[c++] = GLX_ALPHA_SIZE;
      attribs[c++] = t;
    }
    /* we don't need double-buffering, though */
    attribs[c++] = GLX_DOUBLEBUFFER;
    attribs[c++] = False;
#endif
    attribs[c] = None;
    if (ctx->glXChooseFBConfigSGIX)
      configs = ctx->glXChooseFBConfigSGIX(gdi_display, DefaultScreen(gdi_display), attribs, &t);
    else
      configs = ctx->glXChooseFBConfig(gdi_display, DefaultScreen(gdi_display), attribs, &t);
    if (configs) {
      /* get best match */
      config = configs[0];
      XFree(configs);
    }
  }

  if (ctx->glx13_pbuffers || ctx->glXCreateGLXPbufferSGIX) {
    TRACE("creating Pbuffer with size %dx%d, config %p\n",
          bmp->bitmap.bmWidth, bmp->bitmap.bmHeight, config);
    X11DRV_expect_error(gdi_display, X11DRV_expect_any, NULL);
    if (ctx->glXCreateGLXPbufferSGIX) {
      int attribs[] = {None};
      tgt->draw = ctx->glXCreateGLXPbufferSGIX(gdi_display, config,
                                               bmp->bitmap.bmWidth,
                                               bmp->bitmap.bmHeight,
                                               attribs);
    }
    else if (ctx->glx13_pbuffers) {
      int attribs[8];
      int c = 0;
      attribs[c++] = GLX_PBUFFER_WIDTH;
      attribs[c++] = bmp->bitmap.bmWidth;
      attribs[c++] = GLX_PBUFFER_HEIGHT;
      attribs[c++] = bmp->bitmap.bmHeight;
      attribs[c] = None;
      tgt->draw = ctx->glXCreatePbuffer(gdi_display, config, attribs);
    }
    XSync(gdi_display, False);
    if (X11DRV_check_error()) {
      tgt->draw = 0;
      ret = FALSE;
      WARN("Pbuffer creation failed for target %p\n", tgt);
      goto finish;
    }
    TRACE("created Pbuffer %08lx for target %p\n", tgt->draw, tgt);
    tgt->flags |= FLAG_PBUFFER;
    X11DRV_GLX_AttachAux(ctx, hbmp);
    goto finish;
  }

  if (!bmp->physBitmap)
    X11DRV_CreateBitmap(hbmp);
  draw = (Pixmap)bmp->physBitmap;

  /* FIXME: try ctx->glXCreateGLXPixmapWithConfigSGIX, perhaps? */

  tgt->draw = glXCreateGLXPixmap(gdi_display, ctx->vis, draw);
  TRACE("created GLXpixmap %08lx\n", tgt->draw);
  tgt->flags |= FLAG_PIXMAP;

finish:
  if (ret) {
    tgt->bmp = hbmp;
    tgt->dib = (X11DRV_DIBSECTION *)bmp->dib;
  }
  GDI_ReleaseObj( hbmp );
  return ret;
}

static void X11DRV_GLX_TakeOver(LPD3DCONTEXT ctx, LPD3DTARGET tgt, HBITMAP hbmp)
{
  BITMAPOBJ *bmp;

  bmp = (BITMAPOBJ *)GDI_GetObjPtr( hbmp, BITMAP_MAGIC );
  tgt->bmp = hbmp;
  tgt->dib = (X11DRV_DIBSECTION *)bmp->dib;
  GDI_ReleaseObj( hbmp );
}

/* OK, this procedure is getting unmaintainable... */

static void X11DRV_GLX_MakeTarget(LPD3DCONTEXT ctx, LPDDRAWI_DDRAWSURFACE_LCL lsurf)
{
  HWND hWnd = lsurf ? (HWND)lsurf->lpSurfMore->lpDDRAWReserved : 0;
  LPDDRAWI_DDRAWSURFACE_GBL surf = lsurf ? lsurf->lpGbl : (LPDDRAWI_DDRAWSURFACE_GBL)NULL;
  HBITMAP hBmp = surf ? GET_LPDDRAWSURFACE_GBL_MORE(surf)->hKernelSurface : (HBITMAP)0;
  BITMAPOBJ *bmpObj = NULL;
  DWORD oldflags = X11DRV_GLX_GetFlags(ctx);
  LPD3DTARGET tgt, zbuf;
  BOOL is_new = FALSE;

  if (hBmp) bmpObj = GDI_GetObjPtr(hBmp, BITMAP_MAGIC); /* grab GDI lock */
  ENTER_GL();
  tgt = hBmp ? X11DRV_GLX_FindTargetByHBMP(ctx, hBmp)
             : X11DRV_GLX_FindTargetByHWND(ctx, hWnd);
  if (tgt) {
    X11DRV_GLX_SwitchToTarget(ctx, tgt);
    zbuf = X11DRV_GLX_GetCurZbuf(ctx);
  } else {
    tgt = X11DRV_GLX_NewTarget(ctx);
    zbuf = X11DRV_GLX_GetCurZbuf(ctx);
    is_new = TRUE;
    X11DRV_GLX_CleanTarget(ctx, tgt);
    X11DRV_GLX_CleanTarget(ctx, zbuf);
    X11DRV_GLX_DestroyTarget(ctx, zbuf);
  }
  tgt->flags &= ~FLAG_FLIPPED;
  if (hBmp || hWnd) {
    /* save this context in surface (hoping only one context uses it) */
    lsurf->dwReserved1 = (DWORD)ctx;
    TRACE("setting surf %p to ctx %p\n", lsurf, ctx);
    if (lsurf->ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) {
      /* find frontbuffer */
      LPATTACHLIST from = lsurf->lpAttachListFrom;
      LPDDRAWI_DDRAWSURFACE_LCL front = from ? from->lpAttached : NULL;
      if (front) {
        front->dwReserved1 = (DWORD)ctx;
        TRACE("setting front surf %p to ctx %p\n", front, ctx);
      }
    }
#ifdef RENDER_TO_WINDOW
    if (lsurf->ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_BACKBUFFER)) {
      if (oldflags & FLAG_PIXMAP) {
        /* destroy context if it's indirect for pixmaps only */
        if (ctx->p.ctx && ((tgt->flags & FLAG_INDIRECT) == FLAG_PINDIRECT)) {
          X11DRV_GLX_CleanTargets(ctx);
          X11DRV_GLX_KillCurrent(ctx);
          glXDestroyContext(gdi_display, ctx->p.ctx);
          ctx->p.ctx = 0;
          ctx->owner_ctx = 0;
          ctx->owner_last_draw = 0;
          ctx->p.stamp++;
          tgt->flags &= ~FLAG_CINDIRECT;
          X11DRV_GLX_DestroyTargets(ctx);
        }
      }
      tgt->flags &= ~FLAG_OFFSCREEN;
      if (is_new) {
        X11DRV_DIBSECTION *dib = hBmp ? X11DRV_GLX_AttachAux(ctx, hBmp) : NULL;
        Drawable draw;
        if (hWnd) {
          /* rendering into a window */
          draw = X11DRV_get_client_window(hWnd);
          TRACE("WND: %08x Drawable: 0x%08lx\n", hWnd, draw);
        } else {
          /* rendering fullscreen */
          draw = X11DRV_DD_PrimaryClient;
          TRACE("Drawable: 0x%08lx\n", draw);
        }
        if (!draw) {
          TRACE("no drawable, host off desktop\n");
          draw = X11DRV_get_client_window(GetDesktopWindow());
          TRACE("Drawable: 0x%08lx\n", draw);
        }
        tgt->draw = draw;
        tgt->bmp = hBmp;
        tgt->dib = dib;
      }
      if (lsurf->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
        tgt->flags &= ~FLAG_BACKBUF;
      else
        tgt->flags |= FLAG_BACKBUF;
      if (tgt->flags & FLAG_WINDIRECT) tgt->flags |= FLAG_CINDIRECT;
    }
    else
#endif
    if (is_new) {
      if (!(oldflags & FLAG_PIXMAP) && !(ctx->glXCreateGLXPbufferSGIX || ctx->glx13_pbuffers)) {
        /* destroy context if it's direct for windows only */
        if (ctx->p.ctx && ((tgt->flags & FLAG_DIRECT) == FLAG_WDIRECT)) {
          X11DRV_GLX_CleanTargets(ctx);
          X11DRV_GLX_KillCurrent(ctx);
          glXDestroyContext(gdi_display, ctx->p.ctx);
          ctx->p.ctx = 0;
          ctx->owner_ctx = 0;
          ctx->owner_last_draw = 0;
          ctx->p.stamp++;
          X11DRV_GLX_DestroyTargets(ctx);
        }
      }
      if (tgt->flags & FLAG_PINDIRECT) tgt->flags |= FLAG_CINDIRECT;
      do {
        /* need to render to "front" of pixmap */
        tgt->flags &= ~FLAG_BACKBUF;
        /* evict old cached pixmap */
        X11DRV_GLX_DestroyTarget(ctx, tgt);
        /* prepare new pixmap */
        if (!X11DRV_GLX_CreateOffscreen(ctx, tgt, hBmp)) {
          /* couldn't allocate, try to reuse an old one */
          tgt = X11DRV_GLX_ReuseOffscreenTarget(ctx, hBmp);
          if (tgt && tgt->draw) {
            ERR("failed to create new offscreen render target - reusing old render target - may be slow\n");
            X11DRV_GLX_TakeOver(ctx, tgt, hBmp);
          }
          else if (tgt) {
            ERR("failed to create new offscreen render target - evicting old render target - may be slow\n");
            /* will try allocating again on next iteration */
          }
          else {
            /* no old ones exist - utter failure */
            ERR("failed to create offscreen render target - expect failure\n");
            break;
          }
        }
      } while (!tgt->draw);
    }
    if (is_new) X11DRV_GLX_MakeZbuffer(ctx, ctx->zsurf);
  } else {
    /* this is probably GL_DirectDraw_Init trying to determine device caps,
     * it's probably OK to use the root window for this purpose, but the context
     * needs to use the root_windows's visual, not the standard one.  Some GL drivers
     * (ie: NVidia) choke if you have mismatched context/window visuals.  */
    XWindowAttributes root_win_attr;
    Visual *rootVisual;
    XVisualInfo template;
    int num;

    tgt->draw = root_window;
    tgt->flags = 0;
    tgt->bmp = 0;
    tgt->dib = 0;

    if (XGetWindowAttributes( gdi_display, root_window, &root_win_attr ))
    {
      rootVisual = root_win_attr.visual;
    }
    else
    {
      /* Get the default visual, since we can't seem to get the attributes from the
         Root Window.  Let's hope that the Root Window Visual matches the DefaultVisual */
      rootVisual = DefaultVisual( gdi_display, DefaultScreen(gdi_display) );
    }

    template.visualid = XVisualIDFromVisual(rootVisual);
    if (ctx->vis) XFree(ctx->vis);
    ctx->vis = XGetVisualInfo(gdi_display, VisualIDMask, &template, &num);

  }
  ctx->nsurf = lsurf;

  LEAVE_GL();
  if (bmpObj) GDI_ReleaseObj(hBmp);
}

/* This function is called with GLX resources locked */
DWORD X11DRV_GLX_MakeCurrent(LPD3DCONTEXT ctx)
{
  LPD3DTARGET tgt = X11DRV_GLX_GetCurTgt(ctx);
  BOOL direct = !(tgt->flags & FLAG_CINDIRECT);
  HRESULT res = D3D_OK;
  LPD3DTCONTEXT old_ctx = X11DRV_GLX_GetCurrentT();
  LPD3DTCONTEXT new_ctx = old_ctx;
  GLXContext save_ctx, used_ctx;
  GLXDrawable save_draw, used_draw;
  BOOL borked;

  /* only need to sync with GDI if we're rendering on a pixmap */
  if (tgt->flags & FLAG_PIXMAP) {
    /* a full sync is somewhat expensive, try to only do it if there
     * might be unprocessed GDI requests at this point */
    if (NextRequest(gdi_display) != (LastKnownRequestProcessed(gdi_display)+1))
      glXWaitX();
  }
  if (old_ctx && ctx == old_ctx->parent && ctx->p.stamp == old_ctx->stamp &&
      old_ctx->parent->owner_ctx == old_ctx->ctx && ctx->nsurf == ctx->lsurf &&
      tgt->draw == old_ctx->last_draw)
    return D3D_OK; /* already current */

  save_ctx = used_ctx = glXGetCurrentContext();
  save_draw = used_draw = glXGetCurrentDrawable();
  borked = FALSE;

  TRACE("target=%p\n", tgt);

  if (!tgt->draw) {
    TRACE("target has no drawable\n");
    return D3DERR_INVALID_DEVICE;
  }

  if (old_ctx && ctx != old_ctx->parent) {
    /* a different Direct3D context was current in this thread,
     * we need to deselect and possibly destroy it */
    LPD3DCONTEXT parent = old_ctx->parent;
    DWORD tid = parent->owner_tid;

    if (save_ctx && save_ctx == ctx->owner_ctx) borked = TRUE;
    else if (save_ctx && save_ctx == parent->owner_ctx && tid != GetCurrentThreadId()) borked = TRUE;
    else if (parent->borked) borked = TRUE;
    if (borked) TRACE("GL thread borkage detected (1)\n");

    save_ctx = old_ctx->save_ctx;
    save_draw = old_ctx->save_draw;

    if (used_ctx != ctx->owner_ctx) {
      if (!glXMakeCurrent(gdi_display, old_ctx->save_draw, old_ctx->save_ctx))
        ERR("couldn't clear context (1)\n");
      used_ctx = old_ctx->save_ctx;
      used_draw = old_ctx->save_draw;
      old_ctx->last_draw = 0;
      TRACE("cleared (1) to: ctx %p, draw %08lx\n", old_ctx->save_ctx, old_ctx->save_draw);
    } else TRACE("skipped clear (1)\n");

    if (tid == GetCurrentThreadId()) {
      parent->owner_tid = 0;
      parent->owner_unix_tid = 0;
    }
    if (&parent->p != old_ctx) {
      TRACE("killing slave context %p (ctx %p, parent=%p)\n", old_ctx, old_ctx->ctx, parent);
      if (old_ctx->prev) old_ctx->prev->next = old_ctx->next;
      if (old_ctx->next) old_ctx->next->prev = old_ctx->prev;
      if (tid == GetCurrentThreadId() && parent->owner_ctx != parent->p.ctx) {
        if (parent->p.ctx) glXDestroyContext(gdi_display, parent->p.ctx);
        parent->p.ctx = parent->owner_ctx;
      }
      parent->refs--;
      if (old_ctx->ctx && old_ctx->ctx != parent->p.ctx) glXDestroyContext(gdi_display, old_ctx->ctx);
      CloseHandle(old_ctx->thread);
      if (ctx->owner_tid) {
        /* now reuse old_ctx as new_ctx */
        memset(old_ctx, 0, sizeof(*old_ctx));
      } else {
        /* will use "main" context below */
        HeapFree(GetProcessHeap(), 0, old_ctx);
        new_ctx = NULL;
      }
    }
    else new_ctx = NULL;
    X11DRV_GLX_SetCurrentT(NULL);
    if (!parent->refs) {
      TRACE("killing master context %p (ctx %p)\n", parent, parent->p.ctx);
      if (used_ctx == parent->p.ctx) used_ctx = 0;
      if (parent->p.ctx) glXDestroyContext(gdi_display, parent->p.ctx);
      X11DRV_GLX_FreeContext(parent);
    }
    old_ctx = NULL;
  }
  else if (!old_ctx && save_ctx && save_ctx == ctx->owner_ctx) {
    TRACE("GL thread borkage detected (2), using: ctx %p, draw %08lx\n", ctx->p.save_ctx, ctx->p.save_draw);
    save_ctx = ctx->p.save_ctx;
    save_draw = ctx->p.save_draw;
    borked = TRUE;
  }
  if (ctx->borked) borked = TRUE;

  if (old_ctx && ctx->p.stamp != old_ctx->stamp) {
    /* "main" context replaced (by switch between direct and indirect rendering),
     * have to replace this thread's context too */
    if (!glXMakeCurrent(gdi_display, old_ctx->save_draw, old_ctx->save_ctx))
      ERR("couldn't clear context (2)\n");
    TRACE("cleared (2) to: ctx %p, draw %08lx\n", old_ctx->save_ctx, old_ctx->save_draw);
    used_ctx = old_ctx->save_ctx;
    used_draw = old_ctx->save_draw;
    old_ctx->last_draw = 0;
    if (old_ctx->ctx && old_ctx->ctx != old_ctx->parent->p.ctx) glXDestroyContext(gdi_display, old_ctx->ctx);
    new_ctx->ctx = 0;
    new_ctx->stamp = ctx->p.stamp;
    old_ctx = NULL;
  }

  if (!new_ctx) {
    /* use "main" context if unused */
    if (!ctx->owner_tid) {
      new_ctx = &ctx->p;
      borked = FALSE;
    } else new_ctx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(D3DTCONTEXT));
  }
  if (!new_ctx->parent) {
    TRACE("initializing slave context %p (parent=%p)\n", new_ctx, ctx);
    new_ctx->parent = ctx;
    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
                    &new_ctx->thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
    new_ctx->stamp = ctx->p.stamp;
    ctx->refs++;
    /* this is threadsafe because the X11/GL lock is held */
    new_ctx->prev = &ctx->p;
    new_ctx->next = ctx->p.next;
    if (new_ctx->next) new_ctx->next->prev = new_ctx;
    ctx->p.next = new_ctx;
  }

  TRACE("current on entry: ctx %p, draw %08lx\n", save_ctx, save_draw);
  if (!old_ctx) {
    if (borked) {
#ifdef ENABLE_BORKEN_MIGRATE
      /* this is the only way my nvidia drivers work (swapping contexts
       * with glXMakeCurrent a few times fails for some reason) */
      TRACE("trying to work around borkage with a single context\n");
      new_ctx->ctx = ctx->p.ctx;
#endif
      ctx->borked = TRUE;
    }
    new_ctx->save_ctx = save_ctx;
    new_ctx->save_draw = save_draw;
  }
  if (ctx->p.ctx && ctx->cant_copy && ctx->owner_tid != GetCurrentThreadId()) {
sig_migrate:
    /* when glXCopyContext doesn't work, fall back to the more dangerous signal method */
    if (ctx->owner_tid) {
      TRACE("requesting context from owner thread\n");
      /* invoke owner thread's X11DRV_GLX_UserSignal handler */
      wine_tkill(ctx->owner_unix_tid, SIGUSR1);
      do {
        int v;
        read(ctx->sync_pipe[0], &v, sizeof(v));
      } while (ctx->owner_tid);
      TRACE("context acquired\n");
      /* make sure the context is reset */
      used_ctx = 0; used_draw = 0;
    }
    new_ctx->ctx = ctx->owner_ctx;
  }
  if (new_ctx->ctx) {
    /* hmm, just re-establish this context? */
    if (ctx->owner_ctx && ctx->owner_ctx != new_ctx->ctx) {
      /* Direct3D context last used in another thread, try copying state from there */
      if (old_ctx) {
        save_ctx = old_ctx->save_ctx;
        save_draw = old_ctx->save_draw;
        if (!glXMakeCurrent(gdi_display, old_ctx->save_draw, old_ctx->save_ctx))
          ERR("couldn't clear context (4)\n");
        used_ctx = old_ctx->save_ctx;
        used_draw = old_ctx->save_draw;
        old_ctx->last_draw = 0;
        TRACE("cleared (4) to: ctx %p, draw %08lx\n", old_ctx->save_ctx, old_ctx->save_draw);
      }
      TRACE("copying context from %p to %p\n", ctx->owner_ctx, new_ctx->ctx);
      X11DRV_expect_error(gdi_display, X11DRV_expect_any, NULL);
      glXCopyContext(gdi_display, ctx->owner_ctx, new_ctx->ctx, GL_ALL_ATTRIB_BITS);
      XSync(gdi_display, False);
      if (X11DRV_check_error()) {
        ERR("context copy failed for mysterious reasons\n");
      }
    }
    if (used_ctx != new_ctx->ctx ||
        used_draw != tgt->draw) {
      BOOL ok;
      TRACE("resetting context %p, ctx %p, bmp %08x, draw %08lx\n", ctx, new_ctx->ctx, tgt->bmp, tgt->draw);
      ok = glXMakeCurrent(gdi_display, tgt->draw, new_ctx->ctx);
      new_ctx->last_draw = tgt->draw;
      ctx->owner_last_draw = tgt->draw;
      if (!ok) {
        ERR("failed to reset OpenGL context\n");
        TRACE("context remains: ctx %p, draw %08lx\n", glXGetCurrentContext(), glXGetCurrentDrawable());
        new_ctx->last_draw = glXGetCurrentDrawable();
        /* res = D3DERR_INVALID_DEVICE; */
      }
    }
  }
  while (!new_ctx->ctx) {
    BOOL ok;
    TRACE("creating context: sharedctx=%p, %s\n", ctx->owner_ctx, direct?"direct":"indirect");
    new_ctx->ctx = glXCreateContext(gdi_display, ctx->vis, ctx->owner_ctx, direct);
    if (!new_ctx->ctx) {
      WARN("unable to create OpenGL rendering context\n");
      res = D3DERR_INVALID_DEVICE;
      break;
    }
    if (ctx->owner_ctx && ctx->owner_ctx != new_ctx->ctx && ctx->owner_last_draw) {
      /* Direct3D context last used in another thread, try copying state from there */
      TRACE("copying context from %p to %p\n", ctx->owner_ctx, new_ctx->ctx);
      X11DRV_expect_error(gdi_display, X11DRV_expect_any, NULL);
      glXCopyContext(gdi_display, ctx->owner_ctx, new_ctx->ctx, GL_ALL_ATTRIB_BITS);
      XSync(gdi_display, False);
      if (X11DRV_check_error()) {
        /* if the copy is only for copy_aux, this is harmless, but if the app really
         * uses a different rendering thread, this could be bad */
        ERR("direct context copies rejected by OpenGL implementation, switching to signals\n");
        ctx->cant_copy = TRUE;
        glXDestroyContext(gdi_display, new_ctx->ctx);
        new_ctx->ctx = 0;
        pipe(ctx->sync_pipe);
        goto sig_migrate;
      }
    }
    TRACE("context %p, ctx %p, bmp %08x, draw %08lx, %s\n", ctx, new_ctx->ctx, tgt->bmp, tgt->draw, direct?"direct":"indirect");
    X11DRV_expect_error(gdi_display, X11DRV_expect_any, NULL);
    ok = glXMakeCurrent(gdi_display, tgt->draw, new_ctx->ctx);
    if (X11DRV_check_error()) {
      glXDestroyContext(gdi_display, new_ctx->ctx);
      new_ctx->ctx = 0;
      if (direct) {
#ifndef NO_INDIRECT
        if (ctx->owner_ctx) {
#endif
          ERR("direct rendering to GLXPixmap rejected by OpenGL implementation\n");
          res = D3DERR_INVALID_DEVICE;
          break;
#ifndef NO_INDIRECT
        }
        ERR("direct rendering to GLXPixmap rejected by OpenGL implementation, switching to indirect rendering\n");
        direct = FALSE;
#endif
      } else {
        ERR("rendering to GLXPixmap rejected by OpenGL implementation\n");
        res = D3DERR_INVALID_DEVICE;
        break;
      }
    }
    else if (!ok) {
      glXDestroyContext(gdi_display, new_ctx->ctx);
      new_ctx->ctx = 0;
      ERR("failed to set OpenGL context\n");
      res = D3DERR_INVALID_DEVICE;
      break;
    }
  }
  if (res == D3D_OK) {
    TRACE("initializing context\n");
    /* success - update state to reflect new render target */
    new_ctx->last_draw = tgt->draw;
    ctx->lsurf = ctx->nsurf;
    ctx->surf = ctx->lsurf ? ctx->lsurf->lpGbl : (LPDDRAWI_DDRAWSURFACE_GBL)NULL;
    /* cache results of direct/indirect check */
    if (direct) {
      if (tgt->flags & FLAG_PIXMAP) tgt->flags |= FLAG_PDIRECT;
      else tgt->flags |= FLAG_WDIRECT;
    } else {
      if (tgt->flags & FLAG_PIXMAP) tgt->flags |= FLAG_PINDIRECT;
      else tgt->flags |= FLAG_WINDIRECT;
    }
    /* choose front or back buffer for rendering */
    if (tgt->flags & FLAG_BACKBUF) {
      glDrawBuffer(GL_BACK);
      TRACE("rendering to GL_BACK\n");
    } else {
      glDrawBuffer(GL_FRONT);
      TRACE("rendering to GL_FRONT\n");
    }
    ctx->owner_tid = GetCurrentThreadId();
    ctx->owner_unix_tid = wine_gettid();
    ctx->owner_ctx = new_ctx->ctx;
    ctx->owner_last_draw = tgt->draw;
    X11DRV_GLX_SetCurrentT(new_ctx);
  } else {
    /* failure - revert state to old render target */
    TRACE("resetting context\n");
    if (ctx->lsurf && ctx->lsurf != ctx->nsurf)
      X11DRV_GLX_MakeTarget(ctx, ctx->lsurf);
  }

  return res;
}

void X11DRV_GLX_FlushCurrent(LPD3DCONTEXT ctx)
{
  LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);

#ifndef NO_SCENE_LOCK
  /* if in scene, do not flush */
  if (cur->lockedBmp) return;
#endif

  if (cur->flags & FLAG_PIXMAP) {
    /* render target is directly accessible by GDI,
     * do a full sync just in case */
    glXWaitGL();
  }
  else {
    /* render target is not directly accessible by GDI,
     * so any access would invoke copy_aux, which does
     * glFinish on its own, so we don't have to do
     * a full sync here, let's just flush */
    glFlush();
  }
}

static BOOL X11DRV_GLX_DoKillCurrent(BOOL dont_free)
{
  LPD3DTCONTEXT old_ctx = X11DRV_GLX_GetCurrentT();
  LPD3DCONTEXT parent;
  DWORD tid;
  BOOL ret = FALSE;

  if (!old_ctx) return FALSE;
  parent = old_ctx->parent;
  tid = parent->owner_tid;
  glXMakeCurrent(gdi_display, old_ctx->save_draw, old_ctx->save_ctx);
  old_ctx->last_draw = 0;
  TRACE("cleared to: ctx %p, draw %08lx\n", old_ctx->save_ctx, old_ctx->save_draw);
  if (tid == GetCurrentThreadId()) {
    parent->owner_tid = 0;
    parent->owner_unix_tid = 0;
  }
  if (dont_free) return TRUE;
  if (&parent->p != old_ctx) {
    TRACE("killing slave context %p (ctx %p, parent=%p)\n", old_ctx, old_ctx->ctx, parent);
    if (old_ctx->prev) old_ctx->prev->next = old_ctx->next;
    if (old_ctx->next) old_ctx->next->prev = old_ctx->prev;
    if (tid == GetCurrentThreadId() && parent->owner_ctx != parent->p.ctx) {
      if (parent->p.ctx) glXDestroyContext(gdi_display, parent->p.ctx);
      parent->p.ctx = parent->owner_ctx;
    }
    parent->refs--;
    if (old_ctx->ctx && old_ctx->ctx != parent->p.ctx) glXDestroyContext(gdi_display, old_ctx->ctx);
    CloseHandle(old_ctx->thread);
    HeapFree(GetProcessHeap(), 0, old_ctx);
  }
  else ret = TRUE;
  X11DRV_GLX_SetCurrentT(NULL);
  if (!parent->refs) {
    TRACE("killing master context %p (ctx %p)\n", parent, parent->p.ctx);
    if (parent->p.ctx) glXDestroyContext(gdi_display, parent->p.ctx);
    X11DRV_GLX_FreeContext(parent);
  }
  return ret;
}

BOOL X11DRV_GLX_KillCurrent(LPD3DCONTEXT ctx)
{
  BOOL ret;

  if (ctx != X11DRV_GLX_GetCurrent())
    return FALSE;

  ENTER_GL();
  ret = X11DRV_GLX_DoKillCurrent(FALSE);
  LEAVE_GL();
  return ret;
}

void X11DRV_GLX_UserSignal(CONTEXT* pctx)
{
  LPD3DCONTEXT ctx = X11DRV_GLX_GetCurrent();
  if (ctx) {
    int v = 0;
    WARN("deselecting GLX context from signal handler - can be risky\n");
    /* the X11/GL lock is held by the calling thread */
    /* and let's not free the context from here, it may be needed again
     * pretty quickly, and it may cause serious trouble to free resources
     * from inside a signal handler */
    X11DRV_GLX_DoKillCurrent(TRUE);
    if (ctx->cant_copy) write(ctx->sync_pipe[1], &v, sizeof(v));
  }
}

void X11DRV_GLX_Detach(void)
{
  LPD3DCONTEXT ctx = X11DRV_GLX_GetCurrent();
  /* if the TLS has been freed, then abort */
  if (X11DRV_GLX_TLS == INVALID_HANDLE_VALUE) return;
  if (ctx) X11DRV_GLX_KillCurrent(ctx);
}

#define DD_GL_EXT(x) "GL_"#x
static const char*const GL_ext_names[DD_GL_EXTENSION_COUNT] = {
  DD_GL_EXTENSION_LIST
};
#undef DD_GL_EXT

static DWORD PASCAL X11DRV_GLX_ContextCreate(LPD3DHAL_CONTEXTCREATEDATA data)
{
  int i, num;
  XVisualInfo template;
  LPD3DCONTEXT ctx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(D3DCONTEXT));
  const char *gl_exts;
  const char *glx_exts;
  const char *gl_vendor;
  const char *gl_renderer;
  HRESULT res;
  BOOL bNeedUserLock = data->u2.lpDDSLcl ? TRUE : FALSE;

  TRACE("creating 3D context %p\n", ctx);
  ctx->p.parent = ctx;
  DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
                  &ctx->p.thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
  ctx->refs = 1;
  /* FIXME: do this properly */

  if (bNeedUserLock) USER_Lock();   

  TRACE("locking render target\n");
  X11DRV_GLX_LockTarget(data->u2.lpDDSLcl, TRUE);
  ENTER_GL();

  if (data->u4.dwPID == (DWORD)-1 && !data->u1.lpDDLcl) {
    /* called from opengl32, not d3d...
     * check for shared context. */
    LPD3DCONTEXT share_ctx = (LPD3DCONTEXT)data->dwhContext;
    if (share_ctx) {
      /* this should make MakeCurrent create the context shared. I hope. */
      X11DRV_GLX_KillCurrent(share_ctx);
      ctx->owner_ctx = share_ctx->owner_ctx;
    }
  }

  /* check GLX version */
  glXQueryVersion(gdi_display, &ctx->glx_major, &ctx->glx_minor);
  TRACE("detected GLX version %d.%d\n", ctx->glx_major, ctx->glx_minor);
  if (ctx->glx_major > 1 || ctx->glx_minor >= 3) ctx->glx13 = TRUE;

  /* create context on root window first so we can check extensions
   * (in case we need pbuffer extensions to create first render target) */
  X11DRV_GLX_MakeTarget(ctx, NULL);
  res = X11DRV_GLX_MakeCurrent(ctx);

  /* check driver vendor */
  gl_vendor = glGetString(GL_VENDOR);
  TRACE("vendor: %s\n", gl_vendor);
  gl_renderer = glGetString(GL_RENDERER);
  TRACE("renderer: %s\n", gl_renderer);

  /* check GL and GLX extensions */
  gl_exts = glGetString(GL_EXTENSIONS);
  if (!gl_exts) ERR("failed to retrieve OpenGL extension list\n");
  for (i=0; i<DD_GL_EXTENSION_COUNT; i++) {
    ctx->gl_ext[i] = gl_exts ? (strstr(gl_exts, GL_ext_names[i]) != NULL) : FALSE;
  }

  if (HAVE_GL_EXT(ctx, ARB_multitexture)) {
    glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &ctx->texunits);
  }

#ifdef HAVE_GLX_GETPROCADDRESS
  {
    void (*extproc)(void);
#define DD_GL_PROC(x,y) extproc = glXGetProcAddressARB(#y); ctx->y = (x)extproc;
    DD_GL_PROC_LIST
#undef DD_GL_PROC
  }

  glx_exts = glXQueryExtensionsString(gdi_display, DefaultScreen(gdi_display));
  if (!glx_exts) ERR("failed to retrieve GLX extension list\n");
  else TRACE("GLX extensions: %s\n", glx_exts);

#ifdef ENABLE_PBUFFER
  ctx->glXGetFBConfigs          = (void*)glXGetProcAddressARB("glXGetFBConfigs");
  ctx->glXChooseFBConfig        = (void*)glXGetProcAddressARB("glXChooseFBConfig");
  ctx->glXGetFBConfigAttrib     = (void*)glXGetProcAddressARB("glXGetFBConfigAttrib");
  ctx->glXGetVisualFromFBConfig = (void*)glXGetProcAddressARB("glXGetVisualFromFBConfig");
  ctx->glXCreatePbuffer         = (void*)glXGetProcAddressARB("glXCreatePbuffer");
  ctx->glXDestroyPbuffer        = (void*)glXGetProcAddressARB("glXDestroyPbuffer");

  if (ctx->glx13 && ctx->glXCreatePbuffer) {
    TRACE("found GLX 1.3\n");
    ctx->glx13_pbuffers = TRUE;
  }
  else if (gl_vendor && strcmp(gl_vendor, "ATI Technologies Inc.") == 0) {
    /* the ATI FireGL drivers support pbuffers "in secret"
     * (gl queries (as in glxinfo) doesn't show it), so
     * we have to hardcode it to the vendor string */
    TRACE("found ATI FireGL\n");
    ctx->glx13_pbuffers = TRUE;
  }

  if (glx_exts && strstr(glx_exts, "GLX_SGIX_fbconfig")) {
    TRACE("found GLX_SGIX_fbconfig\n");
    ctx->glXGetFBConfigAttribSGIX         = (void*)glXGetProcAddressARB("glXGetFBConfigAttribSGIX");
    ctx->glXChooseFBConfigSGIX            = (void*)glXGetProcAddressARB("glXChooseFBConfigSGIX");
    ctx->glXCreateGLXPixmapWithConfigSGIX = (void*)glXGetProcAddressARB("glXCreateGLXPixmapWithConfigSGIX");
    ctx->glXCreateContextWithConfigSGIX   = (void*)glXGetProcAddressARB("glXCreateContextWithConfigSGIX");
    ctx->glXGetVisualFromFBConfigSGIX     = (void*)glXGetProcAddressARB("glXGetVisualFromFBConfigSGIX");
    ctx->glXGetFBConfigFromVisualSGIX     = (void*)glXGetProcAddressARB("glXGetFBConfigFromVisualSGIX");
  }
  if (glx_exts && strstr(glx_exts, "GLX_SGIX_pbuffer")) {
    TRACE("found GLX_SGIX_pbuffer\n");
    ctx->glXCreateGLXPbufferSGIX  = (void*)glXGetProcAddressARB("glXCreateGLXPbufferSGIX");
    ctx->glXDestroyGLXPbufferSGIX = (void*)glXGetProcAddressARB("glXDestroyGLXPbufferSGIX");
    ctx->glXQueryGLXPbufferSGIX   = (void*)glXGetProcAddressARB("glXQueryGLXPbufferSGIX");
    ctx->glXSelectEventSGIX       = (void*)glXGetProcAddressARB("glXSelectEventSGIX");
    ctx->glXGetSelectedEventSGIX  = (void*)glXGetProcAddressARB("glXGetSelectedEventSGIX");
  }
#endif
#endif

  /* now for the real context */
  if (data->u2.lpDDSLcl) {
    X11DRV_GLX_KillCurrent(ctx);
    glXDestroyContext(gdi_display, ctx->p.ctx);
    ctx->p.ctx = 0; ctx->owner_ctx = 0; ctx->owner_tid = 0; ctx->owner_unix_tid = 0; ctx->owner_last_draw = 0;
    template.visualid = XVisualIDFromVisual(visual);
    if (ctx->vis) XFree(ctx->vis);
    ctx->vis = XGetVisualInfo(gdi_display, VisualIDMask, &template, &num);

    TRACE("preparing render target\n");
    X11DRV_GLX_MakeTarget(ctx, data->u2.lpDDSLcl);
    res = X11DRV_GLX_MakeCurrent(ctx);
  }

  if (res == D3D_OK) {
    TRACE("preparing Z buffer\n");
    X11DRV_GLX_MakeZbuffer(ctx, data->u3.lpDDSZLcl);

    X11DRV_GLX_EB_Context_Init();

    X11DRV_GLX_SetupLogo(ctx);
  }

  glXWaitGL();
  LEAVE_GL();

  if (res == D3D_OK) {
    TRACE("created 3D context\n");
  } else {
    ERR("error creating 3D context\n");
    ENTER_GL();
    X11DRV_GLX_Destroy(ctx);
    LEAVE_GL();
    HeapFree(GetProcessHeap(),0,ctx);
    ctx = NULL;
  }
  X11DRV_GLX_LockTarget(data->u2.lpDDSLcl, FALSE);

  if (bNeedUserLock) USER_Unlock();

  data->dwhContext = (DWORD)ctx;
  data->ddrval = res;

  return DDHAL_DRIVER_HANDLED;
}

static void CALLBACK X11DRV_GLX_ContextDestroyer(ULONG_PTR the_arg)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)the_arg;
  LPD3DTCONTEXT this = X11DRV_GLX_GetCurrentT();
  BOOL mt;

  /* weed out redundant calls */
  if (!this) return;
  if (this->parent != ctx) return;
  if (!(X11DRV_GLX_GetFlags(ctx) & FLAG_DESTROYED)) return;
  /* ok, this looks like a valid request */
  mt = X11DRV_GLX_KillCurrent(ctx);
  TRACE("main: %d\n", mt);
}

static DWORD PASCAL X11DRV_GLX_ContextDestroy(LPD3DHAL_CONTEXTDESTROYDATA data)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)data->dwhContext;
  LPD3DTCONTEXT this = X11DRV_GLX_GetCurrentT();
  DWORD refs;
  BOOL mt;

  TRACE("destroyed 3D context %p\n", ctx);

  if (ctx) {
    X11DRV_GLX_LockTarget(ctx->lsurf, TRUE);
    ENTER_GL();
    mt = X11DRV_GLX_Destroy(ctx);
    refs = --ctx->refs;
    if (!refs) {
      TRACE("killing master context %p (ctx %p)\n", ctx, ctx->p.ctx);
      if (ctx->p.ctx) {
        glXDestroyContext(gdi_display, ctx->p.ctx);
        ctx->p.ctx = 0;
      }
    }
    X11DRV_GLX_GetCurTgt(ctx)->flags |= FLAG_DESTROYED;
    LEAVE_GL();
    X11DRV_GLX_LockTarget(ctx->lsurf, FALSE);
    TRACE("refs: %ld, main: %d\n", refs, mt);
    if (!refs) X11DRV_GLX_FreeContext(ctx);
    else {
      LPD3DTCONTEXT cur = &ctx->p;
      while (cur) {
        if (cur != this)
          QueueUserAPC(X11DRV_GLX_ContextDestroyer, cur->thread, (ULONG_PTR)ctx);
        cur = cur->next;
      }
    }
  }

  data->ddrval = DD_OK;
  return DDHAL_DRIVER_HANDLED;
}

static DWORD PASCAL X11DRV_GLX_ContextDestroyAll(LPD3DHAL_CONTEXTDESTROYALLDATA data)
{
  /* since this isn't a real display driver, we probably don't need to do anything here,
   * the kernel and the X server will clean up after Wine exits */
  data->ddrval = DD_OK;
  return DDHAL_DRIVER_HANDLED;
}

static void X11DRV_GLX_SetupLogo(LPD3DCONTEXT ctx)
{
#ifdef TRANSGAMING_LOGO
/* makedep is overzealous and doesn't obey the #ifdef. */
/*#include "logo.c"*/
#error "Uncomment the above line, and comment this one."

    ctx->logo_size = logo_size;

    glGenTextures(1, &ctx->logo);
    glBindTexture(GL_TEXTURE_2D, ctx->logo);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ctx->logo_size, ctx->logo_size,
		 0, GL_RGBA, GL_UNSIGNED_BYTE, logo);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);

    ctx->logo_list = glGenLists(1);

    glNewList(ctx->logo_list, GL_COMPILE);

    glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
    glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT
		 | GL_DEPTH_BUFFER_BIT | GL_TEXTURE_BIT | GL_TRANSFORM_BIT
		 | GL_VIEWPORT_BIT);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0.0, ctx->logo_size, ctx->logo_size, 0.0, -1, 1);

    glDisable(GL_ALPHA_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_DITHER);
    glDisable(GL_FOG);
    glDisable(GL_LIGHTING);
    glDisable(GL_STENCIL_TEST);
    glDisable(GL_SCISSOR_TEST);
    glMatrixMode(GL_TEXTURE);
    if (ctx->texunits > 1) {
	int i;
	for (i=0; i<ctx->texunits; i++) {
	    ctx->glActiveTextureARB(GL_TEXTURE0_ARB + i);
	    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	    glDisable(GL_TEXTURE_2D);

	    glPushMatrix();
	    glLoadIdentity();
	    glDisable(GL_TEXTURE_GEN_S);
	    glDisable(GL_TEXTURE_GEN_T);
	    glDisable(GL_TEXTURE_GEN_R);
	    glDisable(GL_TEXTURE_GEN_Q);
	}
	ctx->glActiveTextureARB(GL_TEXTURE0_ARB);
    }
    else
    {
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glPushMatrix();
	glLoadIdentity();
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_GEN_Q);
    }
    /* disable cubemapping, 3d */

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glDisable(GL_DEPTH_TEST);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glColor4f(1, 1, 1, 0.333);
    if (gluCheckExtension("GL_EXT_secondary_color",
			  glGetString(GL_EXTENSIONS)))
    {
	glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
	glSecondaryColor3fEXT(0, 0, 0);
    }

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, ctx->logo);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0);
    /* LOD bias shouldn't make a difference here. */

    glBegin(GL_QUADS);
    glTexCoord2f(0, 0);	glVertex2f(0, 0);
    glTexCoord2f(1, 0);	glVertex2f(ctx->logo_size, 0);
    glTexCoord2f(1, 1);	glVertex2f(ctx->logo_size, ctx->logo_size);
    glTexCoord2f(0, 1);	glVertex2f(0, ctx->logo_size);
    glEnd();

    glMatrixMode(GL_TEXTURE);
    if (ctx->texunits > 1) {
	int i;
	for (i=0; i<ctx->texunits; i++) {
	    ctx->glActiveTextureARB(GL_TEXTURE0_ARB + i);
	    glPopMatrix();
	}
	ctx->glActiveTextureARB(GL_TEXTURE0_ARB);
    }
    else
    {
	glPopMatrix();
    }
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glPopAttrib();
    glPopClientAttrib();

    glEndList();
#endif
}

#ifdef TRANSGAMING_LOGO
static void X11DRV_GLX_RenderLogo(LPD3DCONTEXT ctx)
{
    glPushAttrib(GL_VIEWPORT_BIT);
    glViewport(ctx->lsurf->lpGbl->wWidth - ctx->logo_size,
	       ctx->lsurf->lpGbl->wHeight - ctx->logo_size,
	       ctx->logo_size, ctx->logo_size);

    glCallList(ctx->logo_list);

    glPopAttrib();
}
#endif

static const GLenum GL_cube_targets[6] = {
  GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
  GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
  GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
};

static inline GLenum GL_tgt_load(LPDDRAWI_DDRAWSURFACE_LCL lcl)
{
  LPDDRAWI_DDRAWSURFACE_MORE more = lcl->lpSurfMore;
  if (more->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP) {
      unsigned u;
      for (u=0; u<6; u++)
        if (more->ddsCapsEx.dwCaps2 & (0x400 << u))
           return GL_cube_targets[u];
  }
  return GL_TEXTURE_2D;
}

static inline GLenum GL_tgt_par(LPDDRAWI_DDRAWSURFACE_LCL lcl)
{
  LPDDRAWI_DDRAWSURFACE_MORE more = lcl->lpSurfMore;
 if (more->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP)
    return GL_TEXTURE_CUBE_MAP_ARB;
  return GL_TEXTURE_2D;
}

static inline GLenum GL_tgt_bind(LPDDRAWI_DDRAWSURFACE_LCL lcl)
{
  LPDDRAWI_DDRAWSURFACE_MORE more = lcl->lpSurfMore;
  if (more->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP)
    return GL_TEXTURE_BINDING_CUBE_MAP_ARB;
  return GL_TEXTURE_BINDING_2D;
}

static void X11DRV_GLX_UpdateTexture(LPD3DCONTEXT ctx)
{
  /* now using nsurf because copy_aux may not restore lsurf immediately */
  LPDDRAWI_DDRAWSURFACE_LCL lcl = ctx->nsurf;
  if (!lcl) return; /* shouldn't happen */
  if ((lcl->ddsCaps.dwCaps & DDSCAPS_TEXTURE) &&
      (LPD3DCONTEXT)lcl->dwReserved1 == ctx) {
    LPDDRAWI_DDRAWSURFACE_GBL gbl = lcl->lpGbl;
    LPDDRAWI_DDRAWSURFACE_GBL_MORE gblm = GET_LPDDRAWSURFACE_GBL_MORE(gbl);
    if (gblm->dwDriverReserved) {
      GLint oldtex;
      GLenum target = GL_tgt_par(lcl);
      int level = 0;
      /* FIXME: detect level */
      TRACE("reloading texture %ld level %d from render target\n", gblm->dwDriverReserved, level);
      ENTER_GL();
      X11DRV_GLX_MakeCurrent(ctx);
      glGetIntegerv(GL_tgt_bind(lcl), &oldtex);
      glBindTexture(target, (GLuint)gblm->dwDriverReserved);
      if (X11DRV_GLX_Inverted(ctx, X11DRV_GLX_GetCurTgt(ctx))) {
        /* load texture from pbuffer */
        glReadBuffer(GL_FRONT);
        glCopyTexSubImage2D(GL_tgt_load(lcl), level, 0, 0, 0, 0,
                            gbl->wWidth, gbl->wHeight);
      } else {
        FIXME("must reload texture from surface data\n");
        /* must use glTexSubImage2D from the surface data */
      }
      glBindTexture(target, oldtex);
      if (!X11DRV_GLX_GetCurTgt(ctx)->lockedBmp) glFlush();
      LEAVE_GL();
    }
  }
}

/* Acquire all the locks needed during a scene capture */
static void AcquireRenderLocks(LPD3DCONTEXT ctx, BOOL draw)
{
  int s = DIB_Status_None, sz = DIB_Status_None;
  LPD3DTARGET cur = NULL, zbuf = NULL, cur_tmp;
  HBITMAP last_bitmap = 0;

  if (draw) {
#ifndef NO_SCENE_LOCK
    USER_Lock();
    X11DRV_GLX_MakeTarget(ctx, ctx->nsurf); /* used to be lsurf */
#endif
    do {
      cur = X11DRV_GLX_GetCurTgt(ctx);
      zbuf = X11DRV_GLX_GetCurZbuf(ctx);
      TRACE("Locking bitmap %08x (tgt=%p)\n", cur->bmp, cur);
      cur->lockedBmp = (BITMAPOBJ *)GDI_GetObjPtr(cur->bmp, BITMAP_MAGIC);
      cur_tmp = X11DRV_GLX_GetCurTgt(ctx);
      if (cur == cur_tmp) break;
      TRACE("Wrong target locked (race condition), trying again\n");
      if (last_bitmap)
        GDI_ReleaseObj(last_bitmap);
      last_bitmap = cur->bmp;
    } while (TRUE);
    if (last_bitmap)
      GDI_ReleaseObj(last_bitmap);
#ifdef DEFER_RENDER_COERCE
    s = X11DRV_DIB_Lock(cur->lockedBmp, DIB_Status_None, FALSE);
#else
    s = X11DRV_DIB_Lock(cur->lockedBmp, (cur->flags & FLAG_PIXMAP) ? DIB_Status_GdiMod : DIB_Status_AuxMod, FALSE);
#endif
    if (zbuf && zbuf->bmp) {
      zbuf->lockedBmp = (BITMAPOBJ *)GDI_GetObjPtr(zbuf->bmp, BITMAP_MAGIC);
      sz = X11DRV_DIB_Lock(zbuf->lockedBmp, DIB_Status_AuxMod, FALSE);
      GDI_ReleaseObj(zbuf->bmp);
    }
    GDI_ReleaseObj(cur->bmp);
#ifndef NO_SCENE_LOCK
    USER_Unlock();
#endif
  }
  ENTER_GL();
  X11DRV_GLX_MakeCurrent(ctx);
  if (draw) {
#ifndef DEFER_RENDER_COERCE
    if ((s != DIB_Status_AuxMod) && !(cur->flags & FLAG_PIXMAP)) X11DRV_GLX_CopyDIB(ctx, cur, 0, NULL);
#endif
    if (zbuf && zbuf->bmp && sz != DIB_Status_AuxMod) X11DRV_GLX_CopyDIB(ctx, zbuf, 0, NULL);
  }
}  

/* Release all the locks held during a scene capture */
static void ReleaseRenderLocks(LPD3DCONTEXT ctx, BOOL draw, LPD3DTARGET cur, LPD3DTARGET zbuf)
{
  if (draw) {
    cur->lockf = 0;
  }
  LEAVE_GL();
  if (draw) {
    TRACE("Unlocking bitmap %08x\n", cur->bmp);
    if (zbuf && zbuf->bmp) {
      X11DRV_DIB_Unlock(zbuf->lockedBmp, TRUE);
      zbuf->lockedBmp = NULL;
    }
    X11DRV_DIB_Unlock(cur->lockedBmp, TRUE);
    cur->lockedBmp = NULL;
  }
}

static DWORD PASCAL X11DRV_GLX_SceneCapture(LPD3DHAL_SCENECAPTUREDATA data)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)data->dwhContext;
#ifndef NO_SCENE_LOCK
  LPD3DTARGET cur, zbuf;
#endif

  switch (data->dwFlag) {
  case D3DHAL_SCENE_CAPTURE_START:
#ifdef NO_SCENE_LOCK
    USER_Lock();
    X11DRV_GLX_MakeTarget(ctx, ctx->nsurf); /* used to be lsurf */
    USER_Unlock();
#else
    AcquireRenderLocks(ctx, TRUE);
#endif
    ctx->in_scene = TRUE;
    break;
  case D3DHAL_SCENE_CAPTURE_END:
    ctx->in_scene = FALSE;
#ifndef NO_SCENE_LOCK
    cur = X11DRV_GLX_GetCurTgt(ctx);
    zbuf = X11DRV_GLX_GetCurZbuf(ctx);
#endif
#if 0 /* would need locking */
#  if defined( TRANSGAMING_LOGO )
    X11DRV_GLX_RenderLogo(ctx);
    /* glFinish(); */
#  endif 
#endif
    X11DRV_GLX_UpdateTexture(ctx);
#ifndef NO_SCENE_LOCK
    X11DRV_GLX_FlushCurrent(ctx);
    ReleaseRenderLocks(ctx, TRUE, cur, zbuf);
#endif
    break;
  }
  data->ddrval = DD_OK;
  return DDHAL_DRIVER_HANDLED;
}

static DWORD PASCAL X11DRV_GLX_SetRenderTarget(LPD3DHAL_SETRENDERTARGETDATA data)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)data->dwhContext;
  HRESULT res = D3DERR_INVALID_DEVICE;
  LPD3DTARGET cur, zbuf, other;
  BOOL rescene = FALSE;

  TRACE("target: %p, %p\n", data->u1.lpDDSLcl, data->u2.lpDDSZLcl);
  cur = X11DRV_GLX_GetCurTgt(ctx);
  if (ctx && ctx->in_scene) {
    TRACE("render target change while in scene, ending scene capture\n");
    zbuf = X11DRV_GLX_GetCurZbuf(ctx);
    rescene = TRUE;
    ctx->in_scene = FALSE;
    X11DRV_GLX_UpdateTexture(ctx);
#ifndef NO_SCENE_LOCK
    X11DRV_GLX_FlushCurrent(ctx);
    ReleaseRenderLocks(ctx, TRUE, cur, zbuf);
#endif
  }
  ENTER_GL();
  if (ctx) {
    X11DRV_GLX_MakeTarget(ctx, data->u1.lpDDSLcl);
    zbuf = X11DRV_GLX_GetCurZbuf(ctx);
    other = X11DRV_GLX_FindTarget(ctx, data->u2.lpDDSZLcl);
    if (other && zbuf != other) {
      WARN("same zbuffer used on more than one render target\n");
      /* FIXME: MakeCurrent and CleanTarget to save other zbuffer contents,
       * so that it can be restored on this zbuffer?
       * Since that might be slow, let's not until someone really needs it,
       * and then perhaps it could be made a config option */

      /* invalidate the other zbuffer since this one is now more current */
      X11DRV_GLX_DestroyTarget(ctx, other);
    }
    res = X11DRV_GLX_MakeCurrent(ctx);
    X11DRV_GLX_FlushCurrent(ctx);
    if (res == D3D_OK) {
      X11DRV_GLX_MakeZbuffer(ctx, data->u2.lpDDSZLcl);
    }
  }
  else {
    X11DRV_GLX_DoKillCurrent(FALSE);
  }
  LEAVE_GL();
  if (rescene) {
    TRACE("restarting scene capture\n");
#ifdef NO_SCENE_LOCK
    USER_Lock();
    X11DRV_GLX_MakeTarget(ctx, ctx->nsurf);
    USER_Unlock();
#else
    AcquireRenderLocks(ctx, TRUE);
#endif
    ctx->in_scene = TRUE;
  }

  data->ddrval = res;
  return DDHAL_DRIVER_HANDLED;
}

static LPDDHAL_CREATESURFACE X11DRV_GLX_old_create_surface;
static LPDDHALSURFCB_DESTROYSURFACE X11DRV_GLX_old_destroy_surface;
static LPDDHALSURFCB_FLIP X11DRV_GLX_old_flip;
static LPDDHALSURFCB_LOCK X11DRV_GLX_old_lock;
static LPDDHALSURFCB_UNLOCK X11DRV_GLX_old_unlock;

static DWORD PASCAL X11DRV_GLX_CreateSurface(LPDDHAL_CREATESURFACEDATA data)
{
  LPDDRAWI_DDRAWSURFACE_LCL lcl = *data->lplpSList;
  LPDDRAWI_DDRAWSURFACE_GBL gbl = lcl->lpGbl;
  LPDDRAWI_DDRAWSURFACE_GBL_MORE gblm = GET_LPDDRAWSURFACE_GBL_MORE(gbl);
  LPDDSURFACEDESC2 desc = (LPDDSURFACEDESC2)data->lpDDSurfaceDesc;
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)lcl->dwReserved1;

  if (desc->ddsCaps.dwCaps & DDSCAPS_TEXTURE) {
    /* only process late allocations (with a context) */
    if (ctx && !(desc->ddsCaps.dwCaps2 & DDSCAPS2_MIPMAPSUBLEVEL)) {
      ENTER_GL();
      X11DRV_GLX_MakeCurrent(ctx);
      glGenTextures(1, (GLuint*)&gblm->dwDriverReserved);
      LEAVE_GL();
      TRACE("generated texture %ld\n", gblm->dwDriverReserved);
      if (!gblm->dwDriverReserved)
        ERR("couldn't generate texture ID for %p\n", lcl);
    }
  }

  if (X11DRV_GLX_old_create_surface)
    return X11DRV_GLX_old_create_surface(data);
  else
    return DDHAL_DRIVER_NOTHANDLED;
}

static DWORD PASCAL X11DRV_GLX_DestroySurface(LPDDHAL_DESTROYSURFACEDATA data)
{
  LPDDRAWI_DDRAWSURFACE_LCL lcl = data->lpDDSurface;
  /* LPDDRAWI_DDRAWSURFACE_MORE lclm = lcl->lpSurfMore; */
  LPDDRAWI_DDRAWSURFACE_GBL gbl = lcl->lpGbl;
  LPDDRAWI_DDRAWSURFACE_GBL_MORE gblm = GET_LPDDRAWSURFACE_GBL_MORE(gbl);
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)lcl->dwReserved1;

  if (ctx) {
    LPD3DTARGET tgt = X11DRV_GLX_FindTarget(ctx, lcl);
    if ((lcl->ddsCaps.dwCaps & DDSCAPS_TEXTURE) &&
        (gblm->dwDriverReserved)) {
      TRACE("deleted texture %ld\n", gblm->dwDriverReserved);
      ENTER_GL();
      X11DRV_GLX_MakeCurrent(ctx);
      glDeleteTextures(1, (GLuint*)&gblm->dwDriverReserved);
      gblm->dwDriverReserved = 0;
      X11DRV_GLX_FlushCurrent(ctx);
      LEAVE_GL();
    }
    if (tgt) {
      TRACE("destroying a known render target\n");
#if 0 /* then again, since the DIB section will be destroyed, why update it */
      X11DRV_GLX_CleanTarget(ctx, tgt);
      /* since CleanTarget may have made tgt->draw current,
       * we now call MakeCurrent to try to deselect it again
       * (FIXME: KillCurrent should be called if it's still selected)
       */
#endif
      ENTER_GL();
      X11DRV_GLX_MakeCurrent(ctx);
      if (tgt == X11DRV_GLX_GetCurTgt(ctx)) {
        /* destroying current render target */
        /* we could try to choose a different render target
         * before cleaning up the context, but I'm lazy */
        TRACE("destroying current render target\n");

        /* clean up context since it may no longer be valid after this */
        X11DRV_GLX_CleanTargets(ctx);
        X11DRV_GLX_KillCurrent(ctx);
      }
      X11DRV_GLX_DestroyTarget(ctx, tgt);
      X11DRV_GLX_FlushCurrent(ctx);
      LEAVE_GL();
      /* do we need to do more? */
    }
  }

  if (X11DRV_GLX_old_destroy_surface)
    return X11DRV_GLX_old_destroy_surface(data);
  else
    return DDHAL_DRIVER_NOTHANDLED;
}

static DWORD PASCAL X11DRV_GLX_Flip(LPDDHAL_FLIPDATA data)
{
  if (data->lpSurfCurr == X11DRV_DD_Primary) {
    LPD3DCONTEXT ctx = (LPD3DCONTEXT)data->lpSurfTarg->dwReserved1;
    TRACE("front=%p, back=%p, ctx=%p\n", data->lpSurfCurr, data->lpSurfTarg, ctx);
    if (ctx && ctx->lsurf == data->lpSurfTarg &&
        !(X11DRV_GLX_GetFlags(ctx) & FLAG_OFFSCREEN)) {
      /* we have a windowed render target */
      int state;
      LPD3DTARGET front, back;
      LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
      LPD3DTARGET zbuf = X11DRV_GLX_GetCurZbuf(ctx);
      BITMAPOBJ *bmp = NULL;

      front = X11DRV_GLX_FindTarget(ctx, data->lpSurfCurr);
      back = X11DRV_GLX_FindTarget(ctx, data->lpSurfTarg);

      TRACE("targets: front=%p, back=%p, current=%p\n", front, back, cur);
      if (cur->lockedBmp) {
        /* don't know if this could cause problems or not */
        TRACE("calling Flip on surface locked for rendering\n");
        state = cur->dib ? cur->dib->status : DIB_Status_None;
        bmp = cur->lockedBmp;
      }
      else
      {
        bmp = (BITMAPOBJ *)GDI_GetObjPtr(cur->bmp, BITMAP_MAGIC);
        state = X11DRV_DIB_Lock(bmp, DIB_Status_None, FALSE);
      }

#ifdef ENABLE_DDRAW_FLIP
      if (state != DIB_Status_AuxMod || cur->rect_area) {
        /* should we force a GL flip or do an ordinary ddraw flip?
         * I'll do a ddraw flip since it is most often the fastest */
        X11DRV_DIB_Unlock(bmp, TRUE);
        GDI_ReleaseObj(cur->bmp);
        /* but if ddraw is going to make the flip, neither the front nor back
         * buffers can be left in GLX's custody, or much confusion will ensue */
        if (front) X11DRV_CoerceDIBSection2(front->bmp, DIB_Status_InSync, FALSE);
        if (back) X11DRV_CoerceDIBSection2(back->bmp, DIB_Status_InSync, FALSE);
        goto ddraw_flip;
      }
#endif

      TRACE("performing GLX flip: state = %d\n", state );

      ENTER_GL();
      X11DRV_GLX_MakeCurrent(ctx);
      /* If we're not GL synced then do so now before flipping the GL buffer */
      if (state != DIB_Status_AuxMod) {
        BITMAPOBJ *oldbmp;
        X11DRV_DIB_Coerce(bmp, DIB_Status_AuxMod, FALSE);
        oldbmp = cur->lockedBmp;
        cur->lockedBmp = bmp;
        X11DRV_GLX_CopyDIB(ctx, cur, 0, NULL);
        cur->lockedBmp = oldbmp;
      }

      if (cur->rect_area) {
        /* we need the whole render area for a GL flip */
        BITMAPOBJ *oldbmp = cur->lockedBmp;
        cur->lockedBmp = bmp;
        X11DRV_GLX_D3dRenderArea((ULONG_PTR)ctx, NULL, FALSE);
        cur->lockedBmp = oldbmp;
      }

      X11DRV_GLX_DisplayFPS(ctx, cur);

      glXSwapBuffers(gdi_display, cur->draw);
      /* This should not be strictly necessary, since glXSwapBuffers is supposed to do 
       * an implicit glFlush. Having this call tends to slow us down about 5-10%,
       * so we will remove it until the need for it is proven. 
       * if (!cur->lockedBmp) glXWaitGL(); */
      LEAVE_GL();
      TRACE("complete\n");

      /* cur->flags ^= FLAG_FLIPPED; */
      if (front) front->flags ^= FLAG_BACKBUF;
      if (back) back->flags ^= FLAG_BACKBUF;

      if (!cur->lockedBmp)
      {
        X11DRV_DIB_Unlock(bmp, TRUE);
        GDI_ReleaseObj(cur->bmp);
      } else {
	/* this is to guard against applications holding the scene capture locks
	 * too long and starving another thread that may be waiting for them.
	 * e.g. app starts SceneCapture, but doesn't end SceneCapture for
	 * a long period of time, instead updating the screen using GLX_Flip. */
	ReleaseRenderLocks(ctx, TRUE, cur, zbuf);
	AcquireRenderLocks(ctx, TRUE);
      }

      X11DRV_DD_PrimaryReset();

      return DDHAL_DRIVER_HANDLED;
    }
  }
#ifdef ENABLE_DDRAW_FLIP
ddraw_flip:
#endif
  if (X11DRV_GLX_old_flip)
    return X11DRV_GLX_old_flip(data);
  else
    return DDHAL_DRIVER_NOTHANDLED;
}

static DWORD PASCAL X11DRV_GLX_Lock(LPDDHAL_LOCKDATA data)
{
  LPDDRAWI_DDRAWSURFACE_LCL lcl = data->lpDDSurface;
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)lcl->dwReserved1;

  if (ctx) {
    LPD3DTARGET tgt = X11DRV_GLX_FindTarget(ctx, lcl);
    if (tgt) {
      BITMAPOBJ *bmp = (BITMAPOBJ *)GDI_GetObjPtr(tgt->bmp, BITMAP_MAGIC);
      int state = X11DRV_DIB_Lock(bmp, DIB_Status_None, FALSE);
      if (state != DIB_Status_AuxMod) {
        /* do the same as X11DRV_DDHAL_Lock */
        wine_tsx11_lock();
        if (NextRequest(gdi_display) != (LastKnownRequestProcessed(gdi_display)+1))
        {
          TRACE("performing GDI sync\n");
          XSync(gdi_display, False);
        }
        wine_tsx11_unlock();
      }
      TRACE("tgt=%p\n", tgt);
      tgt->locks++;
      if (tgt->locks == 1) {
        /* first lock */
        if (data->bHasRect && (state == DIB_Status_AuxMod)) {
          if (data->rArea.left > 0 || data->rArea.top > 0 ||
              data->rArea.right < bmp->bitmap.bmWidth ||
              data->rArea.bottom < bmp->bitmap.bmHeight) {
            TRACE("using rectlock\n");
            /* only the specified rectangle will be accessed by the app,
             * so the subsequent copy_aux only needs to copy this rect,
             * not the whole surface */
            tgt->rect_lock = TRUE;
            tgt->rect = data->rArea;
          }
        }
        tgt->lockf = (state == DIB_Status_AuxMod) ? data->dwFlags : (tgt->lockf & data->dwFlags);
      } else {
        if (tgt->rect_lock) {
          /* another lock, cancel the above optimization, must read everything */
          if (state == DIB_Status_AuxMod) tgt->rect_lock = FALSE; 
          else {
            FIXME("read entire framebuffer! (except already-locked region)\n");
          }
        }
        tgt->lockf = 0;
      }
      X11DRV_DIB_Unlock(bmp, TRUE);
      GDI_ReleaseObj(tgt->bmp);
      /* return here, don't move on to x11ddraw */
      return DDHAL_DRIVER_NOTHANDLED;
    }
  }
  if (X11DRV_GLX_old_lock)
    return X11DRV_GLX_old_lock(data);
  else
    return DDHAL_DRIVER_NOTHANDLED;
}

static DWORD PASCAL X11DRV_GLX_Unlock(LPDDHAL_UNLOCKDATA data)
{
  LPDDRAWI_DDRAWSURFACE_LCL lcl = data->lpDDSurface;
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)lcl->dwReserved1;
  if (ctx) {
    LPD3DTARGET tgt = X11DRV_GLX_FindTarget(ctx, lcl);
    if (tgt) {
      BITMAPOBJ *bmp = (BITMAPOBJ *)GDI_GetObjPtr(tgt->bmp, BITMAP_MAGIC);
      int state = X11DRV_DIB_Lock(bmp, DIB_Status_None, FALSE);
      BOOL ret = FALSE;
      TRACE("tgt=%p\n", tgt);
      if (tgt->locks) {
        tgt->locks--;
        if (tgt->locks == 0) {
          /* last lock - copy back to render target immediately if we just
           * locked a small rectangle (so the rest of the DIB section is invalid) */
          if (tgt->rect_lock) {
            if (state != DIB_Status_AuxMod) {
              /* write modified rectangle back */
              X11DRV_DIB_Coerce(bmp, DIB_Status_AuxMod, FALSE);
              if (tgt->lockf & DDLOCK_READONLY) {
                TRACE("read-only lock released\n");
                tgt->dib->status = DIB_Status_AuxMod;
              } else {
                LPDDRAWI_DDRAWSURFACE_LCL old = ctx->nsurf; /* was lsurf */
                LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
                BITMAPOBJ *oldbmp;
                ENTER_GL();
                if (old != lcl) X11DRV_GLX_MakeTarget(ctx, lcl);
                X11DRV_GLX_MakeCurrent(ctx);
                oldbmp = tgt->lockedBmp;
                tgt->lockedBmp = bmp;
                X11DRV_GLX_CopyDIB(ctx, tgt, 0, NULL);
                tgt->lockedBmp = oldbmp;
                if (old != lcl) {
                  X11DRV_GLX_MakeTarget(ctx, old);
                  if (cur->lockedBmp) X11DRV_GLX_MakeCurrent(ctx);
                }
                X11DRV_GLX_FlushCurrent(ctx);
                LEAVE_GL();
              }
            }
            tgt->lockf = 0;
          }
          else {
            if (state != DIB_Status_AuxMod && lcl == X11DRV_DD_Primary &&
                !(tgt->lockf & DDLOCK_READONLY)) {
              /* if primary surface was updated in 2D mode, do a normal
               * 2D ddraw update (some 3D games may choose to show movie
               * clips and stuff in 2D) */
              X11DRV_DD_Update();
            }
            /* tgt->lockf = 0; */
          }
          tgt->rect_lock = FALSE;
          /* if (!tgt->lockedBmp) tgt->lockf = 0; */
        }
        /* return after this, don't move on to x11ddraw */
        ret = TRUE;
      }
      X11DRV_DIB_Unlock(bmp, TRUE);
      GDI_ReleaseObj(tgt->bmp);
      if (ret) return DDHAL_DRIVER_NOTHANDLED;
    }
  }
  if (X11DRV_GLX_old_unlock)
    return X11DRV_GLX_old_unlock(data);
  else
    return DDHAL_DRIVER_NOTHANDLED;
}

BOOL X11DRV_GLX_D3dRenderLock(ULONG_PTR dwhContext, BOOL draw, LPBOOL invert)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)dwhContext;
  LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
  TRACE("(%p,%d)\n", ctx, draw);
#ifdef NO_SCENE_LOCK
  AcquireRenderLocks(ctx, draw);
#endif
  if (!draw) return FALSE;
  *invert = X11DRV_GLX_Inverted(ctx, cur);
  if (cur->rect_lock) { ERR("rectlock active - not good\n"); }
  if (draw && cur->lockedBmp) {
    if (cur->flags & FLAG_PIXMAP) {
      if (cur->dib->status != DIB_Status_GdiMod) {
        X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_GdiMod, FALSE);
        cur->lockf = 0;
      }
    }
    else if (cur->dib->status != DIB_Status_AuxMod) {
      if (cur->lockf & DDLOCK_READONLY) {
        TRACE("last lock was read-only\n");
        X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_AuxMod, FALSE);
        cur->dib->status = DIB_Status_AuxMod;
        cur->lockf = 0;
      } else {
        cur->lockf = 0;
#ifdef INCREMENTAL_READ
        return TRUE; /* request rect for D3dRenderArea */
#else
        X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_AuxMod, FALSE);
        X11DRV_GLX_CopyDIB(ctx, cur, 0, NULL);
#endif
      }
    }
    else if (cur->rect_area) {
      TRACE("using render rectlock\n");
#ifdef INCREMENTAL_READ
      return TRUE; /* request rect for D3dRenderArea */
#endif
    }
  }
  return FALSE;
}

void X11DRV_GLX_D3dRenderUnlock(ULONG_PTR dwhContext, BOOL draw)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)dwhContext;
  LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
  LPD3DTARGET zbuf = X11DRV_GLX_GetCurZbuf(ctx);
  TRACE("(%p,%d)\n", ctx, draw);
#ifndef DEFER_RENDER_COERCE
  if (draw && cur->rect_area) {
    TRACE("disabling render rectlock\n");
    X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_AppMod, FALSE);
    cur->rect_area = FALSE;
  }
#endif
#ifdef NO_SCENE_LOCK
  X11DRV_GLX_FlushCurrent(ctx);
  ReleaseRenderLocks(ctx, draw, cur, zbuf);
#endif
}

void X11DRV_GLX_D3dRenderArea(ULONG_PTR dwhContext, LPRECT rect, BOOL clear)
{
  LPD3DCONTEXT ctx = (LPD3DCONTEXT)dwhContext;
  LPD3DTARGET cur = X11DRV_GLX_GetCurTgt(ctx);
  RECT irect;
  TRACE("(%p,%p)\n", ctx, rect);
  if (rect) {
    TRACE(" rect: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
    if (X11DRV_GLX_Inverted(ctx, cur)) {
      irect.left = rect->left;
      irect.top = cur->dib->dibSection.dsBm.bmHeight - rect->bottom;
      irect.right = rect->right;
      irect.bottom = cur->dib->dibSection.dsBm.bmHeight - rect->top;
      rect = &irect;
      TRACE(" inverted: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
    }
  }
  if (cur->lockedBmp) {
    if (cur->rect_area) {
      RECTL def, unionr;
      LPRECTL nrect;
#ifdef INCREMENTAL_READ
      DWORD ca, na, ua;
      BOOL intersect;
#endif

      TRACE("render rectlock already established\n");
      if (cur->dib->status != DIB_Status_AuxMod) {
        /* shouldn't happen */
        ERR("render rectlock in use while not in AuxMod\n");
      }
      nrect = (RECTL*)rect;
      if (!nrect) {
        def.left = 0;
        def.top = 0;
        def.right = ctx->lsurf->lpGbl->wWidth;
        def.bottom = ctx->lsurf->lpGbl->wHeight;
        nrect = &def;
      }
      /* what's the combined rect */
      unionr.left = min(cur->rect.left, nrect->left);
      unionr.right = max(cur->rect.right, nrect->right);
      unionr.top = min(cur->rect.top, nrect->top);
      unionr.bottom = max(cur->rect.bottom, nrect->bottom);
#ifdef INCREMENTAL_READ
      /* do old and new rects overlap */
      intersect = cur->rect.left < nrect->right && 
                  nrect->left < cur->rect.right &&
                  cur->rect.top < nrect->bottom &&
                  nrect->top < cur->rect.bottom;
      /* what's the area of the rects */
      ca = (cur->rect.right-cur->rect.left)*(cur->rect.bottom-cur->rect.top);
      na = (nrect->right-nrect->left)*(nrect->bottom-nrect->top);
      ua = (unionr.right-unionr.left)*(unionr.bottom-unionr.top);
      if (ua > ((ca+na)*3) && !intersect) {
        /* seems combining is not worth it */
        TRACE("new area is far away, releasing old rectlock\n");
        X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_AppMod, FALSE);
        cur->rect_area = FALSE;
      } else {
        RECTL incr[4];
        DWORD c = 0;
        TRACE("accumulating new area\n");
        /* top */
        if (unionr.top < cur->rect.top) {
          incr[c].left = unionr.left;
          incr[c].top = unionr.top;
          incr[c].right = unionr.right;
          incr[c].bottom = cur->rect.top;
          c++;
        }
        /* left */
        if (unionr.left < cur->rect.left) {
          incr[c].left = unionr.left;
          incr[c].top = cur->rect.top;
          incr[c].right = cur->rect.left;
          incr[c].bottom = cur->rect.bottom;
          c++;
        }
        /* right */
        if (unionr.right > cur->rect.right) {
          incr[c].left = cur->rect.right;
          incr[c].top = cur->rect.top;
          incr[c].right = unionr.right;
          incr[c].bottom = cur->rect.bottom;
          c++;
        }
        /* bottom */
        if (unionr.bottom > cur->rect.bottom) {
          incr[c].left = unionr.left;
          incr[c].top = cur->rect.bottom;
          incr[c].right = unionr.right;
          incr[c].bottom = unionr.bottom;
          c++;
        }
        if (c) X11DRV_GLX_CopyDIB(ctx, cur, c, incr);
        cur->rect = unionr;
        if (unionr.left > 0 || unionr.top > 0 ||
            unionr.right < cur->dib->dibSection.dsBm.bmWidth ||
            unionr.bottom < cur->dib->dibSection.dsBm.bmHeight)
          return;
        /* if the whole surface is covered, render rectlock is no longer useful */
        cur->rect_area = FALSE;
        return;
      }
#else
      cur->rect = unionr;
      if (unionr.left > 0 || unionr.top > 0 ||
          unionr.right < cur->dib->dibSection.dsBm.bmWidth ||
          unionr.bottom < cur->dib->dibSection.dsBm.bmHeight)
        return;
      /* if the whole surface is covered, render rectlock is no longer useful */
      cur->rect_area = FALSE;
      return;
#endif
    }
    if ((cur->dib->status != DIB_Status_AuxMod) && !(cur->flags & FLAG_PIXMAP)) {
      X11DRV_DIB_Coerce(cur->lockedBmp, DIB_Status_AuxMod, FALSE);
      if (rect) {
        if (rect->left > 0 || rect->top > 0 ||
            rect->right < cur->dib->dibSection.dsBm.bmWidth ||
            rect->bottom < cur->dib->dibSection.dsBm.bmHeight) {
          TRACE("enabling render rectlock\n");
#ifdef INCREMENTAL_READ
          X11DRV_GLX_CopyDIB(ctx, cur, 1, (RECTL*)rect);
#else
          X11DRV_GLX_CopyDIB(ctx, cur, 0, NULL);
#endif
          cur->rect_area = TRUE;
          cur->rect = *(RECTL*)rect;
          return;
        }
      }
      X11DRV_GLX_CopyDIB(ctx, cur, 0, NULL);
    }
  }
}

static D3DHAL_CALLBACKS d3dhal_callbacks = {
  sizeof(D3DHAL_CALLBACKS),
  X11DRV_GLX_ContextCreate,
  X11DRV_GLX_ContextDestroy,
  X11DRV_GLX_ContextDestroyAll,
  X11DRV_GLX_SceneCapture,
  /* use lpReserved10 for something useful, like GetProcAddress */
#ifdef HAVE_GLX_GETPROCADDRESS
  glXGetProcAddressARB
#else
  NULL
#endif
};

static D3DHAL_CALLBACKS2 d3dhal_callbacks2 = {
  sizeof(D3DHAL_CALLBACKS2),
  0x1f, /* all callbacks are 32-bit */
  X11DRV_GLX_SetRenderTarget
};

static D3DHAL_GLOBALDRIVERDATA d3dhal_driverdata = {
  sizeof(D3DHAL_GLOBALDRIVERDATA),
  {	/* hwCaps */
   sizeof(D3DDEVICEDESC_V1),
   D3DDD_WINE_OPENGL_DEVICE
  },
  0,	/* dwNumVertices */
  0,	/* dwNumClipVertices */
  0,	/* dwNumTextureFormats */
  NULL	/* lpTextureFormats */
};

static X11DRIVERINFO driver_info[] = {
  {&GUID_D3DCallbacks2, sizeof(D3DHAL_CALLBACKS2), &d3dhal_callbacks2, NULL},
  {&GUID_WineGLD3DCallbacks, sizeof(struct GL_D3D_DriverCallbacks),
   &X11DRV_GLX_GL_D3D_driver_callbacks, NULL}
};

int X11DRV_GLX_CreateDriver(LPDDHALINFO info)
{
  LPX11DEVICE device = (LPX11DEVICE)info->lpPDevice;

  if (!glx_available) return 0;

  /* attach Direct3D callbacks */
  info->lpD3DGlobalDriverData = (ULONG_PTR)&d3dhal_driverdata;
  info->lpD3DHALCallbacks = (ULONG_PTR)&d3dhal_callbacks;
  info->lpDDExeBufCallbacks = &X11DRV_GLX_exebufcallbacks;

  /* add 3D caps bits */
  info->ddCaps.dwCaps |= DDCAPS_3D;
  info->ddCaps.dwCaps2 |= DDCAPS2_NO2DDURING3DSCENE;
  info->ddCaps.ddsCaps.dwCaps |= DDSCAPS_TEXTURE | DDSCAPS_3DDEVICE |
                                 DDSCAPS_ZBUFFER | DDSCAPS_MIPMAP |
                                 DDSCAPS_EXECUTEBUFFER | DDSCAPS_OPTIMIZED;

  /* attach our D3D callback structures to driver info linked list */
  driver_info[0].lpNext = driver_info+1;
  driver_info[1].lpNext = device->lpInfo;
  device->lpInfo = driver_info;

  /* chain some surface callbacks */
  X11DRV_GLX_old_create_surface = info->lpDDCallbacks->CreateSurface;
  X11DRV_GLX_old_destroy_surface = info->lpDDSurfaceCallbacks->DestroySurface;
  X11DRV_GLX_old_flip = info->lpDDSurfaceCallbacks->Flip;
  X11DRV_GLX_old_lock = info->lpDDSurfaceCallbacks->Lock;
  X11DRV_GLX_old_unlock = info->lpDDSurfaceCallbacks->Unlock;
  info->lpDDCallbacks->CreateSurface = X11DRV_GLX_CreateSurface;
  info->lpDDSurfaceCallbacks->DestroySurface = X11DRV_GLX_DestroySurface;
  info->lpDDSurfaceCallbacks->Flip = X11DRV_GLX_Flip;
  info->lpDDSurfaceCallbacks->Lock = X11DRV_GLX_Lock;
  info->lpDDSurfaceCallbacks->Unlock = X11DRV_GLX_Unlock;

  X11DRV_GLX_VAR_CreateDriver(info);

  return TRUE;
}
