#include "config.h"
#include "gdkprivate-fb.h"
#include "mi.h"
#include <string.h>
#include <gdkregion-generic.h>

#include <pango/pangoft2.h>
#include <freetype/ftglyph.h>

#ifndef g_alloca
#define g_alloca alloca
#endif

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 void         gdk_fb_drawable_finalize  (GObject *object);

#ifdef ENABLE_SHADOW_FB
static void         gdk_shadow_fb_draw_rectangle     (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      gint              filled,
						      gint              x,
						      gint              y,
						      gint              width,
						      gint              height);
static void         gdk_shadow_fb_draw_arc           (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      gint              filled,
						      gint              x,
						      gint              y,
						      gint              width,
						      gint              height,
						      gint              angle1,
						      gint              angle2);
static void         gdk_shadow_fb_draw_polygon       (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      gint              filled,
						      GdkPoint         *points,
						      gint              npoints);
static void         gdk_shadow_fb_draw_text          (GdkDrawable      *drawable,
						      GdkFont          *font,
						      GdkGC            *gc,
						      gint              x,
						      gint              y,
						      const gchar      *text,
						      gint              text_length);
static void         gdk_shadow_fb_draw_text_wc       (GdkDrawable      *drawable,
						      GdkFont          *font,
						      GdkGC            *gc,
						      gint              x,
						      gint              y,
						      const GdkWChar   *text,
						      gint              text_length);
static void         gdk_shadow_fb_draw_glyphs        (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      PangoFont        *font,
						      gint              x,
						      gint              y,
						      PangoGlyphString *glyphs);
static void         gdk_shadow_fb_draw_drawable      (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      GdkPixmap        *src,
						      gint              xsrc,
						      gint              ysrc,
						      gint              xdest,
						      gint              ydest,
						      gint              width,
						      gint              height);
static void         gdk_shadow_fb_draw_image         (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      GdkImage         *image,
						      gint              xsrc,
						      gint              ysrc,
						      gint              xdest,
						      gint              ydest,
						      gint              width,
						      gint              height);
static void         gdk_shadow_fb_draw_points        (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      GdkPoint         *points,
						      gint              npoints);
static void         gdk_shadow_fb_draw_segments      (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      GdkSegment       *segs,
						      gint              nsegs);
static void         gdk_shadow_fb_draw_lines         (GdkDrawable      *drawable,
						      GdkGC            *gc,
						      GdkPoint         *points,
						      gint              npoints);
#endif


static gpointer parent_class = NULL;

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

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

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = gdk_fb_drawable_finalize;
  
  drawable_class->create_gc = _gdk_fb_gc_new;
  
#ifdef ENABLE_SHADOW_FB
  drawable_class->draw_rectangle = gdk_shadow_fb_draw_rectangle;
  drawable_class->draw_arc = gdk_shadow_fb_draw_arc;
  drawable_class->draw_polygon = gdk_shadow_fb_draw_polygon;
  drawable_class->draw_text = gdk_shadow_fb_draw_text;
  drawable_class->draw_text_wc = gdk_shadow_fb_draw_text_wc;
  drawable_class->draw_drawable = gdk_shadow_fb_draw_drawable;
  drawable_class->draw_points = gdk_shadow_fb_draw_points;
  drawable_class->draw_segments = gdk_shadow_fb_draw_segments;
  drawable_class->draw_lines = gdk_shadow_fb_draw_lines;
  drawable_class->draw_glyphs = gdk_shadow_fb_draw_glyphs;
  drawable_class->draw_image = gdk_shadow_fb_draw_image;
#else
  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;
#endif
  
  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->_copy_to_image = _gdk_fb_copy_to_image;
}

static void
gdk_fb_drawable_finalize (GObject *object)
{
  gdk_drawable_set_colormap (GDK_DRAWABLE (object), NULL);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}


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)
{
  GdkDrawableFBData *private;

  private = GDK_DRAWABLE_FBDATA (drawable);

  if (private->colormap == colormap)
    return;

  if (private->colormap)
    gdk_colormap_unref (private->colormap);
  private->colormap = colormap;
  if (private->colormap)
    gdk_colormap_ref (private->colormap);
}

