/*
 * Copyright (C) 2004-2007 Claudio Ciccani <klan@directfb.org>
 *
 * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * 
 * Based on glfbdev.c, written by Brian Paul.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <pthread.h>

#include <directfb.h>
#include <directfb_version.h>

#include <directfbgl.h>

#include <direct/mem.h>
#include <direct/messages.h>
#include <direct/interface.h>

#undef CLAMP
#include "glheader.h"
#include "buffers.h"
#include "context.h"
#include "extensions.h"
#include "framebuffer.h"
#include "renderbuffer.h"
#include "imports.h"
#include "texformat.h"
#include "teximage.h"
#include "texstore.h"
#include "vbo/vbo.h"
#include "swrast/swrast.h"
#include "swrast_setup/swrast_setup.h"
#include "tnl/tnl.h"
#include "tnl/t_context.h"
#include "tnl/t_pipeline.h"
#include "drivers/common/driverfuncs.h"


#define VERSION_CODE( M, m, r )  (((M) * 1000) + ((m) * 100) + ((r)))
#define DIRECTFB_VERSION_CODE    VERSION_CODE( DIRECTFB_MAJOR_VERSION, \
                                               DIRECTFB_MINOR_VERSION, \
                                               DIRECTFB_MICRO_VERSION )


static DFBResult
Probe( void *data );

static DFBResult
Construct( IDirectFBGL      *thiz,
           IDirectFBSurface *surface );

#include <direct/interface_implementation.h>

DIRECT_INTERFACE_IMPLEMENTATION( IDirectFBGL, Mesa )

/*
 * private data struct of IDirectFBGL
 */
typedef struct {
     int                     ref;       /* reference counter */
     
     int                     locked;
     
     IDirectFBSurface       *surface;
     DFBSurfacePixelFormat   format;
     int                     width;
     int                     height;
     
     struct {
          GLubyte           *start;
          GLubyte           *end;
          int                pitch;
     } video;

     GLvisual                visual;
     GLframebuffer           framebuffer;
     GLcontext               context;
     struct gl_renderbuffer  render;
} IDirectFBGL_data;

/******************************************************************************/

static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned int    global_ref  = 0;

static INLINE int directfbgl_init( void )
{
     pthread_mutexattr_t attr;
     int                 ret;
     
     if (global_ref++)
          return 0;

     pthread_mutexattr_init( &attr );
     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
     ret = pthread_mutex_init( &global_lock, &attr );
     pthread_mutexattr_destroy( &attr );

     return ret;
}

static INLINE void directfbgl_finish( void )
{
     if (--global_ref == 0)
          pthread_mutex_destroy( &global_lock );
}

#define directfbgl_lock()    pthread_mutex_lock( &global_lock )
#define directfbgl_unlock()  pthread_mutex_unlock( &global_lock )

/******************************************************************************/

static bool  directfbgl_init_visual    ( GLvisual              *visual,
                                         DFBSurfacePixelFormat  format );
static bool  directfbgl_create_context ( GLcontext             *context,
                                         GLframebuffer         *framebuffer,
                                         GLvisual              *visual,
                                         IDirectFBGL_data      *data );
static void  directfbgl_destroy_context( GLcontext             *context,
                                         GLframebuffer         *framebuffer );

/******************************************************************************/


static void
IDirectFBGL_Mesa_Destruct( IDirectFBGL *thiz )
{
     IDirectFBGL_data *data = (IDirectFBGL_data*) thiz->priv;

     directfbgl_destroy_context( &data->context, &data->framebuffer );
     
     if (data->surface)
          data->surface->Release( data->surface );

     DIRECT_DEALLOCATE_INTERFACE( thiz );

     directfbgl_finish();
}

static DFBResult
IDirectFBGL_Mesa_AddRef( IDirectFBGL *thiz )
{
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL );

     data->ref++;

     return DFB_OK;
}

static DFBResult
IDirectFBGL_Mesa_Release( IDirectFBGL *thiz )
{
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL )

     if (--data->ref == 0) 
          IDirectFBGL_Mesa_Destruct( thiz );

     return DFB_OK;
}

