#include "gdkprivate-fb.h"
#include "mi.h"

#include <freetype/ftglyph.h>

#include <string.h>
#include <endian.h>
#ifndef __BYTE_ORDER
#error "endian.h needs to #define __BYTE_ORDER"
#endif

#ifndef g_alloca
#define g_alloca alloca
#endif

static void gdk_fb_drawable_set_pixel(GdkDrawable *drawable, GdkGC *gc, int x, int y, GdkColor *spot, gboolean abs_coords);
typedef enum { GPR_USED_BG, GPR_AA_GRAYVAL, GPR_NONE, GPR_ERR_BOUNDS } GetPixelRet;
static GetPixelRet gdk_fb_drawable_get_pixel(GdkDrawable *drawable, GdkGC *gc, int x, int y, GdkColor *spot,
					     gboolean abs_coords, GdkDrawable *bg_relto, GdkDrawable *bgpm);

void                gdk_fb_draw_rectangle (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   gint              filled,
					   gint              x,
					   gint              y,
					   gint              width,
					   gint              height);
static void         gdk_fb_draw_arc       (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   gint              filled,
					   gint              x,
					   gint              y,
					   gint              width,
					   gint              height,
					   gint              angle1,
					   gint              angle2);
static void         gdk_fb_draw_polygon   (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   gint              filled,
					   GdkPoint         *points,
					   gint              npoints);
static void         gdk_fb_draw_text      (GdkDrawable      *drawable,
					   GdkFont          *font,
					   GdkGC            *gc,
					   gint              x,
					   gint              y,
					   const gchar      *text,
					   gint              text_length);
static void         gdk_fb_draw_text_wc   (GdkDrawable      *drawable,
					   GdkFont          *font,
					   GdkGC            *gc,
					   gint              x,
					   gint              y,
					   const GdkWChar   *text,
					   gint              text_length);
static void         gdk_fb_draw_glyphs    (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   PangoFont        *font,
					   gint              x,
					   gint              y,
					   PangoGlyphString *glyphs);
void                gdk_fb_draw_drawable  (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   GdkPixmap        *src,
					   gint              xsrc,
					   gint              ysrc,
					   gint              xdest,
					   gint              ydest,
					   gint              width,
					   gint              height);
static void         gdk_fb_draw_image     (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   GdkImage         *image,
					   gint              xsrc,
					   gint              ysrc,
					   gint              xdest,
					   gint              ydest,
					   gint              width,
					   gint              height);
static void         gdk_fb_draw_points    (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   GdkPoint         *points,
					   gint              npoints);
static void         gdk_fb_draw_segments  (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   GdkSegment       *segs,
					   gint              nsegs);
static void         gdk_fb_draw_lines     (GdkDrawable      *drawable,
					   GdkGC            *gc,
					   GdkPoint         *points,
					   gint              npoints);
static GdkColormap* gdk_fb_get_colormap   (GdkDrawable      *drawable);
static void         gdk_fb_set_colormap   (GdkDrawable      *drawable,
					   GdkColormap      *colormap);
static gint         gdk_fb_get_depth      (GdkDrawable      *drawable);
static GdkVisual*   gdk_fb_get_visual     (GdkDrawable      *drawable);

static gpointer parent_class = NULL;

static void
gdk_fb_get_size(GdkDrawable *d, gint *width, gint *height)
{
  if(width)
    *width = GDK_DRAWABLE_P(d)->width;
  if(height)
    *height = GDK_DRAWABLE_P(d)->height;
}

static void
gdk_drawable_impl_fb_class_init (GdkDrawableFBClass *klass)
{
  GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  drawable_class->create_gc = _gdk_fb_gc_new;
  drawable_class->draw_rectangle = gdk_fb_draw_rectangle;
  drawable_class->draw_arc = gdk_fb_draw_arc;
  drawable_class->draw_polygon = gdk_fb_draw_polygon;
  drawable_class->draw_text = gdk_fb_draw_text;
  drawable_class->draw_text_wc = gdk_fb_draw_text_wc;
  drawable_class->draw_drawable = gdk_fb_draw_drawable;
  drawable_class->draw_points = gdk_fb_draw_points;
  drawable_class->draw_segments = gdk_fb_draw_segments;
  drawable_class->draw_lines = gdk_fb_draw_lines;
  drawable_class->draw_glyphs = gdk_fb_draw_glyphs;
  drawable_class->draw_image = gdk_fb_draw_image;
  
  drawable_class->set_colormap = gdk_fb_set_colormap;
  drawable_class->get_colormap = gdk_fb_get_colormap;
  
  drawable_class->get_size = gdk_fb_get_size;
 
  drawable_class->get_depth = gdk_fb_get_depth;
  drawable_class->get_visual = gdk_fb_get_visual;
  
  drawable_class->get_image = _gdk_fb_get_image;
}

GType
gdk_drawable_impl_fb_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
      static const GTypeInfo object_info =
      {
        sizeof (GdkDrawableFBClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gdk_drawable_impl_fb_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GdkDrawableFBData),
        0,              /* n_preallocs */
        (GInstanceInitFunc) NULL,
      };
      
      object_type = g_type_register_static (GDK_TYPE_DRAWABLE,
                                            "GdkDrawableFB",
                                            &object_info,
					    0);
    }
  
  return object_type;
}

/*****************************************************
 * FB specific implementations of generic functions *
 *****************************************************/

static GdkColormap*
gdk_fb_get_colormap (GdkDrawable *drawable)
{
  GdkColormap *retval = GDK_DRAWABLE_FBDATA(drawable)->colormap;

  if(!retval)
    retval = gdk_colormap_get_system();

  return retval;
}