/* Calculates the real clipping region for a drawable, taking into account
 * other windows, gc clip region and gc clip mask.
 */
GdkRegion *
gdk_fb_clip_region (GdkDrawable *drawable,
		    GdkGC *gc,
		    gboolean do_clipping,
		    gboolean do_children,
		    gboolean full_shapes)
{
  GdkRectangle draw_rect;
  GdkRegion *real_clip_region, *tmpreg, *shape;
  gboolean skipit = FALSE;
  GdkDrawableFBData *private;
  GdkWindowObject *parent;

  GDK_CHECK_IMPL (drawable);
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  
  g_assert(!GDK_IS_WINDOW (private->wrapper) ||
	   !GDK_WINDOW_P (private->wrapper)->input_only);
  
  draw_rect.x = private->llim_x;
  draw_rect.y = private->llim_y;
  if (!GDK_IS_WINDOW (private) ||
      GDK_WINDOW_IS_MAPPED (private->wrapper))
    {
      draw_rect.width = private->lim_x - draw_rect.x;
      draw_rect.height = private->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 (GDK_IS_WINDOW (private->wrapper))
    {
      parent = GDK_WINDOW_P (private->wrapper);
      while (parent != (GdkWindowObject *)_gdk_parent_root)
	{
	  if (full_shapes)
	    {
	      shape = gdk_fb_window_get_abs_shape (GDK_DRAWABLE (parent));
	      if (shape)
		{
		  gdk_region_intersect (real_clip_region, shape);
		  gdk_region_destroy (shape);
		}
	    }
	  else
	    {
	      gint dx, dy;
	      shape = gdk_fb_window_peek_shape (GDK_DRAWABLE (parent), &dx, &dy);
	      if (shape)
		{
		  GdkRectangle rect;
		  GdkRegion *reg;
		  
		  gdk_region_get_clipbox (shape, &rect);
		  
		  rect.x += GDK_DRAWABLE_IMPL_FBDATA (parent)->abs_x + dx;
		  rect.y += GDK_DRAWABLE_IMPL_FBDATA (parent)->abs_y + dy;
		  
		  reg = gdk_region_rectangle(&rect);
		  gdk_region_intersect (real_clip_region, reg);
		  gdk_region_destroy (reg);
		}
	      
	    }
	  parent = parent->parent;
	}
    }

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

  if (do_clipping &&
      GDK_IS_WINDOW (private->wrapper) &&
      GDK_WINDOW_IS_MAPPED (private->wrapper) &&
      !GDK_WINDOW_P (private->wrapper)->input_only)
    {
      GdkWindow *parentwin, *lastwin;
      GdkDrawableFBData *impl_private;

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

      /* Remove the areas of all overlapping windows above parentwin in the hiearachy */
      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)
	    {
	      if (!GDK_WINDOW_IS_MAPPED (cur->data) || GDK_WINDOW_P (cur->data)->input_only)
		continue;

	      impl_private = GDK_DRAWABLE_IMPL_FBDATA(cur->data);

	      /* This shortcut is really necessary for performance when there are a lot of windows */
	      if (impl_private->llim_x >= real_clip_region->extents.x2 ||
		  impl_private->lim_x <= real_clip_region->extents.x1 ||
		  impl_private->llim_y >= real_clip_region->extents.y2 ||
		  impl_private->lim_y <= real_clip_region->extents.y1)
		continue;
	      
	      draw_rect.x = impl_private->llim_x;
	      draw_rect.y = impl_private->llim_y;
	      draw_rect.width = impl_private->lim_x - draw_rect.x;
	      draw_rect.height = impl_private->lim_y - draw_rect.y;

	      tmpreg = gdk_region_rectangle (&draw_rect);

	      shape = gdk_fb_window_get_abs_shape (impl_private->wrapper);
	      if (shape)
		{
		  gdk_region_intersect (tmpreg, shape);
		  gdk_region_destroy (shape);
		}
	      
	      gdk_region_subtract (real_clip_region, tmpreg);
	      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, private->abs_x + GDK_GC_P (gc)->clip_x_origin,
			     private->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;
	  GdkDrawableFBData *cmask_private;

	  cmask_private = GDK_DRAWABLE_IMPL_FBDATA (cmask);

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

	  draw_rect.x = private->abs_x +
	                cmask_private->llim_x +
	                GDK_GC_FBDATA (gc)->values.clip_x_origin;
	  
	  draw_rect.y = private->abs_y +
	                cmask_private->llim_y +
	                GDK_GC_FBDATA (gc)->values.clip_y_origin;
	  
	  draw_rect.width = cmask_private->width;
	  draw_rect.height = cmask_private->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;
}


struct GdkSpanHelper
{
  GdkDrawable *drawable;
  GdkGC *gc;
  GdkColor color;
};

static void
gdk_fb_fill_span_helper(GdkSpan *span,
			gpointer data)
{
  struct GdkSpanHelper *info = (struct GdkSpanHelper *)data;
  GdkGC * gc = info->gc;
  
  (GDK_GC_FBDATA (gc)->fill_span) (info->drawable, gc, span, &info->color);
}

void
gdk_fb_fill_spans (GdkDrawable *real_drawable,
		   GdkGC *gc,
		   GdkSpan *spans,
		   int nspans,
		   gboolean sorted)
{
  int i;
  struct GdkSpanHelper info;
  GdkRegion *real_clip_region;
  gboolean handle_cursor = FALSE;
  GdkDrawable *drawable;
  GdkDrawableFBData *private;
  
  drawable = real_drawable;
  private = GDK_DRAWABLE_FBDATA (drawable);

  g_assert (gc);

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

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

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

  if (private->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 < nspans; i++)
    {
      GdkSpan *cur;
      
      cur = &spans[i];
      
      cur->x += private->abs_x;
      cur->y += private->abs_y;

      if ( (cur->y < private->llim_y) || (cur->y >= private->lim_y))
	cur->width = 0;
      
      if (cur->x < private->llim_x)
	{
	  cur->width -= private->llim_x - cur->x;
	  cur->x = private->llim_x;
	}
      
      if (cur->x + cur->width > private->lim_x)
	{
	  cur->width = private->lim_x - cur->x;
	}

      if (cur->width <= 0)
	cur->width = 0;
    }

  gdk_region_spans_intersect_foreach (real_clip_region,
				      spans,
				      nspans,
				      sorted,
				      gdk_fb_fill_span_helper,
				      &info);

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

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

  GDK_CHECK_IMPL (drawable);

  if (GDK_IS_WINDOW (private->wrapper))
    {
      dc->bgpm = GDK_WINDOW_P (private->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 = - private->abs_x;
  dc->clipyoff = - private->abs_y;

  dc->real_clip_region = gdk_fb_clip_region (drawable, gc, do_clipping, TRUE, 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 &&
      private->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_CHECK_IMPL (src);
  GDK_CHECK_IMPL (drawable);
  
  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)
{
  GdkDrawableFBData *private = GDK_DRAWABLE_FBDATA (drawable);
  GdkDrawableFBData *src_private = GDK_DRAWABLE_FBDATA (src);
  GdkRectangle rect;
  int src_x_off, src_y_off;
  GdkRegion *tmpreg, *real_clip_region;
  int i;
  int draw_direction = 1;
  gdk_fb_draw_drawable_func *draw_func = NULL;
  GdkGCFBData *gc_private;

  g_assert (gc);

  GDK_CHECK_IMPL (src);
  GDK_CHECK_IMPL (drawable);

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

  gc_private = GDK_GC_FBDATA (gc);
  
  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 = ydest + height;

      if (EXTENTCHECK (&srcb, &destb) && ((ydest > ysrc) || ((ydest == ysrc) && (xdest > xsrc))))
	draw_direction = -1;
    }

  switch (src_private->depth)
    {
    case 1:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_1];
      break;
    case 8:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_8];
      break;
    case 16:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_16];
      break;
    case 24:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_24];
      break;
    case 32:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_32];
      break;
    case 77:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_7_AA_GRAYVAL];
      break;
    case 78:
      draw_func = gc_private->draw_drawable[GDK_FB_SRC_BPP_8_AA_GRAYVAL];
      break;
    default:
      g_assert_not_reached ();
      break;
    }
  
  /* Do some magic to avoid creating extra regions unnecessarily */
  tmpreg = dc->real_clip_region;

  rect.x = xdest + private->abs_x;
  rect.y = ydest + private->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 + private->abs_x;
  rect.y = ydest + private->abs_y;
  rect.width = MAX (src_private->width - xsrc, 0);
  rect.height = MAX (src_private->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 = (src_private->abs_x + xsrc) - (private->abs_x + xdest);
  src_y_off = (src_private->abs_y + ysrc) - (private->abs_y + ydest);

  for (i = (draw_direction>0)?0:real_clip_region->numRects-1; i >= 0 && i < real_clip_region->numRects; i+=draw_direction)
    {
      GdkRegionBox *cur = &real_clip_region->rects[i];

      (*draw_func) (drawable,
		    gc,
		    src,
		    dc,
		    cur->y1,
		    cur->y2,
		    cur->x1,
		    cur->x2,
		    src_x_off,
		    src_y_off,
		    draw_direction);
    }

 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)
{
  GdkPixmap   *src_impl;

  if (GDK_IS_DRAWABLE_IMPL_FBDATA (src))
    src_impl = src;
  else
    src_impl = GDK_DRAWABLE_IMPL (src);

  GDK_CHECK_IMPL (drawable);
  
  gdk_fb_draw_drawable_2 (drawable, gc, src_impl , xsrc, ysrc, xdest, ydest, width, height, TRUE, TRUE);
}

