#include "config.h"
#include "gdkprivate-fb.h"
#include <string.h>
#include <signal.h>
#include <sys/time.h>

/*
 * Reading pixel values from a generic drawable.
 */

static GetPixelRet
gdk_fb_drawable_get_color (GdkDrawable *drawable,
			   GdkGC *gc,
			   int x,
			   int y,
			   GdkColor *spot)
{
  GetPixelRet retval = GPR_NONE;
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;

  switch (private->depth)
    {
    case 1:
      {
	guchar foo = mem[(x >> 3) + y * rowstride];
	if (foo & (1 << (x % 8)))
	  *spot = GDK_GC_FBDATA (gc)->values.foreground;
	else
	  {
	    retval = GPR_USED_BG;
	    *spot = GDK_GC_FBDATA (gc)->values.background;
	  }
      }
      break;
    case 71:
      if (mem[x + y * rowstride])
	*spot = GDK_GC_FBDATA (gc)->values.foreground;
      else
	*spot = GDK_GC_FBDATA (gc)->values.background;
      break;
    case 77:
      retval = GPR_AA_GRAYVAL;
      spot->pixel = mem[x + y * rowstride] << 1;
      spot->red = spot->green = spot->blue = spot->pixel << 8;
      break;
    case 78: /* AA mode */
      retval = GPR_AA_GRAYVAL;
      spot->pixel = mem[x + y * rowstride];
      spot->red = spot->green = spot->blue = spot->pixel << 8;
      break;
    case 8:
      spot->pixel = mem[x + y * rowstride];
      *spot = private->colormap->colors[spot->pixel];
      break;
    case 16:
      {
	guint16 val16 = *((guint16 *)&mem[x*2 + y*rowstride]);

	spot->red = (((1<<gdk_display->modeinfo.red.length) - 1) & (val16 >> gdk_display->modeinfo.red.offset)) << (16 - gdk_display->modeinfo.red.length);
	spot->green = (((1<<gdk_display->modeinfo.green.length) - 1) & (val16 >> gdk_display->modeinfo.green.offset)) << (16 - gdk_display->modeinfo.green.length);
	spot->blue = (((1<<gdk_display->modeinfo.blue.length) - 1) & (val16 >> gdk_display->modeinfo.blue.offset)) << (16 - gdk_display->modeinfo.blue.length);

	spot->pixel = val16;
      }
      break;
    case 24:
      {
	guchar *smem = &mem[x*3 + y*rowstride];
	spot->red = smem[gdk_display->red_byte] << 8;
	spot->green = smem[gdk_display->green_byte] << 8;
	spot->blue = smem[gdk_display->blue_byte] << 8;
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
	spot->pixel = (smem[0]<<16)|(smem[1]<<8)|smem[2];
#else
	spot->pixel = smem[0]|(smem[1]<<8)|(smem[2]<<16);
#endif
      }
      break;
    case 32:
      {
	guchar *smem = &mem[x*4 + y*rowstride];
	spot->red = smem[gdk_display->red_byte] << 8;
	spot->green = smem[gdk_display->green_byte] << 8;
	spot->blue = smem[gdk_display->blue_byte] << 8;
	spot->pixel = *(guint32 *)smem;
      }
      break;
    }

  return retval;
}

/*************************************
 * gc->get_color() implementations
 *************************************/

static GetPixelRet
gdk_fb_get_color_1 (GdkDrawable *drawable,
		    GdkGC *gc,
		    int x,
		    int y,
		    GdkColor *color)
{
  GetPixelRet retval = GPR_NONE;
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guchar foo;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  foo = mem[(x >> 3) + y * rowstride];
  if (foo & (1 << (x % 8)))
    *color = GDK_GC_FBDATA (gc)->values.foreground;
  else
    {
      retval = GPR_USED_BG;

      *color = GDK_GC_FBDATA (gc)->values.background;
    }
  
  return retval;
}

static GetPixelRet
gdk_fb_get_color_8 (GdkDrawable *drawable,
		    GdkGC *gc,
		    int x,
		    int y,
		    GdkColor *color)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  gint pixel;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  pixel = mem[x + y * rowstride];
  *color = private->colormap->colors[pixel];

  return GPR_NONE;
}

static GetPixelRet
gdk_fb_get_color_16 (GdkDrawable *drawable,
		     GdkGC *gc,
		     int x,
		     int y,
		     GdkColor *color)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guint16 val16;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);

  val16 = *((guint16 *)&mem[x*2 + y*rowstride]);

  color->red = (((1<<gdk_display->modeinfo.red.length) - 1) & (val16 >> gdk_display->modeinfo.red.offset)) << (16 - gdk_display->modeinfo.red.length);
  color->green = (((1<<gdk_display->modeinfo.green.length) - 1) & (val16 >> gdk_display->modeinfo.green.offset)) << (16 - gdk_display->modeinfo.green.length);
  color->blue = (((1<<gdk_display->modeinfo.blue.length) - 1) & (val16 >> gdk_display->modeinfo.blue.offset)) << (16 - gdk_display->modeinfo.blue.length);

  color->pixel = val16;

  return GPR_NONE;
}