static DFBResult
IDirectFBGL_Mesa_Lock( IDirectFBGL *thiz )
{
     IDirectFBSurface *surface;
     int               width   = 0;
     int               height  = 0;
     DFBResult         ret;
     
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL );

     if (data->locked) {
          data->locked++;
          return DFB_OK;
     }

     if (directfbgl_lock())
          return DFB_LOCKED;

     surface = data->surface;
     surface->GetSize( surface, &width, &height );
     
     ret = surface->Lock( surface, DSLF_READ | DSLF_WRITE, 
                          (void*)&data->video.start, &data->video.pitch );
     if (ret) {
          D_ERROR( "DirectFBGL/Mesa: couldn't lock surface.\n" );
          directfbgl_unlock();
          return ret;
     }
     data->video.end = data->video.start + (height-1) * data->video.pitch;

     data->render.Data = data->video.start;

     _mesa_make_current( &data->context, 
                         &data->framebuffer, &data->framebuffer );
     
     if (data->width != width || data->height != height) {
          _mesa_resize_framebuffer( &data->context, 
                                    &data->framebuffer, width, height );
          data->width  = width;
          data->height = height;                        
     }

     data->locked++;
     
     return DFB_OK;
}

static DFBResult
IDirectFBGL_Mesa_Unlock( IDirectFBGL *thiz )
{     
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL );

     if (!data->locked)
          return DFB_OK;
          
     if (--data->locked == 0) {
          _mesa_make_current( NULL, NULL, NULL );
     
          data->surface->Unlock( data->surface );

          directfbgl_unlock();
     }
     
     return DFB_OK;
}

static DFBResult
IDirectFBGL_Mesa_GetAttributes( IDirectFBGL     *thiz,
                                DFBGLAttributes *attributes )
{
     DFBSurfaceCapabilities   caps;
     GLvisual                *visual;
     
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL );

     if (!attributes)
          return DFB_INVARG;

     data->surface->GetCapabilities( data->surface, &caps );

     visual = &data->visual;
     
     attributes->buffer_size      = visual->rgbBits ? : visual->indexBits;
     attributes->depth_size       = visual->depthBits;
     attributes->stencil_size     = visual->stencilBits;
     attributes->aux_buffers      = visual->numAuxBuffers;
     attributes->red_size         = visual->redBits;
     attributes->green_size       = visual->greenBits;
     attributes->blue_size        = visual->blueBits;
     attributes->alpha_size       = visual->alphaBits;
     attributes->accum_red_size   = visual->accumRedBits;
     attributes->accum_green_size = visual->accumGreenBits;
     attributes->accum_blue_size  = visual->accumBlueBits;
     attributes->accum_alpha_size = visual->accumAlphaBits;
     attributes->double_buffer    = ((caps & DSCAPS_FLIPPING) != 0);
     attributes->stereo           = (visual->stereoMode != 0);

     return DFB_OK;
}

#if DIRECTFBGL_INTERFACE_VERSION >= 1
static DFBResult
IDirectFBGL_Mesa_GetProcAddress( IDirectFBGL  *thiz,
                                 const char   *name,
                                 void        **ret_address )
{
     DIRECT_INTERFACE_GET_DATA( IDirectFBGL );

     if (!name)
          return DFB_INVARG;
          
     if (!ret_address)
          return DFB_INVARG;
          
     *ret_address = _glapi_get_proc_address( name );
          
     return (*ret_address) ? DFB_OK : DFB_UNSUPPORTED;
}
#endif


/* exported symbols */

static DFBResult
Probe( void *data )
{
     return DFB_OK;
}