#ifdef EMULATE_GDKFONT
static void
gdk_fb_draw_text(GdkDrawable    *drawable,
		 GdkFont        *font,
		 GdkGC          *gc,
		 gint            x,
		 gint            y,
		 const gchar    *text,
		 gint            text_length)
{
  GdkFontPrivateFB *private;
  guchar *utf8, *utf8_end;
  PangoGlyphString *glyphs = pango_glyph_string_new ();
  PangoEngineShape *shaper, *last_shaper;
  PangoAnalysis analysis;
  guchar *p, *start;
  gint x_offset;
  int i;
  
  g_return_if_fail (font != NULL);
  g_return_if_fail (text != NULL);

  private = (GdkFontPrivateFB*) font;

  utf8 = alloca (text_length*2);

  /* Convert latin-1 to utf8 */
  p = utf8;
  for (i = 0; i < text_length; i++)
    {
      if (text[i]==0)
	*p++ = 1; /* Hack to handle embedded nulls */
      else
	{
	  if(((guchar)text[i])<128)
	    *p++ = text[i];
	  else
	    {
	      *p++ = ((((guchar)text[i])>>6) & 0x3f) | 0xC0;
	      *p++ = (((guchar)text[i]) & 0x3f) | 0x80;
	    }
	}
    }
  utf8_end = p;

  last_shaper = NULL;
  shaper = NULL;

  x_offset = 0;
  p = start = utf8;
  while (p < utf8_end)
    {
      gunichar wc = g_utf8_get_char (p);
      p = g_utf8_next_char (p);
      shaper = pango_font_find_shaper (private->pango_font, pango_language_from_string ("fr"), wc);
      if (shaper != last_shaper)
	{
	  analysis.shape_engine = shaper;
	  analysis.lang_engine = NULL;
	  analysis.font = private->pango_font;
	  analysis.level = 0;
	  
	  pango_shape (start, p - start, &analysis, glyphs);

	  gdk_fb_draw_glyphs (drawable, gc, private->pango_font,
			      x + PANGO_PIXELS (x_offset), y,
			      glyphs);
	  
	  for (i = 0; i < glyphs->num_glyphs; i++)
	    x_offset += glyphs->glyphs[i].geometry.width;
	  
	  start = p;
	}

      last_shaper = shaper;
    }

  if (p > start)
    {
      analysis.shape_engine = shaper;
      analysis.lang_engine = NULL;
      analysis.font = private->pango_font;
      analysis.level = 0;
      
      pango_shape (start, p - start, &analysis, glyphs);
      
      gdk_fb_draw_glyphs (drawable, gc, private->pango_font,
			  x + PANGO_PIXELS (x_offset), y,
			  glyphs);
    }
  
  pango_glyph_string_free (glyphs);
      
  return;
}