static GetPixelRet
gdk_fb_get_color_24 (GdkDrawable *drawable,
		     GdkGC *gc,
		     int x,
		     int y,
		     GdkColor *color)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guchar *smem;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);

  smem = &mem[x*3 + y*rowstride];
  color->red = smem[gdk_display->red_byte] << 8;
  color->green = smem[gdk_display->green_byte] << 8;
  color->blue = smem[gdk_display->blue_byte] << 8;
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
  color->pixel = (smem[0]<<16)|(smem[1]<<8)|smem[2];
#else
  color->pixel = smem[0]|(smem[1]<<8)|(smem[2]<<16);
#endif

  return GPR_NONE;
}

static GetPixelRet
gdk_fb_get_color_32 (GdkDrawable *drawable,
		     GdkGC *gc,
		     int x,
		     int y,
		     GdkColor *color)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guchar *smem;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  smem = &mem[x*4 + y*rowstride];
  color->red = smem[gdk_display->red_byte] << 8;
  color->green = smem[gdk_display->green_byte] << 8;
  color->blue = smem[gdk_display->blue_byte] << 8;
  color->pixel = *(guint32 *)smem;

  return GPR_NONE;
}

/*************************************
 * gc->set_pixel() implementations
 *************************************/

static void
gdk_fb_set_pixel_1(GdkDrawable *drawable,
		   GdkGC *gc,
		   int x,
		   int y,
		   gulong pixel)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guchar *ptr;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  ptr = mem + (y*rowstride) + (x >> 3);

  if (pixel)
    *ptr |= (1 << (x % 8));
  else
    *ptr &= ~(1 << (x % 8));
}

static void
gdk_fb_set_pixel_8(GdkDrawable *drawable,
		   GdkGC *gc,
		   int x,
		   int y,
		   gulong pixel)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);

  mem[x + y*rowstride] = pixel;
}

static void
gdk_fb_set_pixel_16(GdkDrawable *drawable,
		    GdkGC *gc,
		    int x,
		    int y,
		    gulong pixel)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guint16 *ptr;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  ptr = (guint16 *)&mem[x*2 + y*rowstride];
  *ptr = pixel;
}

static void
gdk_fb_set_pixel_24(GdkDrawable *drawable,
		    GdkGC *gc,
		    int x,
		    int y,
		    gulong pixel)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guchar *smem;
  
  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  smem = &mem[x*3 + y*rowstride];
  smem[0] = pixel & 0xff;
  smem[1] = (pixel >> 8) & 0xff;
  smem[2] = (pixel >> 16) & 0xff;
}

static void
gdk_fb_set_pixel_32(GdkDrawable *drawable,
		    GdkGC *gc,
		    int x,
		    int y,
		    gulong pixel)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  guchar *mem = private->mem;
  guint rowstride = private->rowstride;
  guint32 *smem;

  g_assert (private->depth == GDK_GC_FBDATA (gc)->depth);
  
  smem = (guint32 *)&mem[x*4 + y*rowstride];
  *smem = pixel;
}


/*************************************
 * gc->fill_span() implementations
 *************************************/