static DFBResult
Construct( IDirectFBGL *thiz, IDirectFBSurface *surface )
{
     DFBResult ret;
     
     /* Initialize global resources. */
     if (directfbgl_init())
          return DFB_INIT;
     
     /* Allocate interface data. */
     DIRECT_ALLOCATE_INTERFACE_DATA( thiz, IDirectFBGL );
 
     /* Initialize interface data. */
     data->ref = 1;

     /* Duplicate destination surface. */
     ret = surface->GetSubSurface( surface, NULL, &data->surface );
     if (ret) {
          IDirectFBGL_Mesa_Destruct( thiz );
          return ret;
     }

     data->surface->GetPixelFormat( data->surface, &data->format );
     data->surface->GetSize( data->surface, &data->width, &data->height );

     /* Configure visual. */
     if (!directfbgl_init_visual( &data->visual, data->format )) {
          D_ERROR( "DirectFBGL/Mesa: failed to initialize visual.\n" );
          IDirectFBGL_Mesa_Destruct( thiz );
          return DFB_UNSUPPORTED;
     }
     
     /* Create context. */
     if (!directfbgl_create_context( &data->context,
                                     &data->framebuffer,
                                     &data->visual, data )) {
          D_ERROR( "DirectFBGL/Mesa: failed to create context.\n" );
          IDirectFBGL_Mesa_Destruct( thiz );
          return DFB_UNSUPPORTED;
     }

     /* Assign interface pointers. */
     thiz->AddRef         = IDirectFBGL_Mesa_AddRef;
     thiz->Release        = IDirectFBGL_Mesa_Release;
     thiz->Lock           = IDirectFBGL_Mesa_Lock;
     thiz->Unlock         = IDirectFBGL_Mesa_Unlock;
     thiz->GetAttributes  = IDirectFBGL_Mesa_GetAttributes;
#if DIRECTFBGL_INTERFACE_VERSION >= 1
     thiz->GetProcAddress = IDirectFBGL_Mesa_GetProcAddress;
#endif 

     return DFB_OK;
}


/***************************** Driver functions ******************************/

static const GLubyte*
dfbGetString( GLcontext *ctx, GLenum pname )
{
     return NULL;
}

static void
dfbUpdateState( GLcontext *ctx, GLuint new_state )
{
     _swrast_InvalidateState( ctx, new_state );
     _swsetup_InvalidateState( ctx, new_state );
     _vbo_InvalidateState( ctx, new_state );
     _tnl_InvalidateState( ctx, new_state );
}

static void
dfbGetBufferSize( GLframebuffer *buffer, GLuint *width, GLuint *height )
{
     GLcontext        *ctx  = _mesa_get_current_context();
     IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;

     *width  = (GLuint) data->width;
     *height = (GLuint) data->height;
}

/**
 * We only implement this function as a mechanism to check if the
 * framebuffer size has changed (and update corresponding state).
 */
static void
dfbSetViewport( GLcontext *ctx, GLint x, GLint y, GLsizei w, GLsizei h )
{
     /* Nothing to do (the surface can't be resized while it's locked). */
     return;
}

static void
dfbClear( GLcontext *ctx, GLbitfield mask )
{
     IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
 
#define BUFFER_BIT_MASK (BUFFER_BIT_FRONT_LEFT | BUFFER_BIT_FRONT_RIGHT | \
                         BUFFER_BIT_BACK_LEFT  | BUFFER_BIT_BACK_RIGHT  )
     if (mask & BUFFER_BIT_MASK  &&
         ctx->Color.ColorMask[0] &&
         ctx->Color.ColorMask[1] &&
         ctx->Color.ColorMask[2] &&
         ctx->Color.ColorMask[3])
     {
          DFBRegion clip;
          GLubyte   a, r, g, b;
          
          UNCLAMPED_FLOAT_TO_UBYTE( a, ctx->Color.ClearColor[ACOMP] );
          UNCLAMPED_FLOAT_TO_UBYTE( r, ctx->Color.ClearColor[RCOMP] );
          UNCLAMPED_FLOAT_TO_UBYTE( g, ctx->Color.ClearColor[GCOMP] );
          UNCLAMPED_FLOAT_TO_UBYTE( b, ctx->Color.ClearColor[BCOMP] );

          clip.x1 = ctx->DrawBuffer->_Xmin;
          clip.y1 = ctx->DrawBuffer->_Ymin;
          clip.x2 = ctx->DrawBuffer->_Xmax - 1;
          clip.y2 = ctx->DrawBuffer->_Ymax - 1;
          data->surface->SetClip( data->surface, &clip );
          
          data->surface->Unlock( data->surface );
          
          data->surface->Clear( data->surface, r, g, b, a );
          
          data->surface->Lock( data->surface, DSLF_READ | DSLF_WRITE,
                               (void*)&data->video.start, &data->video.pitch );
          data->video.end = data->video.start + (data->height-1) * data->video.pitch;
          data->render.Data = data->video.start;
          
          mask &= ~BUFFER_BIT_MASK;
     }