#else
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");
}
#endif

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)
{
  GdkDrawableFBData *private;
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  
  if (filled)
    {
      gboolean handle_cursor = FALSE;
      GdkRectangle tmprect;
      GdkRegion *tmpreg;
      GdkRegion *real_clip_region;
      GdkColor color;
      int i;
      
      if (GDK_GC_FBDATA (gc)->values_mask & GDK_GC_FOREGROUND)
	color = GDK_GC_FBDATA (gc)->values.foreground;
      else if (GDK_IS_WINDOW (private->wrapper))
	color = GDK_WINDOW_P (private->wrapper)->bg_color;
      else
	gdk_color_black (private->colormap, &color);
      
      real_clip_region = gdk_fb_clip_region (drawable, gc, TRUE, GDK_GC_FBDATA (gc)->values.function != GDK_INVERT, TRUE);
      
      if (private->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 ();
	}
      
      x += private->abs_x;
      y += private->abs_y;
  
      if (x <  private->llim_x)
	{
	  width -= private->llim_x - x;
	  x = private->llim_x;
	}
      if (x + width >  private->lim_x)
	width =  private->lim_x - x;
      
      if (y <  private->llim_y)
	{
	  height -= private->llim_y - y;
	  y = private->llim_y;
	}
      if (y + height >  private->lim_y)
	height =  private->lim_y - y;

      tmprect.x = x;
      tmprect.y = y;
      tmprect.width = width;
      tmprect.height = height;
      tmpreg = gdk_region_rectangle (&tmprect);
      
      gdk_region_intersect (tmpreg, real_clip_region);
      
      for (i = 0; i < tmpreg->numRects; i++)
	{
	  GdkRectangle r;
	  r.x = tmpreg->rects[i].x1;
	  r.y = tmpreg->rects[i].y1;
	  r.width = tmpreg->rects[i].x2 - tmpreg->rects[i].x1;
	  r.height = tmpreg->rects[i].y2 - tmpreg->rects[i].y1;

	  if ((r.width > 0) && (r.height > 0))
	    (GDK_GC_FBDATA (gc)->fill_rectangle) (drawable, gc, &r, &color);
	}
      
      gdk_region_destroy (tmpreg);
      
      gdk_region_destroy (real_clip_region);
      if (handle_cursor)
	gdk_fb_cursor_unhide ();
    }
  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)
{
  GdkSpan *spans = g_alloca (npoints * sizeof(GdkSpan));
  int i;

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

  gdk_fb_fill_spans (drawable, gc, spans, npoints, FALSE);
}

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
    {
      gint tmp_npoints;
      gboolean free_points = FALSE;
      GdkPoint *tmp_points;

      if (points[0].x != points[npoints-1].x || points[0].y != points[npoints-1].y)
	{
	  tmp_npoints = npoints + 1;
	  tmp_points = g_new (GdkPoint, tmp_npoints);
	  free_points = TRUE;
	  memcpy (tmp_points, points, sizeof(GdkPoint) * npoints);
	  tmp_points[npoints].x = points[0].x;
	  tmp_points[npoints].y = points[0].y;
	}
      else
	{
	  tmp_npoints = npoints;
	  tmp_points = points;
	}

      gdk_fb_draw_lines (drawable, gc, tmp_points, tmp_npoints);
      
      if (free_points)
	g_free (tmp_points);
    }
}