static void
gdk_fb_fill_span_generic (GdkDrawable *drawable,
			  GdkGC       *gc,
			  GdkSpan     *span,
			  GdkColor    *color)
{
  int curx;
  GdkColor spot = *color;
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  gint left, right, y;
  int clipxoff, clipyoff; /* Amounts to add to curx & cury to get x & y in clip mask */
  int tsxoff, tsyoff;
  GdkDrawable *cmask;
  guchar *clipmem;
  guint mask_rowstride;
  GdkPixmap *ts = NULL;
  GdkDrawableFBData *ts_private;
  gboolean solid_stipple;
  GdkFunction func;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  y = span->y;
  left = span->x;
  right = span->x + span->width;
  
  func = gc_private->values.function;
    
  cmask = gc_private->values.clip_mask;
  clipxoff = clipyoff = tsxoff = tsyoff = 0;
  mask_rowstride = 0;
  solid_stipple = FALSE;
  clipmem = NULL;
  
  if (cmask)
    {
      GdkDrawableFBData *cmask_private;
      
      cmask_private = GDK_DRAWABLE_IMPL_FBDATA (cmask);
      
      clipmem = cmask_private->mem;
      clipxoff = cmask_private->abs_x - gc_private->values.clip_x_origin - private->abs_x;
      clipyoff = cmask_private->abs_y - gc_private->values.clip_y_origin - private->abs_y;
      mask_rowstride = cmask_private->rowstride;
    }
  
  if (gc_private->values.fill == GDK_TILED &&
      gc_private->values.tile)
    {
      gint xstep;
      gint relx, rely;
      int drawh;
      GdkFBDrawingContext *dc, dc_data;

      dc = &dc_data;
      
      gdk_fb_drawing_context_init (dc, drawable, gc, FALSE, TRUE);
      
      ts = gc_private->values.tile;
      ts_private = GDK_DRAWABLE_IMPL_FBDATA (ts);
      
      rely = y - private->abs_y;
      drawh = (rely - gc_private->values.ts_y_origin) % ts_private->height;
      if (drawh < 0)
	drawh += ts_private->height;
      
      for (curx = left; curx < right; curx += xstep)
	{
	  int draww;
	  
	  relx = curx - private->abs_x;
	  
	  draww = (relx - gc_private->values.ts_x_origin) % ts_private->width;
	  if (draww < 0)
	    draww += ts_private->width;
	  
	  xstep = MIN (ts_private->width - draww, right - relx);
	  
	  gdk_fb_draw_drawable_3 (drawable, gc, GDK_DRAWABLE_IMPL (ts),
				  dc,
				  draww, drawh,
				  relx, rely,
				  xstep, 1);
	}
      
      gdk_fb_drawing_context_finalize (dc);
      
      return;
    }
  else if ((gc_private->values.fill == GDK_STIPPLED ||
	    gc_private->values.fill == GDK_OPAQUE_STIPPLED) &&
	   gc_private->values.stipple)
    {
      ts = gc_private->values.stipple;
      tsxoff = - GDK_DRAWABLE_IMPL_FBDATA (ts)->abs_x - gc_private->values.ts_x_origin - private->abs_x;
      tsyoff = - GDK_DRAWABLE_IMPL_FBDATA (ts)->abs_y - gc_private->values.ts_y_origin - private->abs_y;
      solid_stipple = (gc_private->values.fill == GDK_OPAQUE_STIPPLED);
    }
  
  for (curx = left; curx < right; curx++)
    {
      int maskx = curx+clipxoff, masky = y + clipyoff;
      guchar foo;
      
      if (cmask)
	{
	  foo = clipmem[masky*mask_rowstride + (maskx >> 3)];
	  
	  if (!(foo & (1 << (maskx % 8))))
	    continue;
	}
      
      if (func == GDK_INVERT)
	{
	  (gc_private->get_color) (drawable, gc, curx, y, &spot);
	  spot.pixel = ~spot.pixel;
	  spot.red = ~spot.red;
	  spot.green = ~spot.green;
	  spot.blue = ~spot.blue;
	}
      else if (func == GDK_XOR)
	{
	  (gc_private->get_color) (drawable, gc, curx, y, &spot);
	  spot.pixel ^= gc_private->values.foreground.pixel;
	}
      else if (func != GDK_COPY)
	{
	  g_warning ("Unsupported GdkFunction %d\n", func);
	}
      else if (ts)
	{
	  int wid, hih;
	  
	  ts_private = GDK_DRAWABLE_IMPL_FBDATA (ts);
	  
	  wid = ts_private->width;
	  hih = ts_private->height;
	  
	  maskx = (curx+tsxoff)%wid;
	  masky = (y+tsyoff)%hih;
	  if (maskx < 0)
	    maskx += wid;
	  if (masky < 0)
	    masky += hih;
	  
	  foo = ts_private->mem[(maskx >> 3) + ts_private->rowstride*masky];
	  if (foo & (1 << (maskx % 8)))
	    {
	      spot = gc_private->values.foreground;
	    }
	  else if (solid_stipple)
	    {
	      spot = gc_private->values.background;
	    }
	  else
	    continue;
	}
      
      (gc_private->set_pixel) (drawable, gc, curx, y, spot.pixel);
    }
}

static void
gdk_fb_fill_span_simple_1 (GdkDrawable *drawable,
			   GdkGC       *gc,
			   GdkSpan     *span,
			   GdkColor    *color)
{
  int curx;
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *mem, *ptr;
  guint rowstride;
  gint left, right, y;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  g_assert (!gc_private->values.clip_mask &&
	    !gc_private->values.tile &&
	    !gc_private->values.stipple &&
	    gc_private->values.function != GDK_INVERT);

  y = span->y;
  left = span->x;
  right = span->x + span->width;
  
  mem = private->mem;
  rowstride = private->rowstride;

  {
    int fromx = MIN ((left+7)&(~7), right);
    int begn = fromx - left, begoff = left % 8, endn;
    guchar begmask, endmask;
    int body_end = right & ~7;
    int body_len = (body_end - fromx)/8;
    
    begmask = ((1 << (begn + begoff)) - 1)
      & ~((1 << (begoff)) - 1);
    endn = right - body_end;
    endmask = (1 << endn) - 1;
    
    ptr = mem + y*rowstride + (left >> 3);
	
    if (color->pixel)
      *ptr |= begmask;
    else
      *ptr &= ~begmask;
    
    curx = fromx;
    
    if (curx < right)
      {
	ptr = mem + y*rowstride + (curx >> 3);
	memset (ptr, color->pixel?0xFF:0, body_len);
	
	if (endn)
	  {
	    ptr = mem + y*rowstride + (body_end >> 3);
	    if (color->pixel)
	      *ptr |= endmask;
	    else
	      *ptr &= ~endmask;
	  }
      }
  }
}