#undef BUFFER_BIT_MASK
     
     if (mask)
          _swrast_Clear( ctx, mask );
}


/************************ RenderBuffer functions *****************************/

static void
dfbDeleteRenderbuffer( struct gl_renderbuffer *render )
{
     return;
}

static GLboolean
dfbRenderbufferStorage( GLcontext *ctx, struct gl_renderbuffer *render,
                        GLenum internalFormat, GLuint width, GLuint height )
{
     return GL_TRUE;
}


/***************************** Span functions ********************************/

/* RGB332 */
#define NAME(PREFIX) PREFIX##_RGB332
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLubyte *P = data->video.end - (Y) * data->video.pitch + (X);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[RCOMP]) & 0xe0)     ) | \
          (((S[GCOMP]) & 0xe0) >> 3) | \
          (((S[BCOMP])       ) >> 6) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0xe0)     ); \
   D[GCOMP] = ((*P & 0x1c) << 3); \
   D[BCOMP] = ((*P & 0x03) << 6); \
   D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"

/* ARGB4444 */
#define NAME(PREFIX) PREFIX##_ARGB4444
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL_RGB(P, X, Y, S) \
   *P = ( 0xf000                     | \
          (((S[RCOMP]) & 0xf0) << 4) | \
          (((S[GCOMP]) & 0xf0)     ) | \
          (((S[BCOMP]) & 0xf0) >> 4) )
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[ACOMP]) & 0xf0) << 8) | \
          (((S[RCOMP]) & 0xf0) << 4) | \
          (((S[GCOMP]) & 0xf0)     ) | \
          (((S[BCOMP]) & 0xf0) >> 4) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x0f00) >> 4) | ((*P & 0x0f00) >>  8); \
   D[GCOMP] = ((*P & 0x00f0)     ) | ((*P & 0x00f0) >>  4); \
   D[BCOMP] = ((*P & 0x000f) << 4) | ((*P & 0x000f)      ); \
   D[ACOMP] = ((*P & 0xf000) >> 8) | ((*P & 0xf000) >> 12)

#include "swrast/s_spantemp.h"

/* RGB444 */
#define NAME(PREFIX) PREFIX##_RGB444
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[RCOMP]) & 0xf0) << 4) | \
          (((S[GCOMP]) & 0xf0)     ) | \
          (((S[BCOMP]) & 0xf0) >> 4) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x0f00) >> 4) | ((*P & 0x0f00) >> 8); \
   D[GCOMP] = ((*P & 0x00f0)     ) | ((*P & 0x00f0) >> 4); \
   D[BCOMP] = ((*P & 0x000f) << 4) | ((*P & 0x000f)     ); \
   D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"

/* ARGB2554 */
#define NAME(PREFIX) PREFIX##_ARGB2554
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL_RGB(P, X, Y, S) \
   *P = ( 0xc000                     | \
          (((S[RCOMP]) & 0xf8) << 6) | \
          (((S[GCOMP]) & 0xf8) << 1) | \
          (((S[BCOMP]) & 0xf0) >> 4) )
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[ACOMP]) & 0xc0) << 8) | \
          (((S[RCOMP]) & 0xf8) << 6) | \
          (((S[GCOMP]) & 0xf8) << 1) | \
          (((S[BCOMP]) & 0xf0) >> 4) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x3e00) >>  9); \
   D[GCOMP] = ((*P & 0x01f0) >>  4); \
   D[BCOMP] = ((*P & 0x000f) <<  4); \
   D[ACOMP] = ((*P & 0xc000) >> 14)