static void
gdk_fb_set_colormap (GdkDrawable *drawable,
		     GdkColormap *colormap)
{
  GdkColormap *old_cmap;
  old_cmap = GDK_DRAWABLE_FBDATA(drawable)->colormap;
  GDK_DRAWABLE_FBDATA(drawable)->colormap = gdk_colormap_ref(colormap);
  gdk_colormap_unref(old_cmap);
}

/* Drawing
 */
GdkRegion *
gdk_fb_clip_region(GdkDrawable *drawable, GdkGC *gc, gboolean do_clipping, gboolean do_children)
{
  GdkRectangle draw_rect;
  GdkRegion *real_clip_region, *tmpreg;
  gboolean watchit = FALSE, skipit = FALSE;

  g_assert(!GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper) || !GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->input_only);

  draw_rect.x = GDK_DRAWABLE_FBDATA(drawable)->llim_x;
  draw_rect.y = GDK_DRAWABLE_FBDATA(drawable)->llim_y;
  if(!GDK_IS_WINDOW(GDK_DRAWABLE_FBDATA(drawable)) || GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->mapped)
    {
      draw_rect.width = GDK_DRAWABLE_FBDATA(drawable)->lim_x - draw_rect.x;
      draw_rect.height = GDK_DRAWABLE_FBDATA(drawable)->lim_y - draw_rect.y;
    }
  else
    {
      draw_rect.width = draw_rect.height = 0;
      skipit = TRUE;
    }
  real_clip_region = gdk_region_rectangle(&draw_rect);
  if(skipit)
    return real_clip_region;

  if(gc && GDK_GC_FBDATA(gc)->values.subwindow_mode == GDK_INCLUDE_INFERIORS)
    do_children = FALSE;

  if(do_clipping && GDK_IS_WINDOW(GDK_DRAWABLE_FBDATA(drawable)->wrapper) && GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->mapped && !GDK_WINDOW_P(GDK_DRAWABLE_FBDATA(drawable)->wrapper)->input_only)
    {
      GdkWindow *parentwin, *lastwin;

      lastwin = GDK_DRAWABLE_FBDATA(drawable)->wrapper;
      if(do_children)
	parentwin = lastwin;
      else
	parentwin = (GdkWindow *)GDK_WINDOW_P(lastwin)->parent;

      for(;
	  parentwin; lastwin = parentwin, parentwin = (GdkWindow *)GDK_WINDOW_P(parentwin)->parent)
	{
	  GList *cur;

	  for(cur = GDK_WINDOW_P(parentwin)->children; cur && cur->data != lastwin; cur = cur->next)
	    {
	      GdkRegion *reg2;

	      if(!GDK_WINDOW_P(cur->data)->mapped || GDK_WINDOW_P(cur->data)->input_only)
		continue;


	      draw_rect.x = GDK_DRAWABLE_IMPL_FBDATA(cur->data)->llim_x;
	      draw_rect.y = GDK_DRAWABLE_IMPL_FBDATA(cur->data)->llim_y;
	      draw_rect.width = GDK_DRAWABLE_IMPL_FBDATA(cur->data)->lim_x - draw_rect.x;
	      draw_rect.height = GDK_DRAWABLE_IMPL_FBDATA(cur->data)->lim_y - draw_rect.y;

	      tmpreg = gdk_region_rectangle(&draw_rect);

	      reg2 = gdk_region_copy(real_clip_region);
	      gdk_region_subtract(reg2, tmpreg);
	      if(watchit && !gdk_region_point_in(reg2, 100, 353))
		G_BREAKPOINT();
	      gdk_region_destroy(real_clip_region); real_clip_region = reg2;
	      gdk_region_destroy(tmpreg);
	    }
	}
    }

  if(gc)
    {
      if(GDK_GC_FBDATA(gc)->clip_region)
	{
	  tmpreg = gdk_region_copy(GDK_GC_FBDATA(gc)->clip_region);
	  gdk_region_offset(tmpreg, GDK_DRAWABLE_FBDATA(drawable)->abs_x + GDK_GC_P(gc)->clip_x_origin,
			    GDK_DRAWABLE_FBDATA(drawable)->abs_y + GDK_GC_P(gc)->clip_y_origin);
	  gdk_region_intersect(real_clip_region, tmpreg);
	  gdk_region_destroy(tmpreg);
	}

      if(GDK_GC_FBDATA(gc)->values.clip_mask)
	{
	  GdkDrawable *cmask = GDK_GC_FBDATA(gc)->values.clip_mask;

	  g_assert(GDK_DRAWABLE_IMPL_FBDATA(cmask)->depth == 1);
	  g_assert(GDK_DRAWABLE_IMPL_FBDATA(cmask)->abs_x == 0
		   && GDK_DRAWABLE_IMPL_FBDATA(cmask)->abs_y == 0);

	  draw_rect.x = GDK_DRAWABLE_FBDATA(drawable)->abs_x + GDK_DRAWABLE_IMPL_FBDATA(cmask)->llim_x + GDK_GC_FBDATA(gc)->values.clip_x_origin;
	  draw_rect.y = GDK_DRAWABLE_FBDATA(drawable)->abs_y + GDK_DRAWABLE_IMPL_FBDATA(cmask)->llim_y + GDK_GC_FBDATA(gc)->values.clip_y_origin;
	  draw_rect.width = GDK_DRAWABLE_IMPL_FBDATA(cmask)->width;
	  draw_rect.height = GDK_DRAWABLE_IMPL_FBDATA(cmask)->height;

	  tmpreg = gdk_region_rectangle(&draw_rect);
	  gdk_region_intersect(real_clip_region, tmpreg);
	  gdk_region_destroy(tmpreg);
	  if(!real_clip_region->numRects)
	    g_warning("Empty clip region");
	}
    }

  return real_clip_region;
}