static void
gdk_fb_fill_span_simple_8 (GdkDrawable *drawable,
			   GdkGC       *gc,
			   GdkSpan     *span,
			   GdkColor    *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *mem, *ptr;
  guint rowstride;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  g_assert (!gc_private->values.clip_mask &&
	    !gc_private->values.tile &&
	    !gc_private->values.stipple &&
	    gc_private->values.function != GDK_INVERT);

  mem = private->mem;
  rowstride = private->rowstride;

  ptr = mem + span->y*rowstride + span->x;
  memset (ptr, color->pixel, span->width);
}
static void
gdk_fb_fill_span_simple_16 (GdkDrawable *drawable,
			    GdkGC       *gc,
			    GdkSpan     *span,
			    GdkColor    *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *mem;
  guint rowstride;
  guint16 *p16;
  int n;
  int i;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  g_assert (!gc_private->values.clip_mask &&
	    !gc_private->values.tile &&
	    !gc_private->values.stipple &&
	    gc_private->values.function != GDK_INVERT);

  mem = private->mem;
  rowstride = private->rowstride;

  n = span->width;
  p16 = (guint16 *)(mem + span->y * rowstride + span->x*2);
  for (i = 0; i < n; i++)
    *(p16++) = color->pixel;
}

static void
gdk_fb_fill_span_simple_24 (GdkDrawable *drawable,
			    GdkGC       *gc,
			    GdkSpan     *span,
			    GdkColor    *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *mem, *ptr;
  guint rowstride;
  int n;
  guchar redval, greenval, blueval;
  guchar *firstline, *ptr_end;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  g_assert (!gc_private->values.clip_mask &&
	    !gc_private->values.tile &&
	    !gc_private->values.stipple &&
	    gc_private->values.function != GDK_INVERT);

  mem = private->mem;
  rowstride = private->rowstride;

  redval = color->red>>8;
  greenval = color->green>>8;
  blueval = color->blue>>8;
    
  n = span->width*3;
    
  firstline = ptr = mem + span->y * rowstride + span->x*3;
  ptr_end = ptr+n;
  while (ptr < ptr_end)
    {
      ptr[gdk_display->red_byte] = redval;
      ptr[gdk_display->green_byte] = greenval;
      ptr[gdk_display->blue_byte] = blueval;
      ptr += 3;
    }
}
static void
gdk_fb_fill_span_simple_32 (GdkDrawable *drawable,
			    GdkGC       *gc,
			    GdkSpan     *span,
			    GdkColor    *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *mem;
  guint rowstride;
  guint32 *p32;
  int n;
  int i;

  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  g_assert (gc);

  g_assert (!gc_private->values.clip_mask &&
	    !gc_private->values.tile &&
	    !gc_private->values.stipple &&
	    gc_private->values.function != GDK_INVERT);

  mem = private->mem;
  rowstride = private->rowstride;

  n = span->width;
  p32 = (guint32 *)(mem + span->y * rowstride + span->x*4);
  for (i = 0; i < n; i++)
    *(p32++) = color->pixel;
}


/*************************************
 * gc->draw_drawable() implementations
 *************************************/