#include "swrast/s_spantemp.h"
   
/* ARGB1555 */
#define NAME(PREFIX) PREFIX##_ARGB1555
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL_RGB(P, X, Y, S) \
   *P = ( 0x8000                      | \
          (((S[RCOMP]) & 0xf8) <<  7) | \
          (((S[GCOMP]) & 0xf8) <<  2) | \
          (((S[BCOMP])       ) >>  3) )
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[ACOMP]) & 0x80) << 16) | \
          (((S[RCOMP]) & 0xf8) <<  7) | \
          (((S[GCOMP]) & 0xf8) <<  2) | \
          (((S[BCOMP])       ) >>  3) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x7c00) >> 7) | ((*P & 0x7c00) >> 12); \
   D[GCOMP] = ((*P & 0x03e0) >> 2) | ((*P & 0x03e0) >>  7); \
   D[BCOMP] = ((*P & 0x001f) << 3) | ((*P & 0x001f) <<  2); \
   D[ACOMP] = ((*P & 0x8000) ? 0xff : 0)

#include "swrast/s_spantemp.h"

/* RGB555 */
#define NAME(PREFIX) PREFIX##_RGB555
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[RCOMP]) & 0xf8) <<  7) | \
          (((S[GCOMP]) & 0xf8) <<  2) | \
          (((S[BCOMP])       ) >>  3) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x7c00) >> 7) | ((*P & 0x7c00) >> 12); \
   D[GCOMP] = ((*P & 0x03e0) >> 2) | ((*P & 0x03e0) >>  7); \
   D[BCOMP] = ((*P & 0x001f) << 3) | ((*P & 0x001f) <<  2); \
   D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"

/* RGB16 */
#define NAME(PREFIX) PREFIX##_RGB16
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLushort *P = (GLushort *) (data->video.end - (Y) * data->video.pitch + (X) * 2);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[RCOMP]) & 0xf8) << 8) | \
          (((S[GCOMP]) & 0xfc) << 3) | \
          (((S[BCOMP])       ) >> 3) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0xf800) >> 8) | ((*P & 0xf800) >> 13); \
   D[GCOMP] = ((*P & 0x07e0) >> 3) | ((*P & 0x07e0) >>  9); \
   D[BCOMP] = ((*P & 0x001f) << 3) | ((*P & 0x001f) >>  2); \
   D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"

/* RGB24 */
#define NAME(PREFIX) PREFIX##_RGB24
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLubyte *P = data->video.end - (Y) * data->video.pitch + (X) * 3;
#define INC_PIXEL_PTR(P) P += 3
#define STORE_PIXEL(P, X, Y, S) \
   P[0] = S[BCOMP];  P[1] = S[GCOMP];  P[2] = S[BCOMP]
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = P[2];  D[GCOMP] = P[1];  D[BCOMP] = P[0]; D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"

/* RGB32 */
#define NAME(PREFIX) PREFIX##_RGB32
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLuint *P = (GLuint*) (data->video.end - (Y) * data->video.pitch + (X) * 4);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( ((S[RCOMP]) << 16) | \
          ((S[GCOMP]) <<  8) | \
          ((S[BCOMP])      ) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x00ff0000) >> 16); \
   D[GCOMP] = ((*P & 0x0000ff00) >>  8); \
   D[BCOMP] = ((*P & 0x000000ff)      ); \
   D[ACOMP] = 0xff

#include "swrast/s_spantemp.h"
   
/* ARGB */
#define NAME(PREFIX) PREFIX##_ARGB
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLuint *P = (GLuint*) (data->video.end - (Y) * data->video.pitch + (X) * 4);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL_RGB(P, X, Y, S) \
   *P = ( 0xff000000         | \
          ((S[RCOMP]) << 16) | \
          ((S[GCOMP]) <<  8) | \
          ((S[BCOMP])      ) )
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( ((S[ACOMP]) << 24) | \
          ((S[RCOMP]) << 16) | \
          ((S[GCOMP]) <<  8) | \
          ((S[BCOMP])      ) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] = ((*P & 0x00ff0000) >> 16); \
   D[GCOMP] = ((*P & 0x0000ff00) >>  8); \
   D[BCOMP] = ((*P & 0x000000ff)      ); \
   D[ACOMP] = ((*P & 0xff000000) >> 24)