static void
gdk_fb_draw_lines (GdkDrawable    *drawable,
		   GdkGC          *gc,
		   GdkPoint       *points,
		   gint            npoints)
{
  GdkGCFBData *private;

  private = GDK_GC_FBDATA (gc);
  if (private->values.line_width > 0)
    {
      if ((private->values.line_style != GDK_LINE_SOLID) && private->dash_list)
	miWideDash (drawable, gc, 0, npoints, points);
      else 
	miWideLine (drawable, gc, 0, npoints, points);
    }
  else
    {
      if ((private->values.line_style != GDK_LINE_SOLID) && private->dash_list)
	miZeroDashLine (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);
}

static void
_gdk_fb_draw_glyphs (GdkDrawable      *drawable,
		     GdkGC	     *gc,
		     PangoFont        *font,
		     gint              x,
		     gint              y,
		     PangoGlyphString *glyphs,
		     GdkRectangle     *bbox)
{
  GdkFBDrawingContext fbdc;
  GdkPixmapFBData pixmap;
  PangoGlyphInfo *gi;
  FT_Face face;
  FT_UInt glyph_index;
  int i, xpos;
  int maxy, miny;
  int topy;

  g_return_if_fail (font);

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

  /* Fake its existence as a pixmap */

  ((GTypeInstance *)&pixmap)->g_class = g_type_class_peek (_gdk_pixmap_impl_get_type ());
  pixmap.drawable_data.abs_x = 0;
  pixmap.drawable_data.abs_y = 0;
  pixmap.drawable_data.depth = 78;

  maxy = miny = 0;
  
  gi = glyphs->glyphs;
  for (i = 0, xpos = 0; i < glyphs->num_glyphs; i++, gi++)
    {
      if (gi->glyph)
	{
	  glyph_index = gi->glyph;
	  face = pango_ft2_font_get_face (font);

	  if (face)
	    {
	      /* Draw glyph */
	      FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
	      if (face->glyph->format != ft_glyph_format_bitmap)
		FT_Render_Glyph (face->glyph, ft_render_mode_normal);

	      pixmap.drawable_data.mem = face->glyph->bitmap.buffer;
	      pixmap.drawable_data.rowstride = face->glyph->bitmap.pitch;
	      pixmap.drawable_data.width = face->glyph->bitmap.width;
	      pixmap.drawable_data.height = face->glyph->bitmap.rows;

	      topy = y - face->glyph->bitmap_top + 1;
	      miny = MIN (miny, topy);
	      maxy = MAX (maxy, topy + face->glyph->bitmap.rows);
	      gdk_fb_draw_drawable_3 (drawable, gc, (GdkPixmap *)&pixmap,
				      &fbdc,
				      0, 0,
				      x + PANGO_PIXELS (xpos) + face->glyph->bitmap_left,
				      topy,
				      face->glyph->bitmap.width, face->glyph->bitmap.rows);
	    }
	}
      xpos += glyphs->glyphs[i].geometry.width;
    }

  gdk_fb_drawing_context_finalize (&fbdc);

  if (bbox)
    {
      bbox->x = x;
      bbox->y = miny;
      bbox->width = xpos;
      bbox->height = maxy - miny;
    }
}

static void
gdk_fb_draw_glyphs (GdkDrawable      *drawable,
		    GdkGC	     *gc,
		    PangoFont        *font,
		    gint              x,
		    gint              y,
		    PangoGlyphString *glyphs)
{
  _gdk_fb_draw_glyphs (drawable, gc, font, x, y, glyphs, NULL);
}

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));

  ((GTypeInstance *)&fbd)->g_class = g_type_class_peek (_gdk_pixmap_impl_get_type ());
  
  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;
  fbd.drawable_data.colormap = gdk_colormap_get_system ();
  
  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_FBDATA (drawable)->depth;
}

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