static void
gdk_fb_draw_drawable_generic (GdkDrawable *drawable,
			      GdkGC       *gc,
			      GdkPixmap   *src,
			      GdkFBDrawingContext *dc,
			      gint         start_y,
			      gint         end_y,
			      gint         start_x,
			      gint         end_x,
			      gint         src_x_off,
			      gint         src_y_off,
			      gint         draw_direction)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  int cur_x, cur_y;

  if (draw_direction < 0)
    {
      int tmp;
      tmp = start_y;
      start_y = end_y;
      end_y = tmp;
      start_y--;
      end_y--;
      
      tmp = start_x;
      start_x = end_x;
      end_x = tmp;
      start_x--;
      end_x--;
    }

  for (cur_y = start_y; cur_y != end_y; cur_y+=draw_direction)
    {
      for (cur_x = start_x; cur_x != end_x; cur_x+=draw_direction)
	{
	  GdkColor spot;
	  
	  if (GDK_GC_FBDATA(gc)->values.clip_mask)
	    {
	      int maskx = cur_x + dc->clipxoff, masky = cur_y + dc->clipyoff;
	      guchar foo;
	      
	      foo = dc->clipmem[masky*dc->clip_rowstride + (maskx >> 3)];
	      
	      if (!(foo & (1 << (maskx % 8))))
		continue;
	    }
	  
	  switch (gdk_fb_drawable_get_color (src, gc, cur_x + src_x_off, cur_y + src_y_off, &spot))
	    {
	    case GPR_AA_GRAYVAL:
	      {
		GdkColor realspot, fg;
		guint graylevel = spot.pixel;
		gint tmp;
		
		if (private->depth == 1)
		  {
		    if (spot.pixel > 192)
		      spot = GDK_GC_FBDATA (gc)->values.foreground;
		    else
		      spot = GDK_GC_FBDATA (gc)->values.background;
		    break;
		  }
		else
		  {
		    if (graylevel >= 254)
		      {
			spot = GDK_GC_FBDATA (gc)->values.foreground;
		      }
		    else if (graylevel <= 2)
		      {
			if (!dc->draw_bg)
			  continue;
			
			spot = GDK_GC_FBDATA (gc)->values.background;
		      }
		    else
		      {
			switch ((GDK_GC_FBDATA (gc)->get_color) (drawable, gc, cur_x, cur_y, &realspot))
			  {
			  case GPR_USED_BG:
			    {
			      int bgx, bgy;
			      
			      bgx = (cur_x - GDK_DRAWABLE_IMPL_FBDATA (dc->bg_relto)->abs_x) % GDK_DRAWABLE_IMPL_FBDATA (dc->bgpm)->width + private->abs_x;
			      bgy = (cur_y - GDK_DRAWABLE_IMPL_FBDATA (dc->bg_relto)->abs_y) % GDK_DRAWABLE_IMPL_FBDATA (dc->bgpm)->height + private->abs_y;
			      gdk_fb_drawable_get_color (dc->bgpm, gc, bgx, bgy, &realspot);
			    }
			    break;
			  case GPR_NONE:
			    break;
			  default:
			    g_assert_not_reached ();
			    break;
			  }
			
			fg = GDK_GC_FBDATA (gc)->values.foreground;

			/* Now figure out what 'spot' should actually look like */
			fg.red >>= 8;
			fg.green >>= 8;
			fg.blue >>= 8;
			realspot.red >>= 8;
			realspot.green >>= 8;
			realspot.blue >>= 8;

				
			tmp = (fg.red - realspot.red) * graylevel;
			spot.red = realspot.red + ((tmp + (tmp >> 8) + 0x80) >> 8);
			spot.red <<= 8;
			
			tmp = (fg.green - realspot.green) * graylevel;
			spot.green = realspot.green + ((tmp + (tmp >> 8) + 0x80) >> 8);
			spot.green <<= 8;
			
			tmp = (fg.blue - realspot.blue) * graylevel;
			spot.blue = realspot.blue + ((tmp + (tmp >> 8) + 0x80) >> 8);
			spot.blue <<= 8;
			
			/* Now find the pixel for this thingie */
			switch (private->depth)
			  {
			  case 8:
			    if (!gdk_colormap_alloc_color (private->colormap, &spot, FALSE, TRUE))
			      {
				g_error ("Can't allocate AA color!");
			      }
			    break;
			  case 16:
			    spot.pixel = (spot.red >> (16 - gdk_display->modeinfo.red.length)) << gdk_display->modeinfo.red.offset;
			    spot.pixel |= (spot.green >> (16 - gdk_display->modeinfo.green.length)) << gdk_display->modeinfo.green.offset;
			    spot.pixel |= (spot.blue >> (16 - gdk_display->modeinfo.blue.length)) << gdk_display->modeinfo.blue.offset;
			    break;
			  case 24:
			  case 32:
			    spot.pixel = ((spot.red & 0xFF00) >> 8  << (gdk_display->modeinfo.red.offset))
			      | ((spot.green & 0xFF00) >> 8 << (gdk_display->modeinfo.green.offset))
			      | ((spot.blue & 0xFF00) >> 8 << (gdk_display->modeinfo.blue.offset));
			    break;
			  }
		      }
		  }
	      }
	      break;
	    case GPR_USED_BG:
	      if (!dc->draw_bg)
		continue;
	      break;
	    case GPR_NONE:
	      break;
	    default:
	      g_assert_not_reached ();
	      break;
	    }
	  
	  (GDK_GC_FBDATA (gc)->set_pixel) (drawable, gc, cur_x, cur_y, spot.pixel);
	}
    }

}

void
gdk_fb_draw_drawable_memmove (GdkDrawable *drawable,
			      GdkGC       *gc,
			      GdkPixmap   *src,
			      GdkFBDrawingContext *dc,
			      gint         start_y,
			      gint         end_y,
			      gint         start_x,
			      gint         end_x,
			      gint         src_x_off,
			      gint         src_y_off,
			      gint         draw_direction)
{
  GdkDrawableFBData *src_private = GDK_DRAWABLE_FBDATA (src);
  guint depth = src_private->depth;
  guint src_rowstride = src_private->rowstride;
  guchar *srcmem = src_private->mem;
  int linelen = (end_x - start_x)*(depth>>3);
  gint cur_y;

  if (draw_direction < 0)
    {
      int tmp;
      tmp = start_y;
      start_y = end_y;
      end_y = tmp;
      start_y--;
      end_y--;
    }

  for(cur_y = start_y; cur_y != end_y; cur_y += draw_direction)
    {
      memmove (dc->mem + (cur_y * dc->rowstride) + start_x*(depth>>3),
	       srcmem + ((cur_y + src_y_off)*src_rowstride) + (start_x + src_x_off)*(depth>>3),
	       linelen);
    }

}