static void
gdk_fb_fill_span(GdkDrawable *drawable, GdkGC *gc, GdkSegment *cur, GdkColor *color, GdkVisual *visual)
{
  int curx, cury;
  GdkColor spot = *color;

  if(gc
     && (GDK_GC_FBDATA(gc)->values.clip_mask
	 || GDK_GC_FBDATA(gc)->values.tile
	 || GDK_GC_FBDATA(gc)->values.stipple
	 || GDK_GC_FBDATA(gc)->values.function == GDK_INVERT))
    {
      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;
      gboolean solid_stipple;
      GdkFunction func = GDK_GC_FBDATA(gc)->values.function;

      cmask = GDK_GC_FBDATA(gc)->values.clip_mask;
      clipxoff = clipyoff = tsxoff = tsyoff = 0;
      mask_rowstride = 0;
      solid_stipple = FALSE;
      clipmem = NULL;
      if(cmask)
	{
	  clipmem = GDK_DRAWABLE_IMPL_FBDATA(cmask)->mem;
	  clipxoff = GDK_DRAWABLE_IMPL_FBDATA(cmask)->abs_x - GDK_GC_FBDATA(gc)->values.clip_x_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_x;
	  clipyoff = GDK_DRAWABLE_IMPL_FBDATA(cmask)->abs_y - GDK_GC_FBDATA(gc)->values.clip_y_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_y;
	  mask_rowstride = GDK_DRAWABLE_IMPL_FBDATA(cmask)->rowstride;
	}

      if(GDK_GC_FBDATA(gc)->values.fill == GDK_TILED
	 && GDK_GC_FBDATA(gc)->values.tile)
	{
	  gint xstep, ystep;
	  gint relx, rely;
	  GdkFBDrawingContext *dc, dc_data;
	  dc = &dc_data;

	  gdk_fb_drawing_context_init(dc, drawable, gc, FALSE, TRUE);

	  ts = GDK_GC_FBDATA(gc)->values.tile;
	  for(cury = cur->y1; cury < cur->y2; cury += ystep)
	    {
	      int drawh;
	      
	      rely = cury - GDK_DRAWABLE_FBDATA(drawable)->abs_y;
	      drawh = (rely + GDK_GC_FBDATA(gc)->values.ts_y_origin) % GDK_DRAWABLE_IMPL_FBDATA(ts)->height;
	      if(drawh < 0)
		drawh += GDK_DRAWABLE_P(ts)->height;

	      ystep = MIN(GDK_DRAWABLE_IMPL_FBDATA(ts)->height - drawh, cur->y2 - rely);

	      for(curx = cur->x1; curx < cur->x2; curx += xstep)
		{
		  int draww;

		  relx = curx - GDK_DRAWABLE_FBDATA(drawable)->abs_x;

		  draww = (relx + GDK_GC_FBDATA(gc)->values.ts_x_origin) % GDK_DRAWABLE_IMPL_FBDATA(ts)->width;
		  if(draww < 0)
		    draww += GDK_DRAWABLE_IMPL_FBDATA(ts)->width;

		  xstep = MIN(GDK_DRAWABLE_IMPL_FBDATA(ts)->width - draww, cur->x2 - relx);

		  gdk_fb_draw_drawable_3(drawable, gc, GDK_DRAWABLE_IMPL(ts),
					 dc,
					 draww, drawh,
					 relx, rely,
					 xstep, ystep);
		}
	    }

	  gdk_fb_drawing_context_finalize(dc);

	  return;
	}
      else if((GDK_GC_FBDATA(gc)->values.fill == GDK_STIPPLED
	      || GDK_GC_FBDATA(gc)->values.fill == GDK_OPAQUE_STIPPLED)
	      && GDK_GC_FBDATA(gc)->values.stipple)
	{
	  ts = GDK_GC_FBDATA(gc)->values.stipple;
	  tsxoff = GDK_DRAWABLE_FBDATA(ts)->abs_x - GDK_GC_FBDATA(gc)->values.ts_x_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_x;
	  tsyoff = GDK_DRAWABLE_FBDATA(ts)->abs_y - GDK_GC_FBDATA(gc)->values.ts_y_origin - GDK_DRAWABLE_FBDATA(drawable)->abs_y;
	  solid_stipple = (GDK_GC_FBDATA(gc)->values.fill == GDK_OPAQUE_STIPPLED);
	}

      for(cury = cur->y1; cury < cur->y2; cury++)
	{
	  for(curx = cur->x1; curx < cur->x2; curx++)
	    {
	      int maskx = curx+clipxoff, masky = cury + clipyoff;
	      guchar foo;

	      if(cmask)
		{
		  foo = clipmem[masky*mask_rowstride + (maskx >> 3)];

		  if(!(foo & (1 << (maskx % 8))))
		    continue;
		}

	      if(func == GDK_INVERT)
		{
		  gdk_fb_drawable_get_pixel(drawable, gc, curx, cury, &spot, TRUE, NULL, NULL);
		  spot.pixel = ~spot.pixel;
		  spot.red = ~spot.red;
		  spot.green = ~spot.green;
		  spot.blue = ~spot.blue;
		}
	      else if(ts)
		{
		  int wid = GDK_DRAWABLE_IMPL_FBDATA(ts)->width, hih = GDK_DRAWABLE_IMPL_FBDATA(ts)->height;

		  maskx = (curx+tsxoff)%wid;
		  masky = (cury+tsyoff)%hih;
		  if(maskx < 0)
		    maskx += wid;
		  if(masky < 0)
		    masky += hih;

		  foo = GDK_DRAWABLE_IMPL_FBDATA(ts)->mem[(maskx >> 3) + GDK_DRAWABLE_IMPL_FBDATA(ts)->rowstride*masky];
		  if(foo & (1 << (maskx % 8)))
		    {
		      spot = GDK_GC_FBDATA(gc)->values.foreground;
		    }
		  else if(solid_stipple)
		    {
		      spot = GDK_GC_FBDATA(gc)->values.background;
		    }
		  else
		    continue;
		}

	      gdk_fb_drawable_set_pixel(drawable, gc, curx, cury, &spot, TRUE);
	    }
	}
    }
  else
    {
      guchar *mem = GDK_DRAWABLE_FBDATA(drawable)->mem, *ptr;
      guint rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride;
      int n;

      switch(GDK_DRAWABLE_P(drawable)->depth)
	{
	case 1:
	  {
	    int fromx = MIN((cur->x1+7)&(~7), cur->x2);
	    int begn = fromx - cur->x1, begoff = cur->x1 % 8, endn;
	    guchar begmask, endmask;
	    int body_end = cur->x2 & ~7;
	    int body_len = (body_end - fromx)/8;

	    begmask = ((1 << (begn + begoff)) - 1)
	      & ~((1 << (begoff)) - 1);
	    endn = cur->x2 - body_end;
	    endmask = (1 << endn) - 1;

	    for(cury = cur->y1; cury < cur->y2; cury++)
	      {
		ptr = mem + cury*rowstride + (cur->x1 >> 3);

		if(spot.pixel)
		  *ptr |= begmask;
		else
		  *ptr &= ~begmask;

		curx = fromx;

		if(curx < cur->x2)
		  {
		    ptr = mem + cury*rowstride + (curx >> 3);
		    memset(ptr, spot.pixel?0xFF:0, body_len);

		    if(endn)
		      {
			ptr = mem + cury*rowstride + (body_end >> 3);
			if(spot.pixel)
			  *ptr |= endmask;
			else
			  *ptr &= ~endmask;
		      }
		  }
	      }
	  }
	  break;
	case 8:
	  for(cury = cur->y1; cury < cur->y2; cury++)
	    {
	      ptr = mem + cury*rowstride + cur->x1;
	      memset(ptr, spot.pixel, cur->x2 - cur->x1);
	    }
	  break;
	case 16:
	  {
	    int i;
	    n = cur->x2 - cur->x1;
	    for(cury = cur->y1; cury < cur->y2; cury++)
	      {
		guint16 *p16 = (guint16 *)(mem + cury * rowstride + cur->x1*2);
		for(i = 0; i < n; i++)
		  *(p16++) = spot.pixel;
	      }
	  }
	  break;
	case 24:
	  {
	    guchar redval = spot.red>>8, greenval=spot.green>>8, blueval=spot.blue>>8;
	    guchar *firstline, *ptr_end;

	    if((cur->y2 - cur->y1) <= 0)
	      break;

	    n = (cur->x2 - cur->x1)*3;

	    firstline = ptr = mem + cur->y1 * rowstride + cur->x1*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;
	      }
	    for(cury = cur->y1 + 1, ptr = mem + cury * rowstride + cur->x1*3; cury < cur->y2; cury++, ptr += rowstride)
	      {
		memcpy(ptr, firstline, n);
	      }
	  }
	  break;
	case 32:
	  {
	    int i;
	    n = cur->x2 - cur->x1;
	    for(cury = cur->y1; cury < cur->y2; cury++)
	      {
		guint32 *p32 = (guint32 *)(mem + cury * rowstride + cur->x1*4);
		for(i = 0; i < n; i++)
		  *(p32++) = spot.pixel;
	      }
	  }
	  break;
	default:
	  g_assert_not_reached();
#if 0
	  for(cury = cur->y1; cury < cur->y2; cury++)
	    {
	      for(curx = cur->x1; curx < cur->x2; curx++)
		{
		  gdk_fb_drawable_set_pixel(drawable, gc, curx, cury, &spot, TRUE);
		}
	    }
#endif
	  break;
	}
    }
}