#ifdef ENABLE_SHADOW_FB
static void
gdk_shadow_fb_draw_rectangle (GdkDrawable      *drawable,
			      GdkGC            *gc,
			      gint              filled,
			      gint              x,
			      gint              y,
			      gint              width,
			      gint              height)
{
  GdkDrawableFBData *private;

  gdk_fb_draw_rectangle (drawable, gc, filled, x, y, width, height);

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      gint extra_width;
  
      minx = x + private->abs_x;
      miny = y + private->abs_y;
      maxx = x + width + private->abs_x;
      maxy = y + height + private->abs_y;
      
      if (!filled)
	{
	  extra_width = (GDK_GC_FBDATA (gc)->values.line_width + 1) / 2;
	  
	  minx -= extra_width;
	  miny -= extra_width;
	  maxx += extra_width;
	  maxy += extra_width;
	}
      gdk_shadow_fb_update (minx, miny, maxx, maxy);
    }
}

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

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      gint extra_width;
  
      minx = x + private->abs_x;
      miny = y + private->abs_y;
      maxx = x + width + private->abs_x;
      maxy = y + height + private->abs_y;
      
      if (!filled)
	{
	  extra_width = (GDK_GC_FBDATA (gc)->values.line_width + 1) / 2;
	  
	  minx -= extra_width;
	  miny -= extra_width;
	  maxx += extra_width;
	  maxy += extra_width;
	}
      gdk_shadow_fb_update (minx, miny, maxx, maxy);
    }
}