static void
gdk_fb_draw_drawable_aa_24 (GdkDrawable *drawable,
			    GdkGC       *gc,
			    GdkPixmap   *src,
			    GdkFBDrawingContext *dc,
			    gint         start_y,
			    gint         end_y,
			    gint         start_x,
			    gint         end_x,
			    gint         src_x_off,
			    gint         src_y_off,
			    gint         draw_direction)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  int x, y;
  GdkGCFBData *gc_private;
  guchar *dmem = private->mem;
  guint dst_rowstride = private->rowstride;
  guchar *smem = GDK_DRAWABLE_FBDATA (src)->mem;
  guint src_rowstride = GDK_DRAWABLE_FBDATA (src)->rowstride;
  guchar *dst;
  guint grayval;
  gint r, g, b, tmp;
  GdkColor fg;
  gint fg_r, fg_g, fg_b;
  
  gc_private = GDK_GC_FBDATA (gc);

  fg = GDK_GC_FBDATA (gc)->values.foreground;
  fg_r = fg.red >> 8;
  fg_g = fg.green >> 8;
  fg_b = fg.blue >> 8;

  if (draw_direction < 0)
    {
      int tmp;
      tmp = start_y;
      start_y = end_y;
      end_y = tmp;
      start_y--;
      end_y--;
      
      tmp = start_x;
      start_x = end_x;
      end_x = tmp;
      start_x--;
      end_x--;
    }

  for (y = start_y; y != end_y; y+=draw_direction)
    {
      for (x = start_x; x != end_x; x+=draw_direction)
	{
	  grayval = smem[x + src_x_off + (y + src_y_off) * src_rowstride];

	  if ((grayval <= 2) && (!dc->draw_bg))
	    continue;

	  dst = &dmem[x*3 + y*dst_rowstride];
	  
	  if (grayval >= 254)
	    {
	      dst[gdk_display->red_byte] = fg_r;
	      dst[gdk_display->green_byte] = fg_g;
	      dst[gdk_display->blue_byte] = fg_b;
	    }
	  else if (grayval <= 2)
	    {
	      dst[gdk_display->red_byte] = GDK_GC_FBDATA (gc)->values.background.red >> 8;
	      dst[gdk_display->green_byte] = GDK_GC_FBDATA (gc)->values.background.green >> 8;
	      dst[gdk_display->blue_byte] = GDK_GC_FBDATA (gc)->values.background.blue >> 8;
	    }
	  else
	    {
	      r = dst[gdk_display->red_byte];
	      tmp = (fg_r - r) * (gint)grayval;
	      r = r + ((tmp + (tmp >> 8) + 0x80) >> 8);
	      dst[gdk_display->red_byte] = r;

	      g = dst[gdk_display->green_byte];
	      tmp = (fg_g - g) * (gint)grayval;
	      g = g + ((tmp + (tmp >> 8) + 0x80) >> 8);
	      dst[gdk_display->green_byte] = g;

	      b = dst[gdk_display->blue_byte];
	      tmp = (fg_b - b) * (gint)grayval;
	      b = b + ((tmp + (tmp >> 8) + 0x80) >> 8);
	      dst[gdk_display->blue_byte] = b;
	    }
	}
    }
}

/*************************************
 * gc->fill_rectangle() implementations
 *************************************/

void
gdk_fb_fill_rectangle_generic (GdkDrawable    *drawable,
			       GdkGC          *gc,
			       GdkRectangle   *rect,
			       GdkColor       *color)
{
  GdkDrawableFBData *private;
  GdkSpan *spans;
  int i;

  private = GDK_DRAWABLE_FBDATA (drawable);

  spans = g_new (GdkSpan, rect->height);
  for (i = 0; i < rect->height; i++)
    {
      spans[i].x = rect->x - private->abs_x;
      spans[i].y = rect->y+i - private->abs_y;
      spans[i].width = rect->width;
    }
  gdk_fb_fill_spans (drawable, gc, spans, rect->height, TRUE);
  g_free (spans);
}

void
gdk_fb_fill_rectangle_simple_16 (GdkDrawable    *drawable,
				 GdkGC          *gc,
				 GdkRectangle   *rect,
				 GdkColor       *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *ptr;
  guint rowstride;
  int n;
  gboolean extra;
  int i;
  guint32 pixel;
  gint height;
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  rowstride = private->rowstride - rect->width*2;
  ptr = (guchar *)private->mem + rect->y * private->rowstride + rect->x*2;

  extra = rect->width&1;
  n = rect->width>>1;
    
  pixel = (color->pixel << 16) | color->pixel;

  height = rect->height;
  while (height>0)
    {
      i = n;
      while (i>0)
	{
	  *(guint32 *)ptr = pixel;
	  ptr += 4;
	  i--;
	}
      if (extra)
	{
	  *(guint16 *)ptr = color->pixel;
	  ptr += 2;
	}
      ptr += rowstride;
      height--; 
    }
}

void
gdk_fb_fill_rectangle_simple_32 (GdkDrawable    *drawable,
				 GdkGC          *gc,
				 GdkRectangle   *rect,
				 GdkColor       *color)
{
  GdkGCFBData *gc_private;
  GdkDrawableFBData *private;
  guchar *ptr;
  guint rowstride;
  int n;
  int i;
  guint32 pixel;
  gint height;
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  gc_private = GDK_GC_FBDATA (gc);

  rowstride = private->rowstride - rect->width*4;
  ptr = (guchar *)private->mem + rect->y * private->rowstride + rect->x*4;

  n = rect->width;
    
  pixel = color->pixel;

  height = rect->height;
  while (height>0)
    {
      i = n;
      while (i>0)
	{
	  *(guint32 *)ptr = pixel;
	  ptr += 4;
	  i--;
	}
      ptr += rowstride;
      height--; 
    }
}


/*************************************
 * GC state calculation
 *************************************/