#include "swrast/s_spantemp.h"

/* AiRGB */
#define NAME(PREFIX) PREFIX##_AiRGB
#define FORMAT GL_RGBA8
#define RB_TYPE GLubyte
#define SPAN_VARS \
   IDirectFBGL_data *data = (IDirectFBGL_data*) ctx->DriverCtx;
#define INIT_PIXEL_PTR(P, X, Y) \
   GLuint *P = (GLuint*) (data->video.end - (Y) * data->video.pitch + (X) * 4);
#define INC_PIXEL_PTR(P) P += 1
#define STORE_PIXEL_RGB(P, X, Y, S) \
   *P = ( ((S[RCOMP]) << 16) | \
          ((S[GCOMP]) <<  8) | \
          ((S[BCOMP])      ) )
#define STORE_PIXEL(P, X, Y, S) \
   *P = ( (((S[ACOMP]) ^ 0xff) << 24) | \
          (((S[RCOMP])       ) << 16) | \
          (((S[GCOMP])       ) <<  8) | \
          (((S[BCOMP])       )      ) )
#define FETCH_PIXEL(D, P) \
   D[RCOMP] =  ((*P & 0x00ff0000) >> 16); \
   D[GCOMP] =  ((*P & 0x0000ff00) >>  8); \
   D[BCOMP] =  ((*P & 0x000000ff)      ); \
   D[ACOMP] = (((*P & 0xff000000) >> 24) ^ 0xff)

#include "swrast/s_spantemp.h"


/*****************************************************************************/

static bool
directfbgl_init_visual( GLvisual              *visual,
                        DFBSurfacePixelFormat  format )
{
     GLboolean  rgbFlag        = GL_TRUE;
     GLboolean  dbFlag         = GL_FALSE;
     GLboolean  stereoFlag     = GL_FALSE;
     GLint      redBits        = 0;
     GLint      blueBits       = 0;
     GLint      greenBits      = 0;
     GLint      alphaBits      = 0;
     GLint      indexBits      = 0;
     GLint      depthBits      = 0;
     GLint      stencilBits    = 0;
     GLint      accumRedBits   = 0;
     GLint      accumGreenBits = 0;
     GLint      accumBlueBits  = 0;
     GLint      accumAlphaBits = 0;
     GLint      numSamples     = 0;

     /* FIXME: LUT8 support. */
     switch (format) {
          case DSPF_RGB332:
               redBits   = 3;
               greenBits = 3;
               blueBits  = 2;
               break;
          case DSPF_ARGB4444:
               alphaBits = 4;
          case DSPF_RGB444:
               redBits   = 4;
               greenBits = 4;
               blueBits  = 4;
               break;
          case DSPF_ARGB2554:
               alphaBits = 2;
               redBits   = 5;
               greenBits = 5;
               blueBits  = 4;
               break;
          case DSPF_ARGB1555:
               alphaBits = 1;
          case DSPF_RGB555:
               redBits   = 5;
               greenBits = 5;
               blueBits  = 5;
               break;
          case DSPF_RGB16:
               redBits   = 5;
               greenBits = 6;
               blueBits  = 5;
               break;
          case DSPF_ARGB:
          case DSPF_AiRGB:
               alphaBits = 8;
          case DSPF_RGB24:
          case DSPF_RGB32:
               redBits   = 8;
               greenBits = 8;
               blueBits  = 8;
               break;
          default:
               D_WARN( "unsupported pixelformat" );
               return false;
     }

     if (rgbFlag) {
          accumRedBits   = redBits;
          accumGreenBits = greenBits;
          accumBlueBits  = blueBits;
          accumAlphaBits = alphaBits;
          depthBits      = redBits + greenBits + blueBits;
          stencilBits    = alphaBits;
     } else
          depthBits      = 8;

     return _mesa_initialize_visual( visual,
                                     rgbFlag, dbFlag, stereoFlag,
                                     redBits, greenBits, blueBits, alphaBits,
                                     indexBits, depthBits, stencilBits,
                                     accumRedBits, accumGreenBits,
                                     accumBlueBits, accumAlphaBits,
                                     numSamples );
}