void
gdk_fb_fill_spans(GdkDrawable *real_drawable,
		  GdkGC *gc,
		  GdkRectangle *rects, int nrects)
{
  int i;
  GdkColor color;
  GdkRegion *real_clip_region, *tmpreg;
  GdkRectangle draw_rect;
  GdkVisual *visual = gdk_visual_get_system();
  gboolean handle_cursor = FALSE;
  GdkDrawable *drawable;

  drawable = real_drawable;

  if(GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper) && !GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->mapped)
    return;
  if(GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper) && GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->input_only)
    g_error("Drawing on the evil input-only!");

  if(gc && (GDK_GC_FBDATA(gc)->values_mask | GDK_GC_FOREGROUND))
    color = GDK_GC_FBDATA(gc)->values.foreground;
  else if(GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper))
    color = GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->bg_color;
  else
    gdk_color_black(GDK_DRAWABLE_P(drawable)->colormap, &color);

  real_clip_region = gdk_fb_clip_region(drawable, gc, TRUE, (!gc || GDK_GC_FBDATA(gc)->values.function!=GDK_INVERT));

  if(GDK_DRAWABLE_FBDATA(drawable)->mem == GDK_DRAWABLE_IMPL_FBDATA(gdk_parent_root)->mem
     && gdk_fb_cursor_region_need_hide(real_clip_region))
    {
      handle_cursor = TRUE;
      gdk_fb_cursor_hide();
    }
    
  for(i = 0; i < nrects; i++)
    {
      GdkSegment cur;
      int j;

      cur.x1 = rects[i].x;
      cur.y1 = rects[i].y;
      cur.x2 = cur.x1 + rects[i].width;
      cur.y2 = cur.y1 + rects[i].height;
      g_assert(cur.x2 >= cur.x1);
      g_assert(cur.y2 >= cur.y1);

      cur.x1 += GDK_DRAWABLE_FBDATA(drawable)->abs_x;
      cur.x1 = MAX(cur.x1, GDK_DRAWABLE_FBDATA(drawable)->llim_x);

      cur.x2 += GDK_DRAWABLE_FBDATA(drawable)->abs_x;
      cur.x2 = MIN(cur.x2, GDK_DRAWABLE_FBDATA(drawable)->lim_x);
      cur.x1 = MIN(cur.x1, cur.x2);

      cur.y1 += GDK_DRAWABLE_FBDATA(drawable)->abs_y;
      cur.y1 = MAX(cur.y1, GDK_DRAWABLE_FBDATA(drawable)->llim_y);

      cur.y2 += GDK_DRAWABLE_FBDATA(drawable)->abs_y;
      cur.y2 = MIN(cur.y2, GDK_DRAWABLE_FBDATA(drawable)->lim_y);
      cur.y1 = MIN(cur.y1, cur.y2);

      draw_rect.x = cur.x1;
      draw_rect.y = cur.y1;
      draw_rect.width = cur.x2 - cur.x1;
      draw_rect.height = cur.y2 - cur.y1;

      switch(gdk_region_rect_in(real_clip_region, &draw_rect))
	{
	case GDK_OVERLAP_RECTANGLE_PART:
	  tmpreg = gdk_region_rectangle(&draw_rect);
	  gdk_region_intersect(tmpreg, real_clip_region);
	  for(j = 0; j < tmpreg->numRects; j++)
	    {
	      cur = tmpreg->rects[j];
	      gdk_fb_fill_span(drawable, gc, &cur, &color, visual);
	    }
	  gdk_region_destroy(tmpreg);
	  break;
	case GDK_OVERLAP_RECTANGLE_IN:
	  gdk_fb_fill_span(drawable, gc, &cur, &color, visual);
	  break;
	default:
	  break;
	}
    }

  gdk_region_destroy(real_clip_region);
  if(handle_cursor)
    gdk_fb_cursor_unhide();
}