void
_gdk_fb_gc_calc_state (GdkGC           *gc,
		       GdkGCValuesMask  changed)
{
  GdkGCFBData *gc_private;
  int i;

  gc_private = GDK_GC_FBDATA (gc);

  gc_private->fill_span = gdk_fb_fill_span_generic;
  gc_private->fill_rectangle = gdk_fb_fill_rectangle_generic;

  for (i = 0; i < GDK_NUM_FB_SRCBPP; i++)
    gc_private->draw_drawable[i] = gdk_fb_draw_drawable_generic;
  
  if (changed & _GDK_FB_GC_DEPTH)
    switch (gc_private->depth)
      {
      case 1:
	gc_private->set_pixel = gdk_fb_set_pixel_1;
	gc_private->get_color = gdk_fb_get_color_1;
	break;
      case 8:
	gc_private->set_pixel = gdk_fb_set_pixel_8;
	gc_private->get_color = gdk_fb_get_color_8;
	break;
      case 16:
	gc_private->set_pixel = gdk_fb_set_pixel_16;
	gc_private->get_color = gdk_fb_get_color_16;
	break;
      case 24:
	gc_private->set_pixel = gdk_fb_set_pixel_24;
	gc_private->get_color = gdk_fb_get_color_24;
	break;
      case 32:
	gc_private->set_pixel = gdk_fb_set_pixel_32;
	gc_private->get_color = gdk_fb_get_color_32;
	break;
      default:
	g_assert_not_reached ();
	break;
      }

  if (!gc_private->values.clip_mask)
    {
    switch (gc_private->depth)
      {
      case 8:
	gc_private->draw_drawable[GDK_FB_SRC_BPP_8] = gdk_fb_draw_drawable_memmove;
	break;
      case 16:
	gc_private->draw_drawable[GDK_FB_SRC_BPP_16] = gdk_fb_draw_drawable_memmove;
	break;
      case 24:
	gc_private->draw_drawable[GDK_FB_SRC_BPP_8_AA_GRAYVAL] = gdk_fb_draw_drawable_aa_24;
	gc_private->draw_drawable[GDK_FB_SRC_BPP_24] = gdk_fb_draw_drawable_memmove;
	break;
      case 32:
	gc_private->draw_drawable[GDK_FB_SRC_BPP_32] = gdk_fb_draw_drawable_memmove;
	break;
      }
    }
  
  if (!gc_private->values.clip_mask &&
      !gc_private->values.tile &&
      !gc_private->values.stipple &&
       gc_private->values.function == GDK_COPY)
    {
      switch (gc_private->depth)
	{
	case 1:
	  gc_private->fill_span = gdk_fb_fill_span_simple_1;
	  break;
	case 8:
	  gc_private->fill_span = gdk_fb_fill_span_simple_8;
	  break;
	case 16:
	  gc_private->fill_span = gdk_fb_fill_span_simple_16;
	  gc_private->fill_rectangle = gdk_fb_fill_rectangle_simple_16;
	  break;
	case 24:
	  gc_private->fill_span = gdk_fb_fill_span_simple_24;
	  break;
	case 32:
	  gc_private->fill_span = gdk_fb_fill_span_simple_32;
	  gc_private->fill_rectangle = gdk_fb_fill_rectangle_simple_32;
	  break;
	default:
	  g_assert_not_reached ();
	  break;
	}
    }
}

#ifdef ENABLE_SHADOW_FB
static void
gdk_shadow_fb_copy_rect_0 (gint x, gint y, gint width, gint height)
{
  guchar *dst, *src;
  gint depth;

  depth = gdk_display->modeinfo.bits_per_pixel / 8;

  dst = gdk_display->fb_mmap + x * depth + gdk_display->sinfo.line_length * y;
  src = gdk_display->fb_mem + x * depth + gdk_display->fb_stride * y;

  width = width*depth;
  while (height>0)
    {
      memcpy (dst, src, width);
      dst += gdk_display->sinfo.line_length;
      src += gdk_display->fb_stride;
      height--;
    }
}

static void
gdk_shadow_fb_copy_rect_90 (gint x, gint y, gint width, gint height)
{
  guchar *dst, *src, *pdst;
  gint depth;
  gint w;
  gint i;

  depth = gdk_display->modeinfo.bits_per_pixel / 8;

  src = gdk_display->fb_mem + x * depth + gdk_display->fb_stride * y;
  dst = gdk_display->fb_mmap + y * depth + gdk_display->sinfo.line_length * (gdk_display->fb_width - x - 1);

  while (height>0)
    {
      w = width;
      pdst = dst;
      while (w>0) {
	for (i = 0; i < depth; i++)
	  *pdst++ = *src++;
	pdst -= gdk_display->sinfo.line_length + depth;
	w--;
      }
      dst += depth;
      src += gdk_display->fb_stride - width * depth;
      height--;
    }
}

static void
gdk_shadow_fb_copy_rect_180 (gint x, gint y, gint width, gint height)
{
  guchar *dst, *src, *pdst;
  gint depth;
  gint w;
  gint i;

  depth = gdk_display->modeinfo.bits_per_pixel / 8;

  src = gdk_display->fb_mem + x * depth + gdk_display->fb_stride * y;
  dst = gdk_display->fb_mmap + (gdk_display->fb_width - x - 1) * depth + gdk_display->sinfo.line_length * (gdk_display->fb_height - y - 1) ;

  while (height>0)
    {
      w = width;
      pdst = dst;
      while (w>0) {
	for (i = 0; i < depth; i++)
	  *pdst++ = *src++;
	pdst -= 2 * depth;
	w--;
      }
      dst -= gdk_display->sinfo.line_length;
      src += gdk_display->fb_stride - width * depth;
      height--;
    }
}