static bool
directfbgl_create_context( GLcontext        *context,
                           GLframebuffer    *framebuffer,
                           GLvisual         *visual,
                           IDirectFBGL_data *data )
{
     struct dd_function_table functions;
     
     _mesa_initialize_framebuffer( framebuffer, visual ); 
     
     _mesa_init_driver_functions( &functions );
     functions.GetString     = dfbGetString;
     functions.UpdateState   = dfbUpdateState;
     functions.GetBufferSize = dfbGetBufferSize;
     functions.Viewport      = dfbSetViewport;
     functions.Clear         = dfbClear;
     
     if (!_mesa_initialize_context( context, visual, NULL,
                                    &functions, (void*) data )) {
          D_DEBUG( "DirectFBGL/Mesa: _mesa_initialize_context() failed.\n" );
          _mesa_free_framebuffer_data( framebuffer );
          return false;
     }

     _swrast_CreateContext( context );
     _vbo_CreateContext( context );
     _tnl_CreateContext( context );
     _swsetup_CreateContext( context );
     _swsetup_Wakeup( context );

     _mesa_init_renderbuffer( &data->render, 0 );
     data->render.InternalFormat = GL_RGBA;
     data->render._BaseFormat    = GL_RGBA;
     data->render.DataType       = GL_UNSIGNED_BYTE;
     data->render.Data           = data->video.start;
     data->render.Delete         = dfbDeleteRenderbuffer;
     data->render.AllocStorage   = dfbRenderbufferStorage;
     
     switch (data->format) {
          case DSPF_RGB332:
               data->render.GetRow        = get_row_RGB332;
               data->render.GetValues     = get_values_RGB332;
               data->render.PutRow        = put_row_RGB332;
               data->render.PutRowRGB     = put_row_rgb_RGB332;
               data->render.PutMonoRow    = put_mono_row_RGB332;
               data->render.PutValues     = put_values_RGB332;
               data->render.PutMonoValues = put_mono_values_RGB332;
               break;
          case DSPF_ARGB4444: 
               data->render.GetRow        = get_row_ARGB4444;
               data->render.GetValues     = get_values_ARGB4444;
               data->render.PutRow        = put_row_ARGB4444;
               data->render.PutRowRGB     = put_row_rgb_ARGB4444;
               data->render.PutMonoRow    = put_mono_row_ARGB4444;
               data->render.PutValues     = put_values_ARGB4444;
               data->render.PutMonoValues = put_mono_values_ARGB4444;
               break;
          case DSPF_RGB444: 
               data->render.GetRow        = get_row_RGB444;
               data->render.GetValues     = get_values_RGB444;
               data->render.PutRow        = put_row_RGB444;
               data->render.PutRowRGB     = put_row_rgb_RGB444;
               data->render.PutMonoRow    = put_mono_row_RGB444;
               data->render.PutValues     = put_values_RGB444;
               data->render.PutMonoValues = put_mono_values_RGB444;
               break;
          case DSPF_ARGB2554: 
               data->render.GetRow        = get_row_ARGB2554;
               data->render.GetValues     = get_values_ARGB2554;
               data->render.PutRow        = put_row_ARGB2554;
               data->render.PutRowRGB     = put_row_rgb_ARGB2554;
               data->render.PutMonoRow    = put_mono_row_ARGB2554;
               data->render.PutValues     = put_values_ARGB2554;
               data->render.PutMonoValues = put_mono_values_ARGB2554;
               break;
          case DSPF_ARGB1555:
               data->render.GetRow        = get_row_ARGB1555;
               data->render.GetValues     = get_values_ARGB1555;
               data->render.PutRow        = put_row_ARGB1555;
               data->render.PutRowRGB     = put_row_rgb_ARGB1555;
               data->render.PutMonoRow    = put_mono_row_ARGB1555;
               data->render.PutValues     = put_values_ARGB1555;
               data->render.PutMonoValues = put_mono_values_ARGB1555;
               break;
          case DSPF_RGB555:
               data->render.GetRow        = get_row_RGB555;
               data->render.GetValues     = get_values_RGB555;
               data->render.PutRow        = put_row_RGB555;
               data->render.PutRowRGB     = put_row_rgb_RGB555;
               data->render.PutMonoRow    = put_mono_row_RGB555;
               data->render.PutValues     = put_values_RGB555;
               data->render.PutMonoValues = put_mono_values_RGB555;
               break;
          case DSPF_RGB16:
               data->render.GetRow        = get_row_RGB16;
               data->render.GetValues     = get_values_RGB16;
               data->render.PutRow        = put_row_RGB16;
               data->render.PutRowRGB     = put_row_rgb_RGB16;
               data->render.PutMonoRow    = put_mono_row_RGB16;
               data->render.PutValues     = put_values_RGB16;
               data->render.PutMonoValues = put_mono_values_RGB16;
               break;
          case DSPF_RGB24:
               data->render.GetRow        = get_row_RGB24;
               data->render.GetValues     = get_values_RGB24;
               data->render.PutRow        = put_row_RGB24;
               data->render.PutRowRGB     = put_row_rgb_RGB24;
               data->render.PutMonoRow    = put_mono_row_RGB24;
               data->render.PutValues     = put_values_RGB24;
               data->render.PutMonoValues = put_mono_values_RGB24;
               break;
          case DSPF_RGB32:
               data->render.GetRow        = get_row_RGB32;
               data->render.GetValues     = get_values_RGB32;
               data->render.PutRow        = put_row_RGB32;
               data->render.PutRowRGB     = put_row_rgb_RGB32;
               data->render.PutMonoRow    = put_mono_row_RGB32;
               data->render.PutValues     = put_values_RGB32;
               data->render.PutMonoValues = put_mono_values_RGB32;
               break;
          case DSPF_ARGB:
               data->render.GetRow        = get_row_ARGB;
               data->render.GetValues     = get_values_ARGB;
               data->render.PutRow        = put_row_ARGB;
               data->render.PutRowRGB     = put_row_rgb_ARGB;
               data->render.PutMonoRow    = put_mono_row_ARGB;
               data->render.PutValues     = put_values_ARGB;
               data->render.PutMonoValues = put_mono_values_ARGB;
               break;
          case DSPF_AiRGB:
               data->render.GetRow        = get_row_AiRGB;
               data->render.GetValues     = get_values_AiRGB;
               data->render.PutRow        = put_row_AiRGB;
               data->render.PutRowRGB     = put_row_rgb_AiRGB;
               data->render.PutMonoRow    = put_mono_row_AiRGB;
               data->render.PutValues     = put_values_AiRGB;
               data->render.PutMonoValues = put_mono_values_AiRGB;
               break;
          default:
               D_BUG( "unexpected pixelformat" );
               return false;
     }

     data->render.Width = data->width;
     data->render.Height = data->height;

     _mesa_add_renderbuffer( framebuffer, BUFFER_FRONT_LEFT, &data->render );
     
     _mesa_add_soft_renderbuffers( framebuffer,
                                   GL_FALSE,
                                   visual->haveDepthBuffer,
                                   visual->haveStencilBuffer,
                                   visual->haveAccumBuffer,
                                   GL_FALSE,
                                   GL_FALSE );

     TNL_CONTEXT( context )->Driver.RunPipeline = _tnl_run_pipeline;

     _mesa_enable_sw_extensions( context );
     
     return true;
}

static void
directfbgl_destroy_context( GLcontext     *context,
                            GLframebuffer *framebuffer )
{
     _swsetup_DestroyContext( context );
     _swrast_DestroyContext( context );
     _tnl_DestroyContext( context );
     _vbo_DestroyContext( context );
     //_mesa_free_framebuffer_data( framebuffer );
     _mesa_free_context_data( context );
}    
 