static void
gdk_shadow_fb_draw_polygon (GdkDrawable      *drawable,
			    GdkGC            *gc,
			    gint              filled,
			    GdkPoint         *points,
			    gint              npoints)
{
  GdkDrawableFBData *private;

  gdk_fb_draw_polygon (drawable, gc, filled, points, npoints);

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      gint extra_width;
      int i;
  
      minx = maxx = points[0].x;
      miny = maxy = points[0].y;

      for (i = 1; i < npoints; i++)
	{
	  minx = MIN(minx, points[i].x);
	  maxx = MAX(maxx, points[i].x);
	  miny = MIN(miny, points[i].y);
	  maxy = MAX(maxy, points[i].y);
	}
      
      if (!filled)
	{
	  extra_width = (GDK_GC_FBDATA (gc)->values.line_width + 1) / 2;
	  
	  minx -= extra_width;
	  miny -= extra_width;
	  maxx += extra_width;
	  maxy += extra_width;
	}
      gdk_shadow_fb_update (minx + private->abs_x, miny + private->abs_y,
			    maxx + private->abs_x, maxy + private->abs_y);
    }
}

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

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

static void
gdk_shadow_fb_draw_glyphs (GdkDrawable      *drawable,
			   GdkGC            *gc,
			   PangoFont        *font,
			   gint              x,
			   gint              y,
			   PangoGlyphString *glyphs)
{
  GdkDrawableFBData *private;
  GdkRectangle bbox;
  
  _gdk_fb_draw_glyphs (drawable, gc, font, x, y, glyphs, &bbox);
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    gdk_shadow_fb_update (bbox.x + private->abs_x, bbox.y + private->abs_y,
			  bbox.x + private->abs_x + bbox.width, bbox.y + private->abs_y + bbox.height);
}

static void
gdk_shadow_fb_draw_drawable (GdkDrawable      *drawable,
			     GdkGC            *gc,
			     GdkPixmap        *src,
			     gint              xsrc,
			     gint              ysrc,
			     gint              xdest,
			     gint              ydest,
			     gint              width,
			     gint              height)
{
  GdkDrawableFBData *private;
  
  gdk_fb_draw_drawable (drawable, gc, src, xsrc, ysrc, xdest, ydest, width, height);
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    gdk_shadow_fb_update (xdest + private->abs_x, ydest + private->abs_y,
			  xdest + private->abs_x + width, ydest + private->abs_y + height);
}