static void
gdk_shadow_fb_copy_rect_270 (gint x, gint y, gint width, gint height)
{
  guchar *dst, *src, *pdst;
  gint depth;
  gint w;
  gint i;

  depth = gdk_display->modeinfo.bits_per_pixel / 8;

  src = gdk_display->fb_mem + x * depth + gdk_display->fb_stride * y;
  dst = gdk_display->fb_mmap + (gdk_display->fb_height - y - 1) * depth + gdk_display->sinfo.line_length * x;

  while (height>0)
    {
      w = width;
      pdst = dst;
      while (w>0) {
	for (i = 0; i < depth; i++)
	  *pdst++ = *src++;
	pdst += gdk_display->sinfo.line_length - depth;
	w--;
      }
      dst -= depth;
      src += gdk_display->fb_stride - width * depth;
      height--;
    }
}

static void (*shadow_copy_rect[4]) (gint x, gint y, gint width, gint height);

volatile gint refresh_queued = 0;
volatile gint refresh_x1, refresh_y1;
volatile gint refresh_x2, refresh_y2;

static void
gdk_shadow_fb_refresh (int signum)
{
  gint minx, miny, maxx, maxy;

  if (!refresh_queued)
    {
      struct itimerval timeout;
      /* Stop the timer */ 
      timeout.it_value.tv_sec = 0;
      timeout.it_value.tv_usec = 0;
      timeout.it_interval.tv_sec = 0;
      timeout.it_interval.tv_usec = 0;
      setitimer (ITIMER_REAL, &timeout, NULL);
      return;
    }
 
  
  minx = refresh_x1;
  miny = refresh_y1;
  maxx = refresh_x2;
  maxy = refresh_y2;
  refresh_queued = 0;

  /* clip x */
  if (minx < 0) {
    minx = 0;
    maxx = MAX (maxx, 0);
  }
  if (maxx >= gdk_display->fb_width) {
    maxx = gdk_display->fb_width-1;
    minx = MIN (minx, maxx);
  }
  /* clip y */
  if (miny < 0) {
    miny = 0;
    maxy = MAX (maxy, 0);
  }
  if (maxy >= gdk_display->fb_height) {
    maxy = gdk_display->fb_height-1;
    miny = MIN (miny, maxy);
  }
  
  (*shadow_copy_rect[_gdk_fb_screen_angle]) (minx, miny, maxx - minx + 1, maxy - miny + 1);
}

void
gdk_shadow_fb_stop_updates (void)
{
  struct itimerval timeout;

  refresh_queued = 0;

  /* Stop the timer */ 
  timeout.it_value.tv_sec = 0;
  timeout.it_value.tv_usec = 0;
  timeout.it_interval.tv_sec = 0;
  timeout.it_interval.tv_usec = 0;
  setitimer (ITIMER_REAL, &timeout, NULL);

  refresh_queued = 0;
}

void
gdk_shadow_fb_init (void)
{
  struct sigaction action;

  action.sa_handler = gdk_shadow_fb_refresh;
  sigemptyset (&action.sa_mask);
  action.sa_flags = 0;
  
  sigaction (SIGALRM, &action, NULL);

  shadow_copy_rect[GDK_FB_0_DEGREES] = gdk_shadow_fb_copy_rect_0;
  shadow_copy_rect[GDK_FB_90_DEGREES] = gdk_shadow_fb_copy_rect_90;
  shadow_copy_rect[GDK_FB_180_DEGREES] = gdk_shadow_fb_copy_rect_180;
  shadow_copy_rect[GDK_FB_270_DEGREES] = gdk_shadow_fb_copy_rect_270;
}

/* maxx and maxy are included */
void
gdk_shadow_fb_update (gint minx, gint miny, gint maxx, gint maxy)
{
  struct itimerval timeout;

  if (gdk_display->manager_blocked)
    return;
  
  g_assert (minx <= maxx);
  g_assert (miny <= maxy);

  if (refresh_queued)
    {
      refresh_x1 = MIN (refresh_x1, minx);
      refresh_y1 = MIN (refresh_y1, miny);
      refresh_x2 = MAX (refresh_x2, maxx);
      refresh_y2 = MAX (refresh_y2, maxy);
      refresh_queued = 1;
    }
  else
    {
      refresh_x1 = minx;
      refresh_y1 = miny;
      refresh_x2 = maxx;
      refresh_y2 = maxy;
      refresh_queued = 1;

      getitimer (ITIMER_REAL, &timeout);
      if (timeout.it_value.tv_usec == 0)
	{
	  timeout.it_value.tv_sec = 0;
	  timeout.it_value.tv_usec = 20000; /* 20 ms => 50 fps */
	  timeout.it_interval.tv_sec = 0;
	  timeout.it_interval.tv_usec = 20000; /* 20 ms => 50 fps */
	  setitimer (ITIMER_REAL, &timeout, NULL);
	}
    }
  
}
#else

void
gdk_shadow_fb_update (gint minx, gint miny, gint maxx, gint maxy)
{
}

void
gdk_shadow_fb_init (void)
{
}

#endif