static GetPixelRet
gdk_fb_drawable_get_pixel(GdkDrawable *drawable, GdkGC *gc, int x, int y, GdkColor *spot,
			  gboolean abs_coords, GdkDrawable *bg_relto, GdkDrawable *bgpm)
{
  GetPixelRet retval = GPR_NONE;
  guchar *mem = GDK_DRAWABLE_FBDATA(drawable)->mem;
  guint rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride;

  if(!abs_coords)
    {
      x += GDK_DRAWABLE_FBDATA(drawable)->abs_x;
      y += GDK_DRAWABLE_FBDATA(drawable)->abs_y;
    }

  switch(GDK_DRAWABLE_P(drawable)->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;

	    if(bgpm)
	      {
		int bgx, bgy;

		bgx = (x - GDK_DRAWABLE_IMPL_FBDATA(bg_relto)->abs_x) % GDK_DRAWABLE_IMPL_FBDATA(bgpm)->width;
		bgy = (y - GDK_DRAWABLE_IMPL_FBDATA(bg_relto)->abs_y) % GDK_DRAWABLE_IMPL_FBDATA(bgpm)->height;

		gdk_fb_drawable_get_pixel(bgpm, gc, bgx, bgy, spot, FALSE, NULL, NULL);
		retval = GPR_USED_BG;
	      }
	    else
	      {
		*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 = GDK_DRAWABLE_P(drawable)->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 (__BYTE_ORDER == __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;
}

static void
gdk_fb_drawable_set_pixel(GdkDrawable *drawable, GdkGC *gc, int x, int y, GdkColor *spot,
			  gboolean abs_coords)
{
  guchar *mem = GDK_DRAWABLE_FBDATA(drawable)->mem;
  guint rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride;

  if(!abs_coords)
    {
      x += GDK_DRAWABLE_FBDATA(drawable)->abs_x;
      y += GDK_DRAWABLE_FBDATA(drawable)->abs_y;
    }

  switch(GDK_DRAWABLE_P(drawable)->depth)
    {
    case 1:
      {
	guchar *foo = mem + (y*rowstride) + (x >> 3);

	if(spot->pixel)
	  *foo |= (1 << (x % 8));
	else
	  *foo &= ~(1 << (x % 8));
      }
      break;
    case 8:
      mem[x + y*rowstride] = spot->pixel;
      break;
    case 16:
      {
	guint16 *p16 = (guint16 *)&mem[x*2 + y*rowstride];
	*p16 = spot->pixel;
      }
      break;
    case 24:
      {
	guchar *smem = &mem[x*3 + y*rowstride];
	smem[gdk_display->red_byte] = spot->red >> 8;
	smem[gdk_display->green_byte] = spot->green >> 8;
	smem[gdk_display->blue_byte] = spot->blue >> 8;
      };
      break;
    case 32:
      {
	guint32 *smem = (guint32 *)&mem[x*4 + y*rowstride];
	*smem = spot->pixel;
      }
      break;
    default:
      g_assert_not_reached();
      break;
    }

}

void
gdk_fb_drawing_context_init(GdkFBDrawingContext *dc,
			    GdkDrawable *drawable,
			    GdkGC *gc,
			    gboolean draw_bg,
			    gboolean do_clipping)
{
  dc->mem = GDK_DRAWABLE_FBDATA(drawable)->mem;
  dc->rowstride = GDK_DRAWABLE_FBDATA(drawable)->rowstride;
  dc->handle_cursor = FALSE;
  dc->bgpm = NULL;
  dc->bg_relto = GDK_DRAWABLE_P(drawable)->wrapper;
  dc->draw_bg = draw_bg;

  if(GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper))
    {
      dc->bgpm = GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->bg_pixmap;
      if(dc->bgpm == GDK_PARENT_RELATIVE_BG)
	{
	  for(; dc->bgpm == GDK_PARENT_RELATIVE_BG && dc->bg_relto; dc->bg_relto = (GdkWindow *)GDK_WINDOW_P(dc->bg_relto)->parent)
	    dc->bgpm = GDK_WINDOW_P(dc->bg_relto)->bg_pixmap;
	}

      if(dc->bgpm == GDK_NO_BG)
	dc->bgpm = NULL;
    }
  dc->clipxoff = - GDK_DRAWABLE_FBDATA(drawable)->abs_x;
  dc->clipyoff = - GDK_DRAWABLE_FBDATA(drawable)->abs_y;

  dc->real_clip_region = gdk_fb_clip_region(drawable, gc, do_clipping, TRUE);

  if(gc)
    {
      dc->clipxoff -= GDK_GC_FBDATA(gc)->values.clip_x_origin;
      dc->clipyoff -= GDK_GC_FBDATA(gc)->values.clip_y_origin;

      if(GDK_GC_FBDATA(gc)->values.clip_mask)
	{
	  dc->clipmem = GDK_DRAWABLE_IMPL_FBDATA(GDK_GC_FBDATA(gc)->values.clip_mask)->mem;
	  dc->clip_rowstride = GDK_DRAWABLE_IMPL_FBDATA(GDK_GC_FBDATA(gc)->values.clip_mask)->rowstride;
	}
    }

  if(do_clipping
     && GDK_DRAWABLE_FBDATA(drawable)->mem == GDK_DRAWABLE_IMPL_FBDATA(gdk_parent_root)->mem
     && gdk_fb_cursor_region_need_hide(dc->real_clip_region))
    {
      dc->handle_cursor = TRUE;
      gdk_fb_cursor_hide();
    }
}

void
gdk_fb_drawing_context_finalize(GdkFBDrawingContext *dc)
{
  gdk_region_destroy(dc->real_clip_region);

  if(dc->handle_cursor)
    gdk_fb_cursor_unhide();
}

void
gdk_fb_draw_drawable_2 (GdkDrawable *drawable,
			GdkGC       *gc,
			GdkPixmap   *src,
			gint         xsrc,
			gint         ysrc,
			gint         xdest,
			gint         ydest,
			gint         width,
			gint         height,
			gboolean     draw_bg,
			gboolean     do_clipping)
{
  GdkFBDrawingContext *dc, dc_data;
  dc = &dc_data;

  gdk_fb_drawing_context_init(dc, drawable, gc, draw_bg, do_clipping);
  gdk_fb_draw_drawable_3(drawable, gc, src, dc, xsrc, ysrc, xdest, ydest, width, height);
  gdk_fb_drawing_context_finalize(dc);
}

void
gdk_fb_draw_drawable_3 (GdkDrawable *drawable,
			GdkGC       *gc,
			GdkPixmap   *src,
			GdkFBDrawingContext *dc,
			gint         xsrc,
			gint         ysrc,
			gint         xdest,
			gint         ydest,
			gint         width,
			gint         height)
{
  GdkRectangle rect;
  guchar *srcmem = GDK_DRAWABLE_FBDATA(src)->mem;
  int src_x_off, src_y_off;
  GdkRegion *tmpreg, *real_clip_region;
  int i;
  int draw_direction = 1;
  gboolean do_quick_draw;

  if(GDK_IS_WINDOW(GDK_DRAWABLE_P(drawable)->wrapper))
    {
      if(!GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->mapped)
	return;
      if(GDK_WINDOW_P(GDK_DRAWABLE_P(drawable)->wrapper)->input_only)
	g_error("Drawing on the evil input-only!");
    }

  if(drawable == src)
    {
      GdkRegionBox srcb, destb;
      srcb.x1 = xsrc;
      srcb.y1 = ysrc;
      srcb.x2 = xsrc + width;
      srcb.y2 = ysrc + height;
      destb.x1 = xdest;
      destb.y1 = ydest;
      destb.x2 = xdest + width;
      destb.y2 = xdest + height;

      if(EXTENTCHECK(&srcb, &destb)
	 && ydest > ysrc)
	draw_direction = -1;
#if 0
	{
	  GdkDrawableFBData *fbd = GDK_DRAWABLE_FBDATA(src);

	  /* One lame hack deserves another ;-) */
	  srcmem = g_alloca(fbd->rowstride * (fbd->lim_y - fbd->llim_y));
	  memmove(srcmem, dc->mem + (fbd->rowstride * fbd->llim_y), fbd->rowstride * (fbd->lim_y - fbd->llim_y));
	  srcmem -= (fbd->rowstride * fbd->llim_y);
	}
#endif
    }

  /* Do some magic to avoid creating extra regions unnecessarily */
  tmpreg = dc->real_clip_region;

  rect.x = xdest + GDK_DRAWABLE_FBDATA(drawable)->abs_x;
  rect.y = ydest + GDK_DRAWABLE_FBDATA(drawable)->abs_y;
  rect.width = width;
  rect.height = height;
  real_clip_region = gdk_region_rectangle(&rect);
  gdk_region_intersect(real_clip_region, tmpreg);

  rect.x = xdest + GDK_DRAWABLE_FBDATA(drawable)->abs_x;
  rect.y = ydest + GDK_DRAWABLE_FBDATA(drawable)->abs_y;
  rect.width = MAX(GDK_DRAWABLE_P(src)->width - xsrc, 0);
  rect.height = MAX(GDK_DRAWABLE_P(src)->height - ysrc, 0);
  if(!rect.width || !rect.height)
    goto out;
  tmpreg = gdk_region_rectangle(&rect);
  gdk_region_intersect(real_clip_region, tmpreg);
  gdk_region_destroy(tmpreg);

  src_x_off = (GDK_DRAWABLE_FBDATA(src)->abs_x + xsrc) - (GDK_DRAWABLE_FBDATA(drawable)->abs_x + xdest);
  src_y_off = (GDK_DRAWABLE_FBDATA(src)->abs_y + ysrc) - (GDK_DRAWABLE_FBDATA(drawable)->abs_y + ydest);

  do_quick_draw = GDK_DRAWABLE_P(src)->depth == GDK_DRAWABLE_P(drawable)->depth
    && GDK_DRAWABLE_P(src)->depth >= 8
    && GDK_DRAWABLE_P(src)->depth <= 32
    && (!gc || !GDK_GC_FBDATA(gc)->values.clip_mask);

  for(i = 0; i < real_clip_region->numRects; i++)
    {
      GdkRegionBox *cur = &real_clip_region->rects[i];
      int start_y, end_y, cur_y;

      if(draw_direction > 0)
	{
	  start_y = cur->y1;
	  end_y = cur->y2;
	}
      else
	{
	  start_y = cur->y2 - 1;
	  end_y = cur->y1 - 1;
	}

      if(do_quick_draw)
	{
	  guint depth = GDK_DRAWABLE_P(src)->depth;
	  guint src_rowstride = GDK_DRAWABLE_FBDATA(src)->rowstride;
	  int linelen = (cur->x2 - cur->x1)*(depth>>3);

	  for(cur_y = start_y; cur_y*draw_direction < end_y*draw_direction; cur_y += draw_direction)
	    {
	      memmove(dc->mem + (cur_y * dc->rowstride) + cur->x1*(depth>>3),
		      srcmem + ((cur_y + src_y_off)*src_rowstride) + (cur->x1 + src_x_off)*(depth>>3),
		      linelen);
	    }
	}
      else
	{
	  int cur_x;

	  for(cur_y = start_y; cur_y*draw_direction < end_y*draw_direction; cur_y+=draw_direction)
	    {
	      for(cur_x = cur->x1; cur_x < cur->x2; cur_x++)
		{
		  GdkColor spot;

		  if(gc && 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_pixel(src, gc, cur_x + src_x_off, cur_y + src_y_off, &spot, TRUE, NULL, NULL))
		    {
		    case GPR_AA_GRAYVAL:
		      {
			GdkColor realspot, fg;
			guint graylevel = spot.pixel;
			gint tmp;

			if(GDK_DRAWABLE_P(drawable)->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_fb_drawable_get_pixel(drawable, gc, cur_x, cur_y, &realspot, TRUE, dc->bg_relto, dc->bgpm))
				  {
				  case GPR_NONE:
				  case GPR_USED_BG:
				    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(GDK_DRAWABLE_P(drawable)->depth)
				  {
				  case 8:
				    if(!gdk_colormap_alloc_color(GDK_DRAWABLE_P(drawable)->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) << (gdk_display->modeinfo.red.offset - 8))
				      | ((spot.green & 0xFF00) << (gdk_display->modeinfo.green.offset - 8))
				      | ((spot.blue & 0xFF00) << (gdk_display->modeinfo.blue.offset - 8));
				    break;
				  }
			      }
			  }
		      }
		      break;
		    case GPR_USED_BG:
		      if(!dc->draw_bg)
			continue;
		      break;
		    case GPR_NONE:
		      break;
		    default:
		      g_assert_not_reached();
		      break;
		    }

		  gdk_fb_drawable_set_pixel(drawable, gc, cur_x, cur_y, &spot, GDK_DRAWABLE_P(src)->depth);
		}
	    }
	}
    }

 out:
  gdk_region_destroy(real_clip_region);
}

void
gdk_fb_draw_drawable (GdkDrawable *drawable,
		      GdkGC       *gc,
		      GdkPixmap   *src,
		      gint         xsrc,
		      gint         ysrc,
		      gint         xdest,
		      gint         ydest,
		      gint         width,
		      gint         height)
{
  gdk_fb_draw_drawable_2(drawable, gc, GDK_DRAWABLE_IMPL(src), xsrc, ysrc, xdest, ydest, width, height, TRUE, TRUE);
}

static void
gdk_fb_draw_text(GdkDrawable    *drawable,
		 GdkFont        *font,
		 GdkGC          *gc,
		 gint            x,
		 gint            y,
		 const gchar    *text,
		 gint            text_length)
{
  g_warning("gdk_fb_draw_text NYI");
}

static void
gdk_fb_draw_text_wc (GdkDrawable    *drawable,
		     GdkFont	     *font,
		     GdkGC	     *gc,
		     gint	      x,
		     gint	      y,
		     const GdkWChar *text,
		     gint	      text_length)
{
  g_warning("gdk_fb_draw_text_wc NYI");
}

void
gdk_fb_draw_rectangle (GdkDrawable    *drawable,
		       GdkGC          *gc,
		       gint            filled,
		       gint            x,
		       gint            y,
		       gint            width,
		       gint            height)
{
  GdkRectangle rect;

  if(filled)
    {
      rect.x = x;
      rect.y = y;
      rect.width = width;
      rect.height = height;
      gdk_fb_fill_spans(drawable, gc, &rect, 1);
    }
  else
    {
      GdkPoint pts[5];
      pts[0].x = pts[4].x = x;
      pts[0].y = pts[4].y = y;
      pts[1].x = x + width;
      pts[1].y = y;
      pts[2].x = x + width;
      pts[2].y = y + height;
      pts[3].x = x;
      pts[3].y = y + height;
      gdk_fb_draw_lines(drawable, gc, pts, 5);
    }
}

static void gdk_fb_draw_points    (GdkDrawable    *drawable,
				   GdkGC          *gc,
				   GdkPoint       *points,
				   gint            npoints)
{
  GdkRectangle *rects = g_alloca(npoints * sizeof(GdkRectangle));
  int i;

  for(i = 0; i < npoints; i++)
    {
      rects[i].x = points[i].x;
      rects[i].y = points[i].y;
      rects[i].width = rects[i].height = 1;
    }

  gdk_fb_fill_spans(drawable, gc, rects, npoints);
}

static void gdk_fb_draw_arc       (GdkDrawable    *drawable,
				   GdkGC          *gc,
				   gint            filled,
				   gint            x,
				   gint            y,
				   gint            width,
				   gint            height,
				   gint            angle1,
				   gint            angle2)
{
  miArc arc;

  arc.x = x;
  arc.y = y;
  arc.width = width;
  arc.height = height;
  arc.angle1 = angle1;
  arc.angle2 = angle2;

  if(filled)
    miPolyFillArc(drawable, gc, 1, &arc);
  else
    miPolyArc(drawable, gc, 1, &arc);
}

static void gdk_fb_draw_polygon   (GdkDrawable    *drawable,
				   GdkGC          *gc,
				   gint            filled,
				   GdkPoint       *points,
				   gint            npoints)
{
  if(filled)
    miFillPolygon(drawable, gc, 0, 0, npoints, points);
  else
    {
      GdkPoint *realpts = g_alloca(sizeof(GdkPoint) * (npoints + 1));

      memcpy(realpts, points, sizeof(GdkPoint) * npoints);
      realpts[npoints] = points[0];
      gdk_fb_draw_lines(drawable, gc, points, npoints);
    }
}

static void gdk_fb_draw_lines     (GdkDrawable    *drawable,
				   GdkGC          *gc,
				   GdkPoint       *points,
				   gint            npoints)
{
  if(GDK_GC_FBDATA(gc)->values.line_width > 0)
    miWideLine(drawable, gc, 0, npoints, points);
  else
    miZeroLine(drawable, gc, 0, npoints, points);
}

static void gdk_fb_draw_segments  (GdkDrawable    *drawable,
				   GdkGC          *gc,
				   GdkSegment     *segs,
				   gint            nsegs)
{
  GdkPoint pts[2];
  int i;

  for(i = 0; i < nsegs; i++)
    {
      pts[0].x = segs[i].x1;
      pts[0].y = segs[i].y1;
      pts[1].x = segs[i].x2;
      pts[1].y = segs[i].y2;

      gdk_fb_draw_lines(drawable, gc, pts, 2);
    }
}

void
gdk_fb_drawable_clear(GdkDrawable *d)
{
  extern void
    _gdk_windowing_window_clear_area (GdkWindow *window,
				      gint       x,
				      gint       y,
				      gint       width,
				      gint       height);

  _gdk_windowing_window_clear_area(d, 0, 0, GDK_DRAWABLE_IMPL_FBDATA(d)->width, GDK_DRAWABLE_IMPL_FBDATA(d)->height);
}

extern FT_Library gdk_fb_ft_lib;

extern void pango_fb_font_set_size(PangoFont *font);

static void gdk_fb_draw_glyphs(GdkDrawable      *drawable,
			       GdkGC	        *gc,
			       PangoFont        *font,
			       gint              x,
			       gint              y,
			       PangoGlyphString *glyphs)
{
  int i;
  int xpos;
  GdkFBDrawingContext fbdc;

  g_return_if_fail(font);

  gdk_fb_drawing_context_init(&fbdc, drawable, gc, FALSE, TRUE);

  /* Fake its existence as a pixmap */

  pango_fb_font_set_size(font);
  for(i = xpos = 0; i < glyphs->num_glyphs; i++)
    {
      PangoFBGlyphInfo *pgi;
      int this_wid;

      pgi = pango_fb_font_get_glyph_info(font, glyphs->glyphs[i].glyph);

      this_wid = (xpos + glyphs->glyphs[i].geometry.width)/PANGO_SCALE;
      gdk_fb_draw_drawable_3(drawable, gc, (GdkPixmap *)&pgi->fbd,
			     &fbdc,
			     0, 0,
			     x + (xpos + glyphs->glyphs[i].geometry.x_offset)/PANGO_SCALE,
			     y + glyphs->glyphs[i].geometry.y_offset / PANGO_SCALE
			     + pgi->hbearing,
			     this_wid, pgi->fbd.drawable_data.height);

      xpos += glyphs->glyphs[i].geometry.width;
    }

  gdk_fb_drawing_context_finalize(&fbdc);
}


static void
gdk_fb_draw_image(GdkDrawable *drawable,
		  GdkGC       *gc,
		  GdkImage    *image,
		  gint         xsrc,
		  gint         ysrc,
		  gint         xdest,
		  gint         ydest,
		  gint         width,
		  gint         height)
{
  GdkImagePrivateFB *image_private;
  GdkPixmapFBData fbd;

  g_return_if_fail (drawable != NULL);
  g_return_if_fail (image != NULL);
  g_return_if_fail (gc != NULL);

  image_private = (GdkImagePrivateFB*) image;

  g_return_if_fail (image->type == GDK_IMAGE_NORMAL);

  /* Fake its existence as a pixmap */
  memset(&fbd, 0, sizeof(fbd));
  fbd.drawable_data.mem = image->mem;
  fbd.drawable_data.rowstride = image->bpl;
  fbd.drawable_data.width = fbd.drawable_data.lim_x = image->width;
  fbd.drawable_data.height = fbd.drawable_data.lim_y = image->height;
  fbd.drawable_data.depth = image->depth;
  fbd.drawable_data.window_type = GDK_DRAWABLE_PIXMAP;

  gdk_fb_draw_drawable_2(drawable, gc, (GdkPixmap *)&fbd, xsrc, ysrc, xdest, ydest, width, height, TRUE, TRUE);
}

static gint
gdk_fb_get_depth (GdkDrawable *drawable)
{
  return GDK_DRAWABLE_IMPL_FBDATA (drawable)->depth;
}

static GdkVisual*
gdk_fb_get_visual (GdkDrawable    *drawable)
{
  return gdk_visual_get_system();
}