static void
gdk_shadow_fb_draw_image (GdkDrawable      *drawable,
			  GdkGC            *gc,
			  GdkImage         *image,
			  gint              xsrc,
			  gint              ysrc,
			  gint              xdest,
			  gint              ydest,
			  gint              width,
			  gint              height)
{
  GdkDrawableFBData *private;
  
  gdk_fb_draw_image (drawable, gc, image, xsrc, ysrc, xdest, ydest, width, height);
  
  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    gdk_shadow_fb_update (xdest + private->abs_x, ydest + private->abs_y,
			  xdest + private->abs_x + width, ydest + private->abs_y + height);
}

static void
gdk_shadow_fb_draw_points (GdkDrawable      *drawable,
			   GdkGC            *gc,
			   GdkPoint         *points,
			   gint              npoints)
{
  GdkDrawableFBData *private;

  gdk_fb_draw_points (drawable, gc, points, npoints);

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      int i;
  
      minx = maxx = points[0].x;
      miny = maxy = points[0].y;

      for (i = 1; i < npoints; i++)
	{
	  minx = MIN(minx, points[i].x);
	  maxx = MAX(maxx, points[i].x);
	  miny = MIN(miny, points[i].y);
	  maxy = MAX(maxy, points[i].y);
	}
      
      gdk_shadow_fb_update (minx + private->abs_x, miny + private->abs_y,
			    maxx + private->abs_x, maxy + private->abs_y);
    }
}

static void
gdk_shadow_fb_draw_segments (GdkDrawable      *drawable,
			     GdkGC            *gc,
			     GdkSegment       *segs,
			     gint              nsegs)
{
  GdkDrawableFBData *private;

  gdk_fb_draw_segments (drawable, gc, segs, nsegs);

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      gint extra_width;
      int i;
  
      minx = maxx = segs[0].x1;
      miny = maxy = segs[0].y1;

      for (i = 0; i < nsegs; i++)
	{
	  minx = MIN(minx, segs[i].x1);
	  maxx = MAX(maxx, segs[i].x1);
	  minx = MIN(minx, segs[i].x2);
	  maxx = MAX(maxx, segs[i].x2);
	  
	  miny = MIN(miny, segs[i].y1);
	  maxy = MAX(maxy, segs[i].y1);
	  miny = MIN(miny, segs[i].y2);
	  maxy = MAX(maxy, segs[i].y2);

	}
      
      extra_width = (GDK_GC_FBDATA (gc)->values.line_width + 1) / 2;
      
      minx -= extra_width;
      miny -= extra_width;
      maxx += extra_width;
      maxy += extra_width;
      
      gdk_shadow_fb_update (minx + private->abs_x, miny + private->abs_y,
			    maxx + private->abs_x, maxy + private->abs_y);
    }
}

static void
gdk_shadow_fb_draw_lines (GdkDrawable      *drawable,
			  GdkGC            *gc,
			  GdkPoint         *points,
			  gint              npoints)
{
  GdkDrawableFBData *private;

  gdk_fb_draw_lines (drawable, gc, points, npoints);

  private = GDK_DRAWABLE_FBDATA (drawable);
  if (GDK_IS_WINDOW (private->wrapper))
    {
      gint minx, miny, maxx, maxy;
      gint extra_width;
      int i;
  
      minx = maxx = points[0].x;
      miny = maxy = points[0].y;

      for (i = 1; i < npoints; i++)
	{
	  minx = MIN(minx, points[i].x);
	  maxx = MAX(maxx, points[i].x);
	  miny = MIN(miny, points[i].y);
	  maxy = MAX(maxy, points[i].y);
	}
      
      extra_width = (GDK_GC_FBDATA (gc)->values.line_width + 1) / 2;
	  
      minx -= extra_width;
      miny -= extra_width;
      maxx += extra_width;
      maxy += extra_width;
      
      gdk_shadow_fb_update (minx + private->abs_x, miny + private->abs_y,
			    maxx + private->abs_x, maxy + private->abs_y);
    }
}

#endif
