/**
 *
 * ChoX11: XLib replacement for RISC OS
 *
 * ChoX11 graphics queueing
 *
 * Copyright 2003 by Peter Naulls
 * Written by Peter Naulls and Alan Buckley
 * Polygon fill by Ian Jeffray
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation. No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 */


#define XLIB_ILLEGAL_ACCESS
#define NeedFunctionPrototypes 1
#define NeedNestedPrototypes   1

#include <X11/Xlib.h> 

#include "mi.h"
#include "mifillarc.h"

#include "chox11.h"

#include "kernel.h"

#include <stdio.h>
#include <assert.h>

#define QSIZE 1024
#define QNUM  16

/* Max length of string to store in queue */
#define QSTRING 20


/* Save information for drawing to sprites */
static int *Chox11_savearea;

/**
 * Mapping of X Graphics Functions to GCOL actions
 *
 * This needs work.  It's a cheat for now - the mapping isn't straight
 * forward, and some types of drawing need multiple actions.
 */
static int Chox11_GFunction[16] = {
 /* GXclear         */  0,
 /* GXand           */  2,
 /* GXandReverse    */  6,
 /* GXcopy          */  0,
 /* GXandInverted   */  6,
 /* GXnoop          */  5,
 /* GXxor           */  3,
 /* GXor            */  1,
 /* GXnor           */  4,
 /* GXequiv         */  0,
 /* GXinvert        */  4,
 /* GXorReverse     */  3,
 /* GXcopyInverted  */  0,
 /* GXorInverted    */  7,
 /* GXnand          */  6,
 /* GXset           */  0
};

/* Drawing types */
typedef enum Chox_gaction {
  /* Drawing commands */
  cg_point,
  cg_line,
  cg_lines,
  cg_lines_imm,
  cg_lines_prev_imm,
  cg_fillrect,
  cg_fillpoly,
  cg_fillpoly_imm,
  cg_fillarc,
  cg_fillarc_imm,
  cg_string,
  cg_string_background,
  cg_area,
  cg_area_clip,
  cg_rectangle,
  cg_cleararea,
  cg_circle,

  /* GC synchronisation commands */
  cg_function,
  cg_foreground,
  cg_background,
  cg_font,

  /* RISCOS graphics attribute commands */
  cg_set_foreground,
  cg_set_font,

  /* cg_limit must be last */
  cg_limit
} Chox_gaction;

/* Queue GC synchronisation flags */
#define QALL_NEEDED 0xFFFFFFFF
#define QSET_ALL 0xFF
#define QSET_FOREGROUND 1
#define QSET_FONT       2

/* Lenghts of drawing action, including type */
struct {
   int size;
   int max_needed;
   unsigned int gc_mask;
   unsigned int set_mask;
}  Chox_ginfo[cg_limit] =
 {
   { 3,  8, GCFunction | GCForeground, QSET_FOREGROUND },    /* point        */
   { 5, 10, GCFunction | GCForeground, QSET_FOREGROUND },    /* line         */
   { 2,  7, GCFunction | GCForeground, QSET_FOREGROUND },    /* lines        */
   { 3,  8, GCFunction | GCForeground, QSET_FOREGROUND },    /* lines_imm    */
   { 3,  8, GCFunction | GCForeground, QSET_FOREGROUND },    /* lines_prev_imm */
   { 5, 10, GCFunction | GCForeground, QSET_FOREGROUND },    /* fillrect     */
   { 4,  9, GCFunction | GCForeground, QSET_FOREGROUND },    /* fillpoly     */
   { 5, 10, GCFunction | GCForeground, QSET_FOREGROUND },    /* fillpoly_imm */
   { 7, 12, GCFunction | GCForeground, QSET_FOREGROUND },    /* fillarc      */
   { 3,  8, GCFunction | GCForeground, QSET_FOREGROUND },    /* fillarc_imm  */
   { 4 + QSTRING / 4, 12 + QSTRING / 4, GCFunction | GCForeground | GCBackground | GCFont, QSET_FONT},/* string       */
   { 8 + QSTRING / 4, 20 + QSTRING / 4, GCFunction | GCForeground | GCBackground | GCFont, QSET_FONT},/* string_background */
   { 4, 6, GCFunction, 0},              /* area copy    */
   { 8, 10, GCFunction, 0},             /* area copy with clipping   */
   { 5,  10, GCFunction | GCForeground, QSET_FOREGROUND },   /* rectangle    */
   { 6,  6, 0, 0},                                           /* clear area   */
   { 4,  9, GCFunction | GCForeground, QSET_FOREGROUND },    /* circle       */

   /* GC synchronisation commands */
   { 2, 2, 0, 0},   /* function   */
   { 2, 2, 0, 0},   /* foreground */
   { 2, 2, 0, 0},   /* background */
   { 2, 2, 0, 0},   /* font       */

   /* RISCOS graphics attribute commands */
   { 1, 1, 0, 0},   /* set_foreground    */
   { 1, 1, 0, 0}    /* set_font          */

};


typedef struct Chox_queue {
  XDrawable    *draw;       /* Current Drawable for queue, or NULL */
  unsigned int  items;
  unsigned int  needed_mask;
  unsigned int  set_mask;
  Bool is_screen;
  sprite_areainfo *fill_sprite; /* Sprite used for stipples or tiling */
  int minx; /* Bounding rect in pixels (inclusive) */
  int miny;
  int maxx;
  int maxy;
  Chox_gaction  action[QSIZE];
} Chox_queue;


static Chox_queue queues[QNUM];
static sprite_outputstate Chox11_state;
static unsigned int last_flushed = -1;


static void Chox11Queue_IncludePoint(Chox_queue *queue, int x, int y) {
  if (x < queue->minx) queue->minx = x;
  if (x > queue->maxx) queue->maxx = x;
  if (y < queue->miny) queue->miny = y;
  if (y > queue->maxy) queue->maxy = y;
}

static void Chox11Queue_WRedrawStart(Chox_queue *queue, wimp_rect *clip_rect,
                                     window_redrawblock *redraw,
                                     BOOL *more) {
  int border_width = queue->draw->data.window.border_width;

  redraw->window = queue->draw->data.window.handle;
  redraw->rect.min.x = clip_rect->min.x * 2 + border_width;
  redraw->rect.min.y = -clip_rect->max.y * 2 - border_width - 2;
  /* max is exclusive for update window, but inclusive in queue */
  redraw->rect.max.x = (clip_rect->max.x+1) * 2 + border_width;
  redraw->rect.max.y = -(clip_rect->min.y-1) * 2 - border_width - 2;

  Wimp_UpdateWindow(redraw, more);
}                       

static int clip_rect_iter;

static void Chox11Queue_GetNextClipRect(Chox_queue *queue, wimp_rect *clip_rect, BOOL *more) {
  *more = FALSE;
  if (clip_rect_iter != -1) {
    XRectangle *rect;
    while (!*more && clip_rect_iter < queue->draw->gcinfo.clip_rects->rect_count) {
      rect = &(queue->draw->gcinfo.clip_rects->rects[clip_rect_iter]);
      clip_rect->min.x = rect->x + queue->draw->gcinfo.values.clip_x_origin;
      clip_rect->min.y = rect->y + queue->draw->gcinfo.values.clip_y_origin;
      clip_rect->max.x = rect->x + rect->width + queue->draw->gcinfo.values.clip_x_origin - 1;
      clip_rect->max.y = rect->y + rect->height  + queue->draw->gcinfo.values.clip_y_origin - 1;
      if (clip_rect->min.x < queue->minx) clip_rect->min.x = queue->minx;
      if (clip_rect->min.y < queue->miny) clip_rect->min.y = queue->miny;
      if (clip_rect->max.x > queue->maxx) clip_rect->max.x = queue->maxx;
      if (clip_rect->max.y > queue->maxy) clip_rect->max.y = queue->maxy;
      
	if (clip_rect->min.x <= clip_rect->max.x && clip_rect->min.y <= clip_rect->max.y) *more = TRUE;
      clip_rect_iter++;   
    }
  }
}

static void Chox11Queue_GetFirstClipRect(Chox_queue *queue, wimp_rect *clip_rect, BOOL *clip_gc, BOOL *more) {
  if (queue->draw->gcinfo.clip_rects == NULL || queue->draw->gcinfo.clip_rects->rect_count == 1) {
    *more = TRUE;
    if (clip_gc) *clip_gc = (queue->draw->gcinfo.clip_rects->rect_count == 1);
    clip_rect_iter = -1;
    clip_rect->min.x = queue->minx;
    clip_rect->min.y = queue->miny;
    clip_rect->max.x = queue->maxx;
    clip_rect->max.y = queue->maxy;
  } else {
    clip_rect_iter = 0;
    Chox11Queue_GetNextClipRect(queue, clip_rect, more);
    if (clip_gc) *clip_gc = *more;
  }
}

/**
 * Sets the new RISC OS clip rectangle
 *
 * clip is the new rectangle (OS co-ords inclusive)
 * old  is updated to the current clip rectangle (OS co-ords inclusive)
 * eig_x, eig_y are the eigen factors for the graphics destination
 *
 * Returns True and sets a new clip rectangle if the clip intersects
 *           the current graphics clip rectangle. clip is updated
 *           to the new clip area.
 *         False otherwise
 *
 */
static Bool Chox11Queue_IntersectClipRect(wimp_rect *clip, wimp_rect *old, int eig_x, int eig_y) {

    GFX_GetClip(old);

    old->min.x <<= eig_x;
    old->min.y <<= eig_y;
    old->max.x <<= eig_x;
    old->max.y <<= eig_y;

    if (old->min.x > clip->max.x || old->min.y > clip->max.y
        || old->max.x < clip->min.x || old->max.y < clip->min.y) {
       return False; /* No intersection */
    }

    if (clip->min.x < old->min.x) clip->min.x = old->min.x;
    if (clip->min.y < old->min.y) clip->min.y = old->min.y;
    if (clip->max.x > old->max.x) clip->max.x = old->max.x;
    if (clip->max.y > old->max.y) clip->max.y = old->max.y;

    GFX_SetClip(clip);

    return True;
}

/**
 * Get the bounding rect of the text in pixels (X11 coordinates)
 */
void CalculateTextBounds(Font font, int x, int y, const char *text, int len, int *bounds) {
  unsigned int font_handle;
  char *end;
  int xstop = 0x7fffffff;
  int ystop = 0x7fffffff;
  unsigned int flags = font_plot_GIVENHANDLE | font_STRLEN | font_scan_BBOX;
  font_scanstringblock block;
          
  if (font == NULL) {
    /* Default to wimp font */        
    Font_GetWimpFont(&font_handle);
  } else
    font_handle = ((Chox_Font *)font)->handle;

   block.offsets.space.x = 0;
   block.offsets.space.y = 0;
   block.offsets.letter.x = 0;
   block.offsets.letter.y = 0;
   block.split_ch = -1; /* No split char */

   Font_ScanString(font_handle, text, &end, flags, &xstop,&ystop, &block, NULL, &len);

   bounds[0] = x + block.bbox.min.x/800;
   bounds[1] = y - block.bbox.max.y/800;
   bounds[2] = x + block.bbox.max.x/800;
   bounds[3] = y - block.bbox.min.y/800;
}

static FillPolygon(XPoint *points, int npoints, int shape, int mode,
                   int offx, int offy) {

  /* Edge coherence polygon fill routine (even-odd winding) */

  /* cx11_polynode for edge table linked list                        */
  /* Double-forward linked to allow sort-threading without copying   */

  typedef struct cx11_polynode
  {
    int x,x2,y,flag;
    int inc,num,den,i;
    struct  cx11_polynode *next,*sort;
  } cx11_polynode;

  cx11_polynode *AET,*tmp,*tmq,**tmw,*sort;
  cx11_polynode **ET;
  int y,t,v,ry,miny,maxy,w,ysize;
  cx11_polynode *inputs;
  cx11_polynode *iet, *cur=NULL;

  if (npoints < 3) return;

  inputs = alloca(sizeof(cx11_polynode) * npoints);
  iet = inputs;

  miny=100000;
  maxy=0;

  for (;npoints;npoints--)
  {
    int x = points[npoints-1].x;
    int y = points[npoints-1].y;
    if (mode)
    {
      /* Translate points from previous position, here! */
      /* TODO: */
      /* x += d->x */
      /* y += d->y */
    }
    if (y<miny) miny=y;
    if (y>maxy) maxy=y;
    inputs[npoints-1].x=x;
    inputs[npoints-1].y=y;
    inputs[npoints-1].flag=0;
    inputs[npoints-1].next=cur;
    cur=&inputs[npoints-1];
  }

  ysize=maxy-miny;
  ET = alloca(sizeof(cx11_polynode *) * (ysize + 1));

  /* Clear the Edge Table */

  for (y=0; y<ysize; y++) ET[y]=NULL;

  /* Now put edges in to the table where they're first encountered */

  for (tmp=iet; tmp; tmp=tmp->next)
  {
    for (y=miny; y<=maxy; y++)   /* Check each vertical cx11_polynode */
    {
      if (tmp->flag==0)    /* Used already? */
      {
        if (tmp->y == y)   /* Does this edge start at this y position?  */
        {
          /* A bit messy as it's not a circular list. Ah well.  */
  
          v=tmp->next ? tmp->next->y : iet->y;
          w=tmp->next ? tmp->next->x : iet->x;

          if (tmp->y != v)
          {
            ry=y;
//            tmq = (cx11_polynode *) malloc(sizeof(cx11_polynode));
            tmq = (cx11_polynode *) alloca(sizeof(cx11_polynode));
            if ( !tmq )
            {
              return;
            }
  
            /* Now determine which is the miny end of the edge and obtain  */
            /* "minx" from that end (NOT the minimum x-value of the line)  */
  
            if (tmp->y > v)
            { tmq->den=tmp->y-v; tmq->y=tmp->y; ry=v; tmq->x2=tmp->x; tmq->x=w; }
            else
            { tmq->den=v-tmp->y; tmq->y=v; tmq->x=tmp->x; tmq->x2=w; }

            tmq->num=tmq->x2-tmq->x;
            tmq->inc=tmq->den;
  
            /* If we find that the numerator is negative, make it positive,  */
            /* but we must then step x NEGATIVELY rather than positively...  */
  
            if (tmq->num > 0) tmq->i=1; else { tmq->i=-1; tmq->num  = -tmq->num; }

            /* Phew. Now stick it in the Edge Table...  */
  
            tmq->next=ET[ry-miny];
            ET[ry-miny]=tmq;

          }
          tmp->flag=1;
          goto fastskip;  /* Yes yes, horrid GOTOs, but it's FAST.  */
        }
      }
    }
fastskip:;
  }
  
  /* Create an empty Active Edge Table... (step 2)  */

  AET = alloca(sizeof(cx11_polynode));

  AET->next=NULL;

  /* Now move from top to bottom of the polygon, filling in spans. */

  /* Step 1 : Set y to smallest Y in edge table. */

  for (y=miny; y<maxy; y++)
  {
    /* Step 3.i : remove from AET edges for which y=max */

    for (tmp=AET; tmp->next;)
    {
      if (tmp->next->y == y) {
        tmq=tmp->next; tmp->next=tmp->next->next;
      } else {
        tmp=tmp->next;
      }
    }

    /* Step 3.ii : Add to AET, ET bucket  */

    for (tmp=ET[y-miny]; tmp; tmp=tmp->next)
    {
      tmq = alloca(sizeof(cx11_polynode));
      if (!tmq) return;

      tmq->x=tmp->x;
      tmq->x2=tmp->x2;
      tmq->y=tmp->y;
      tmq->num=tmp->num;
      tmq->den=tmp->den;
      tmq->inc=tmp->inc;
      tmq->i=tmp->i;
      tmq->next=AET->next;
      AET->next=tmq;
    }

    /* Step 3.iii : Sort AET by x value  */

    sort=NULL;
    for (tmp=AET->next; tmp; tmp=tmp->next)
    {
      for (tmw=&sort; *tmw; tmw=&((*tmw)->sort))
      {
        if ((*tmw)->x >= tmp->x) break;
      }
      tmp->sort=*tmw;
      *tmw=tmp;
    }

    /* Step 3.iv : Fill in spans using pairs of coordinates from AET  */

    for (tmp=sort; tmp;)
    {
      if (tmp->x < tmp->sort->x) {
        GFX_Move(offx + tmp->x * 2, offy - y * 2);
        GFX_Draw(offx + tmp->sort->x * 2, offy - y * 2);
      }

      /* Step 3.vi : update X values for all edges in AET     */

      for (t=0; t<2; t++)
      {
        tmp->inc=tmp->inc + tmp->num;
        while (tmp->inc > tmp->den)
        {
          tmp->x = tmp->x + tmp->i;
          tmp->inc = tmp->inc - tmp->den;
        }
        tmp=tmp->sort;
      }
    }
  }
}


/**
 * Plot the sprite in the given context
 */
static void Chox11Queue_PlotSpriteToScreen(sprite_area area, int x, int y, int plot_action) {
  os_error *err;
  wimp_point pos = { x, y };

  if ((err = Sprite_PlotScaled(area, X_SPRITE_NAME, &pos, plot_action | 32, NULL, Chox11_ScreenPixTrans)) != 0) {
    printf("Chox11Queue_PlotSpriteToScreen failed \n%s\n", err->errmess);
  }
}


static void Chox11Queue_SetupPlotInfo(sprite_area area)
{
  _kernel_swi_regs regs;

  regs.r[0] = (unsigned int)area;
  regs.r[1] = (unsigned int)X_SPRITE_NAME;
  regs.r[2] = -1;   /* Current mode */
  regs.r[3] = -1;   /* Current palette */
  regs.r[4] = 0;    /* Get size of buffer */
  regs.r[5] = 2 | 16; /* R1 - name of sprite and can use full palette words */
  regs.r[6] = 0;
  regs.r[7] = 0;

  if (Chox11_ScreenPixTrans) free(Chox11_ScreenPixTrans);
  Chox11_ScreenPixTrans = NULL;

  /* Get the size required for the buffer */
  _kernel_swi(SWI_ColourTrans_GenerateTable, &regs, &regs);

  if (regs.r[4]) {
    Chox11_ScreenPixTrans = malloc(regs.r[4]);
    if (Chox11_ScreenPixTrans) {
      regs.r[4] = (unsigned int)Chox11_ScreenPixTrans;
      /* Actually read the buffer */
      _kernel_swi(SWI_ColourTrans_GenerateTable, &regs, &regs);
     }
   }
}


/**
 * Fill the queue's boundary with the fill sprite
 */
static void Chox11Queue_StippleFill(Chox_queue *queue, int gcol_action) {
  sprite_info fill_info;
  int startx;
  int x;
  int y;
  int width = (queue->maxx - queue->minx + 1) << 1;

  Sprite_ReadInfo(queue->fill_sprite, X_SPRITE_NAME, &fill_info);

  /* Get position of first tile to intersect the clip area */
  startx = queue->draw->gcinfo.values.ts_x_origin - fill_info.width - queue->minx;
  while(startx > 0) startx -= fill_info.width;
  while(startx + fill_info.width < 0) startx += fill_info.width;
                                 
  y = queue->draw->gcinfo.values.ts_y_origin - fill_info.height - queue->miny;
  while(y > 0) y -= fill_info.height;
  while(y + fill_info.height < 0) y += fill_info.height;

  /* Convert to co-ordinates to os units */
  startx <<= 1;
  fill_info.width <<= 1;
  y = (queue->maxy - queue->miny + 1 - y) << 1;
  fill_info.height <<= 1;

  /* Plot the stipple */
  while (y >= 0) {
    y -= fill_info.height;
    x = startx;
    while (x < width) {
      Sprite_Plot(queue->fill_sprite, X_SPRITE_NAME, x, y, gcol_action);
      x += fill_info.width;
    }
  }
}


/**
 * Plot the pixel sprite - x,y is the bottom left of where to plot it in OS coordinates.
 */
static void Chox11Queue_PlotPixelSprite(sprite_areainfo *pixel_sprite, Chox_queue *queue, int x, int y) {
  int colours[2];
  char pixTrans[16];
  _kernel_swi_regs regs;
  _kernel_oserror *err;
//  wimp_point pos = { x, y };

  colours[0] = queue->draw->gcinfo.values.background << 8;
  colours[1] = queue->draw->gcinfo.values.foreground << 8;

  /* Setup up colour translation table */
  regs.r[0] = 0; /* Mode 0 has same characteristics as our 1 bpp stipple */
  regs.r[1] = (unsigned int)colours;
  regs.r[2] = -1; /* Current mode */
  regs.r[3] = -1; /* Current palette */
  regs.r[4] = (int)pixTrans;
  regs.r[5] = 16; /* can use full palette words */
  regs.r[6] = 0;
  regs.r[7] = 0;
  if (_kernel_swi(SWI_ColourTrans_GenerateTable, &regs, &regs) == 0) regs.r[7] = (int)pixTrans;

  /* Plot the sprite */
  regs.r[0] =  52 + 256;
  regs.r[1] = (unsigned int)pixel_sprite;
  regs.r[2] = (unsigned int)X_SPRITE_NAME;
  regs.r[3] = x;
  regs.r[4] = y;
  regs.r[5] = Chox11_GFunction[queue->draw->gcinfo.values.function]|8|32; /* plot action + use mask + pixtrans contains wide colour entries */
  regs.r[6] = 0; /* No scale factors i.e. 1:1 */
 
  if ((err = _kernel_swi(SWI_OS_SpriteOp, &regs, &regs)) != 0) {
    printf("Chox11Queue_PlotPixelSprite failed \n%s\n", err->errmess);
  }

  /* This doesn't work properly for some reason */
  //Sprite_PlotScaled(pixel_sprite, X_SPRITE_NAME, &pos, Chox11_GFunction[queue->draw->gcinfo.values.function] | 8 | 32, NULL, NULL);
}


/**
 * Plot the tile showing only the parts defined by the pixel sprite.
 *
 * x,y is top left for plot of whole screen
 */
static void Chox11Queue_FillTiled(sprite_areainfo *pixel_sprite, Chox_queue *queue, int x, int y) {
  sprite_areainfo *tile_sprite = queue->fill_sprite;
  int startx;
  int xcol;
  int yrow;
  int xscreen, yscreen;
  wimp_point mask_pos;
  sprite_outputstate old_state;
  sprite_info tile_info;

  Sprite_ReadInfo(tile_sprite, X_SPRITE_NAME, &tile_info);

  startx = queue->draw->gcinfo.values.ts_x_origin;
  while (startx > queue->minx) startx -= tile_info.width;
  while (startx + tile_info.width < queue->minx) startx += tile_info.width;
 
  yrow = queue->draw->gcinfo.values.ts_y_origin;
  while (yrow > queue->miny) yrow -= tile_info.height;
  while (yrow + tile_info.height < queue->miny) yrow += tile_info.height;

  if (queue->is_screen) {
    if (screen_bpp < 16 && Chox11_ScreenPixTrans == NULL) {
      Chox11Queue_SetupPlotInfo(tile_sprite);
    }
  }

  while (yrow <= queue->maxy) {
    yscreen = y - (yrow + tile_info.height - 1) * 2;
    mask_pos.y = (tile_info.height - queue->maxy + yrow) * 2;
    xcol = startx;

    while (xcol <= queue->maxx) {
      xscreen = x + xcol * 2;
      mask_pos.x = (queue->minx - xcol) * 2;

      /* Plot shifted pixel sprite to mask to get correctly selected pixels */
      Sprite_RedirectMask(pixel_sprite, X_SPRITE_NAME, NULL, &old_state);
      Sprite_PlotMask(pixel_sprite, X_SPRITE_NAME, &mask_pos);
      Sprite_UnRedirect(&old_state);

      /* Plot tile_sprite with its new mask to the screen */
      if (queue->is_screen) {
        Chox11Queue_PlotSpriteToScreen(tile_sprite, xscreen, yscreen, 8 | Chox11_GFunction[queue->draw->gcinfo.values.function]);
      } else {
        Sprite_Plot(tile_sprite, X_SPRITE_NAME, xscreen, yscreen, 8 | Chox11_GFunction[queue->draw->gcinfo.values.function]);
      }
      xcol += tile_info.width;
    }
    yrow += tile_info.height;
  }
}


static void Chox11Queue_Draw(Chox_queue *gqueue, int offx, int offy) {
  unsigned int item = 0;

  while (item < gqueue->items) {
    Chox_gaction *action = &gqueue->action[item];

    switch (action[0]) {
      case cg_point:
        GFX_PlotPoint(offx + action[1] * 2, offy - action[2] * 2);
        break;

      case cg_line:
        /* Simple line */
        GFX_Move(offx + action[1] * 2, offy - action[2] * 2);
        GFX_Draw(offx + action[3] * 2, offy - action[4] * 2);
        break;

      case cg_lines:
        /* Set of lines in the queue */
        {
          int npoints = action[1];
          int *points = (int *)&action[2];
          int point;

          GFX_Move(offx + points[0] * 2, offy - points[1] * 2);

          for (point = 1; point < npoints; point++) {
            GFX_Draw(offx + points[point * 2] * 2,
                     offy - points[point * 2 + 1] * 2);
          }

          item += npoints * 2;
        }
        break;

      case cg_lines_imm:
        /* Set of lines in a specifed array */
        {
          int npoints = action[1];
          XPoint *points = (XPoint *)action[2];
          int point;

          GFX_Move(offx + points[0].x * 2, offy - points[0].y * 2);

          for (point = 1; point < npoints; point++) {
            GFX_Draw(offx + points[point].x * 2, offy - points[point].y * 2);
          }
        }
        break;

      case cg_lines_prev_imm:
        /* Set of lines in a specifed array relative to previous co-ordinates */
        {
          int npoints = action[1];
          XPoint *points = (XPoint *)action[2];
          int point;
          int x = offx + points[0].x * 2;
          int y = offy - points[0].y * 2;

          GFX_Move(x, y);

          for (point = 1; point < npoints; point++) {
            x += points[point].x * 2;
            y -= points[point].y * 2;
            GFX_Draw(x, y);
          }
        }
        break;

      case cg_fillrect:  
        /* Filled rectangle */
        GFX_RectangleFill(offx + action[1] * 2,
                          offy - (action[2] + action[4] - 1) * 2,
                          (action[3]-1) * 2,
                          (action[4]-1) * 2);
        break;

      case cg_fillpoly:
        /* Set of points in the queue */
        {
          int npoints = action[1];
          XPoint *points = (XPoint *)&action[4];

          FillPolygon(points, npoints, action[2], action[3],
                      offx, offy);

          item += npoints;
        }
        break;

      case cg_fillpoly_imm:
        /* Set of lines in a specifed array */
        {
          int npoints = action[1];
          XPoint *points = (XPoint *)action[4];

          FillPolygon(points, npoints, action[2], action[3],
                      offx, offy);
        }
        break;

      case cg_fillarc:
        {
          miArc arc;

          arc.x = action[1];
          arc.y = action[2];
          arc.width  = action[3];
          arc.height = action[4];
          arc.angle1 = action[5];
          arc.angle2 = action[6];

          miPolyFillArc(1, &arc, offx, offy);
        }
        break;

      case cg_fillarc_imm:
        {
          miPolyFillArc(action[1], (miArc *)action[2], offx, offy);
        }
        break;

      case cg_string_background:
        {
          int coord_base = 4 + QSTRING / 4;

          if (gqueue->is_screen) {
            Chox11_SetGCOL(gqueue->draw->gcinfo.values.background, 0);
          } else {
            GFX_SetColour(0, gqueue->draw->gcinfo.values.background);
          }

          GFX_RectangleFill(offx + action[coord_base] * 2, offy - action[coord_base+3] * 2,
                           (action[coord_base+2] - action[coord_base]) * 2,
                           (action[coord_base+3] - action[coord_base+1]) * 2);
        }
        /* Deliberately drop through to next case to draw the string */

      case cg_string:
        {
          font_handle fhandle;
          /* TODO: Use font from draw->gcinfo.values.font */
          Chox_Font *font = (Chox_Font *)gqueue->draw->gcinfo.values.font;
          char *text = (action[1] <= QSTRING) ? (char *)&action[4] : (char *)action[4];
          
          if (font == NULL) {
            /* Default to wimp font */
            Font_GetWimpFont(&fhandle);

          } else {
            fhandle = font->handle;
          }

          Font_Paint3(fhandle,
                      text,
                      font_plot_OSCOORS | font_STRLEN | font_plot_GIVENHANDLE,
                      offx + action[2] * 2,
                      offy - action[3] * 2,
                      NULL, NULL,
                      action[1]);
        }
        break;

      case cg_area:
        {
          XDrawable *srcd = (XDrawable *)action[1];
          int dest_x = action[2];
          int dest_y = action[3];

          if (gqueue->is_screen) {
            if (screen_bpp < 16 && Chox11_ScreenPixTrans == NULL)
              Chox11Queue_SetupPlotInfo(&srcd->data.image.pixmap);

            Chox11Queue_PlotSpriteToScreen(&srcd->data.image.pixmap,
                                           offx + (dest_x << screen_eig.x),
                                           offy - (dest_y << screen_eig.y),
                                           Chox11_GFunction[gqueue->draw->gcinfo.values.function]);
          } else {
            Sprite_Plot(&srcd->data.image.pixmap, X_SPRITE_NAME,
                        offx + dest_x * 2, offy - dest_y * 2,
                        Chox11_GFunction[gqueue->draw->gcinfo.values.function]);
          }
        }
        break;

      case cg_area_clip:
        {
          XDrawable *srcd = (XDrawable *)action[1];
          int dest_x = action[2];
          int dest_y = action[3];
          wimp_rect clip, old_clip;         
          int eig_x, eig_y;

          if (gqueue->is_screen) {
             eig_x = screen_eig.x;
             eig_y = screen_eig.y;
          } else {
             eig_x = 1;
             eig_y = 1;
          }

          clip.min.x = offx + (action[4] << eig_x);
          clip.min.y = offy - (action[5] << eig_y);
          clip.max.x = offx + (action[6] << eig_x);
          clip.max.y = offy - (action[7] << eig_y);

          if (Chox11Queue_IntersectClipRect(&clip, &old_clip, eig_x, eig_y)) {
            if (gqueue->is_screen) {
              if (screen_bpp < 16 && Chox11_ScreenPixTrans == NULL)
                Chox11Queue_SetupPlotInfo(&srcd->data.image.pixmap);

                Chox11Queue_PlotSpriteToScreen(&srcd->data.image.pixmap,
                                         offx + (dest_x << screen_eig.x),
                                         offy - (dest_y << screen_eig.y),
                                         Chox11_GFunction[gqueue->draw->gcinfo.values.function]);
            } else {
              Sprite_Plot(&srcd->data.image.pixmap, X_SPRITE_NAME,
                          offx + dest_x * 2, offy - dest_y * 2,
                          Chox11_GFunction[gqueue->draw->gcinfo.values.function]);
            }
            GFX_SetClip(&old_clip); /* Restore clip rect */
          }
        }
        break;

      case cg_rectangle:
        {
          /* Plot a rectangle outline */
          GFX_Rectangle(offx + action[1] * 2,
                          offy - (action[2] + action[4]) * 2,
                          action[3] * 2,
                          action[4] * 2);
        }
        break;

      case cg_circle:
        {
           /* Plot a circle outline */
           GFX_Circle(offx + action[1] * 2, offy - action[2] * 2, action[3] * 2);
        }
        break;

      case cg_cleararea:  
        /* Clear an area */
        /* Colour required is different for the screen or a sprite.
           For a screen it needs to be converted to a palette colour
           and the nearest colour in the current screen selected
           using ColourTrans. For a sprite the number given here
           it is already the correct colour number for the sprite.
        */
        if (gqueue->is_screen) {
          Chox11_SetGCOL(action[5], 0);
        } else {
          GFX_SetColour(0, action[5]);
        }

        GFX_RectangleFill(offx + action[1] * 2,
                          offy - (action[2] + action[4] - 1) * 2,
                          action[3] * 2 - 2,
                          action[4] * 2 - 2);
        break;

      case cg_function:
        /* Set the function for plots */
        gqueue->draw->gcinfo.values.function = action[1];
        break;
          
      case cg_foreground:
        /* Set the foreground colour */
        gqueue->draw->gcinfo.values.foreground = action[1];
        break;

      case cg_background:
        /* Set the background colour */
        gqueue->draw->gcinfo.values.background = action[1];
        break;

      case cg_font:
        /* Set the font for text operations */
        gqueue->draw->gcinfo.values.font = action[1];
        break;

      case cg_set_foreground:
        /* Set RISC OS graphics foreground */
        /* Colour required is different for the screen or a sprite.
           For a screen it needs to be converted to a palette colour
           and the nearest colour in the current screen selected
           using ColourTrans. For a sprite the number given here
           it is already the correct colour number for the sprite.
           */
        if (gqueue->is_screen) {
          Chox11_SetGCOL(gqueue->draw->gcinfo.values.foreground,
                         Chox11_GFunction[gqueue->draw->gcinfo.values.function] );
        } else
          GFX_SetColour(Chox11_GFunction[gqueue->draw->gcinfo.values.function],
                        gqueue->draw->gcinfo.values.foreground);
        break;

        case cg_set_font:
        {
          /* Set RISC OS font */
          Chox_Font *font = (Chox_Font *)gqueue->draw->gcinfo.values.font;
          unsigned int font_handle;
          
          if (font == NULL) {
             /* Default to wimp font */        
             Font_GetWimpFont(&font_handle);
          } else
             font_handle = font->handle;

          if (gqueue->fill_sprite) {
             /* Plotting to a 1 bpp bitmap so just use black and white */
            ColourTrans_SetFontColours(font_handle, 0, 0xFFFFFF00, 1);
          } else {
            ColourTrans_SetFontColours(font_handle,
              ChoX11_InternalColourToColourTrans(gqueue->draw->gcinfo.values.background),
              ChoX11_InternalColourToColourTrans(gqueue->draw->gcinfo.values.foreground), 14);
          }
        }
        break;

      default:
        fprintf(stdout, "Graphics command not handled: %d\n", action[0]);
        assert(0);
        break;
    }
    item += Chox_ginfo[action[0]].size;
  }
}


static void Chox11Queue_Free(Chox_queue *gqueue) {
  unsigned int item = 0;

  while (item < gqueue->items) {
    Chox_gaction *action = &gqueue->action[item];

/* Following few commented out lines are to check
 the queue if there is a drawing problem */
//    {
//      int item;
//      printf("Queue action: ");
//      for (item = 0; item < Chox_ginfo[action[0]].size; item++) {
//        printf("%d ", action[item]);
//      }
//      printf("\n");
//    }

    switch (action[0]) {
      case cg_point:
      case cg_line:
        break;

      case cg_lines:
        {
          unsigned int npoints = action[1];
          item += npoints * 2;
        }
        break;

      case cg_lines_imm:
      case cg_lines_prev_imm:
      case cg_fillrect:
      case cg_fillarc:
      case cg_fillarc_imm:
        break;

      case cg_fillpoly:
        {
          unsigned int npoints = action[1];
          item += npoints;
        }
        break;

      case cg_fillpoly_imm:
        break;

      case cg_string_background:
      case cg_string:
        {
          int length = action[1] & ~(1 << 31);

          if (length > QSTRING)
            free((char *)action[4]);
        }
        break;

      case cg_area:
      case cg_area_clip:
        break;

      case cg_rectangle:
      case cg_circle:
        break;

      case cg_cleararea:
        break;

      case cg_function:
      case cg_foreground:
      case cg_background:
        break;

      case cg_font:
        Chox11Font_Release((Chox_Font *)action[1]);
        break;

    case cg_set_foreground:
    case cg_set_font:
        break;

      default:
        fprintf(stderr, "Graphics command not handled: %d\n", action[0]);
        assert(0);
        break;
    }
    item += Chox_ginfo[action[0]].size;
  }

  free(gqueue->fill_sprite);
  gqueue->fill_sprite = NULL;

  gqueue->minx = 32767;
  gqueue->maxx = 0;
  gqueue->miny = 32767;
  gqueue->maxy = 0;  
}


/**
 * Remove all items from a queue, drawing them to its drawable
 */
static void Chox11Queue_Empty(Chox_queue *queue) {
  XDrawable *draw;
  sprite_areainfo *pixel_sprite = 0;
  int draw_height;
  int draw_width;

  if (!queue || queue->items == 0)
    return;

  draw = queue->draw;
  queue->is_screen = draw->isWindow;

  draw_width  = draw->width;
  draw_height = draw->height;

  /* Sanity check on bounds */
  if (queue->minx < 0) queue->minx = 0;
  if (queue->miny < 0) queue->miny = 0;
  if (queue->maxx >= draw_width) queue->maxx = draw_width - 1;
  if (queue->maxy >= draw_height) queue->maxy = draw_height - 1;

  if (draw->gcinfo.clip_rects) {
    if (draw->gcinfo.clip_rects->rect_count == 0) {
      /* No rectangle in clip rect means suppress all output */
      queue->minx = queue->maxx + 1;
    } else {
      if (queue->minx < draw->gcinfo.clip_rects->xmin + draw->gcinfo.values.clip_x_origin)
         queue->minx = draw->gcinfo.clip_rects->xmin + draw->gcinfo.values.clip_x_origin;
      if (queue->miny < draw->gcinfo.clip_rects->ymin + draw->gcinfo.values.clip_y_origin)
         queue->miny = draw->gcinfo.clip_rects->ymin + draw->gcinfo.values.clip_y_origin;
      if (queue->maxx > draw->gcinfo.clip_rects->xmax + draw->gcinfo.values.clip_x_origin)
         queue->maxx = draw->gcinfo.clip_rects->xmax + draw->gcinfo.values.clip_x_origin;
      if (queue->maxy > draw->gcinfo.clip_rects->ymax + draw->gcinfo.values.clip_y_origin)
         queue->maxy = draw->gcinfo.clip_rects->ymax + draw->gcinfo.values.clip_y_origin;
    }
  }

//  fprintf(stdout, "queue empty: %p %d %d\n", draw->data.window.handle, draw->isWindow, queue->items);

  if (queue->minx <= queue->maxx && queue->miny <= queue->maxy) {
    if (draw->gcinfo.values.fill_style != FillSolid) {
      int size;
      int pixel_width;
      int pixel_height;
   
      pixel_width = queue->maxx - queue->minx + 1;
      pixel_height = queue->maxy - queue->miny + 1;
  
      size = sizeof(sprite_areainfo) + Sprite_MemorySizeBpp(pixel_width, pixel_height, 1, sprite_HASMASK);
  
      pixel_sprite = alloca(size);
  
      if (pixel_sprite) {
        screen_modeval mode;
        int old_foreground = draw->gcinfo.values.foreground;
        int old_background = draw->gcinfo.values.background;
        int old_function = draw->gcinfo.values.function;
        sprite_outputstate old_state;
  
        draw->gcinfo.values.foreground = 1;
        draw->gcinfo.values.background = 0;
        draw->gcinfo.values.function = GXcopy;
  
        mode.sprite_mode.istype   = 1;
        mode.sprite_mode.horz_dpi = 90;
        mode.sprite_mode.vert_dpi = 90;
        mode.sprite_mode.type  = 1;
        pixel_sprite->areasize    = size;
        pixel_sprite->numsprites  = 0;
        pixel_sprite->firstoffset = 16;
        pixel_sprite->freeoffset  = 16;
  
        queue->is_screen = False;
  
  // printf("*** Using stipple (style %d)\n", draw->gcinfo.values.fill_style);
        Sprite_Create(pixel_sprite, X_SPRITE_NAME, 0, pixel_width, pixel_height, mode);
        Sprite_CreateMask(pixel_sprite, X_SPRITE_NAME);
  
        /* Switch output to mask */
  
        Sprite_RedirectMask(pixel_sprite, X_SPRITE_NAME, NULL, &old_state);
  
        GFX_SetColour(16, 0); /* Set background colour */
  
        GFX_VDU(16); /* Clear mask */
  
        GFX_SetColour(0, 1); /* Set foreground colour */
        Chox11Queue_Draw(queue, -queue->minx * 2, queue->maxy * 2);
  
        if (draw->gcinfo.values.fill_style == FillStippled) Chox11Queue_StippleFill(queue, 2);
        Sprite_UnRedirect(&old_state);
  
        if (draw->gcinfo.values.fill_style == FillOpaqueStippled) {
           Sprite_Redirect(pixel_sprite, X_SPRITE_NAME, Chox11_savearea, &Chox11_state);
           Chox11Queue_StippleFill(queue, 0);
           Sprite_UnRedirect(&Chox11_state);
        } else if (draw->gcinfo.values.fill_style == FillStippled) {
           Sprite_Redirect(pixel_sprite,  X_SPRITE_NAME, Chox11_savearea, &Chox11_state);
           GFX_SetColour(16, 1); /* Set background colour */
           GFX_VDU(16); /* Clear whole area as mask will do stippling */
           Sprite_UnRedirect(&Chox11_state);
        }
  
        draw->gcinfo.values.foreground = old_foreground;
        draw->gcinfo.values.background = old_background;
        draw->gcinfo.values.function = old_function;
      }
    }
  
    if (draw->gcinfo.clip_sprite != NULL) {
      sprite_areainfo *sarea = draw->gcinfo.clip_sprite;
      int height = ((sprite_header *)&sarea[1])->height;
  
      queue->is_screen = False;
      Sprite_Redirect(draw->gcinfo.clip_sprite, X_SPRITE_NAME, Chox11_savearea,
                      &Chox11_state);
  
      if (pixel_sprite) {
        if (draw->gcinfo.values.fill_style == FillTiled) {
          Chox11Queue_FillTiled(pixel_sprite, queue,
                   -draw->gcinfo.values.clip_x_origin * 2,
                   (draw->gcinfo.values.clip_y_origin + height) * 2);
        } else {
           Chox11Queue_PlotPixelSprite(pixel_sprite, queue,
                   (-draw->gcinfo.values.clip_x_origin + queue->minx) * 2,
                   (draw->gcinfo.values.clip_y_origin - queue->maxy + height) * 2);
        }
      } else {
        Chox11Queue_Draw(queue, -draw->gcinfo.values.clip_x_origin * 2,
                       (draw->gcinfo.values.clip_y_origin + height) * 2);
      }
  
      Sprite_UnRedirect(&Chox11_state);
    }
  
    if (draw->isWindow) {
      window_redrawblock redraw;
      wimp_rect clip_rect;
      BOOL more_rects;
      BOOL more;
      int offx, offy;
  
  //    printf("Chox11Queue_Empty: Processing window, drawable = %p, window = %i\n",
  //           draw, draw->data.window.handle);
  
      Chox11Queue_GetFirstClipRect(queue, &clip_rect, NULL, &more_rects);
      while (more_rects) {
        Chox11Queue_WRedrawStart(queue, &clip_rect, &redraw, &more);
                             
  //    printf("Chox11Queue_Empty: "
  //           "redraw.minx = %u, rdraw.scrollx = %u, draw.border = %u\n",
  //           redraw.rect.min.x, redraw.scroll.x,
  //           draw->data.window.border_width);
       
        offx = redraw.rect.min.x - redraw.scroll.x +
               draw->data.window.border_width;
  
        /* -2, because the origin of RISC OS windows is one pixel up from the left top corner. */
        offy = redraw.rect.max.y - redraw.scroll.y -
               draw->data.window.border_width - 2;
  
        if (draw->gcinfo.clip_sprite) {
          sprite_areainfo *sarea = draw->gcinfo.clip_sprite;
          int height = ((sprite_header *)&sarea[1])->height;
  
          if (screen_bpp < 16 && Chox11_ScreenPixTrans == NULL)
             Chox11Queue_SetupPlotInfo(draw->gcinfo.clip_sprite);
  
          while (more) {
            Chox11Queue_PlotSpriteToScreen(draw->gcinfo.clip_sprite,
                                         offx + (draw->gcinfo.values.clip_x_origin << screen_eig.x),
                                         offy - ((draw->gcinfo.values.clip_y_origin + height) << screen_eig.y),
                                         8);
            Wimp_GetRectangle(&redraw, &more);
          }
  
        } else if (pixel_sprite) {
          /* Draw stippled or tiled */
          if (draw->gcinfo.values.fill_style == FillTiled) {
            while (more) {
              Chox11Queue_FillTiled(pixel_sprite, queue, offx, offy);
              Wimp_GetRectangle(&redraw, &more);
            }
          } else {                 
            while (more) {
              Chox11Queue_PlotPixelSprite(pixel_sprite, queue,
                   offx + (queue->minx << screen_eig.x), offy - (queue->maxy << screen_eig.y));
              Wimp_GetRectangle(&redraw, &more);
            }
          }
        } else {
          /* Draw direct to screen */
          while (more) {
            Chox11Queue_Draw(queue, offx, offy);
            Wimp_GetRectangle(&redraw, &more);
          }
        }
        Chox11Queue_GetNextClipRect(queue, &clip_rect, &more_rects);
      }
    } else {
      sprite_areainfo *sarea = &draw->data.image.pixmap;
      int height = ((sprite_header *)&sarea[1])->height;

      Sprite_Redirect(&draw->data.image.pixmap, X_SPRITE_NAME, Chox11_savearea,
                      &Chox11_state);

      /* clip_sprite and clip_rects are mutually exclusive */
      if (draw->gcinfo.clip_sprite) {
        sprite_areainfo *csarea = draw->gcinfo.clip_sprite;
        int clip_height = ((sprite_header *)&csarea[1])->height;
  
        Sprite_Plot(draw->gcinfo.clip_sprite, X_SPRITE_NAME,
                      draw->gcinfo.values.clip_x_origin * 2,
                      (height - (draw->gcinfo.values.clip_y_origin + clip_height)) * 2,
                      8);
  
      } else {
        wimp_rect clip_rect;
        BOOL clip_gc;
        BOOL more_rects;
        wimp_rect old_clip;
  
        Chox11Queue_GetFirstClipRect(queue, &clip_rect, &clip_gc, &more_rects);
        if (clip_gc) GFX_GetClip(&old_clip);

        while (more_rects) {
          if (clip_gc) {
             int miny = clip_rect.min.y;

             clip_rect.min.x *= 2;
             clip_rect.max.x *= 2;
             clip_rect.min.y = (height - clip_rect.max.y) * 2;
             clip_rect.max.y = (height - miny) * 2;   
             GFX_SetClip(&clip_rect);
          }
  
          if (pixel_sprite) {
            /* Draw stippled or tiled */
            if (draw->gcinfo.values.fill_style == FillTiled) {
               Chox11Queue_FillTiled(pixel_sprite, queue, 0, height * 2);
            } else {                 
              Chox11Queue_PlotPixelSprite(pixel_sprite, queue, queue->minx * 2, (draw_height - 1 - queue->maxy) * 2);
            }
  
          } else {
            Chox11Queue_Draw(queue, 0, height * 2);
          }
          Chox11Queue_GetNextClipRect(queue, &clip_rect, &more_rects);
        }
        if (clip_gc) GFX_SetClip(&old_clip);
      }

      Sprite_UnRedirect(&Chox11_state);
    }
  }
  Chox11Queue_Free(queue);
  memset(queue->action, -1, sizeof(queue->action));

  queue->needed_mask = QALL_NEEDED;
  queue->set_mask    = QSET_ALL;
  queue->items       = 0;
  queue->draw        = NULL;
}


void Chox11Queue_EmptyAll(void) {
  unsigned int queue;

  for (queue = 0; queue < QNUM; queue++) {
    Chox11Queue_Empty(&queues[queue]);
  }
}


void Chox11Queue_EmptyFor(XDrawable *draw) {
  unsigned int queue;

  for (queue = 0; queue < QNUM; queue++) {
    if (queues[queue].draw == draw) {
      Chox11Queue_Empty(&queues[queue]);
      return;
    }
  }
}


extern XFlush(

    Display*		 display

) {
  puts("XFlush");
  Chox11Queue_EmptyAll();
}


/**
 * Return a suitable queue buffer to put items in for this drawable
 *
 * Either use the queue currently in use by this drawable,
 * or the first free one found.  If none are empty, flush on
 * a round-robin basis and return the flushed one.  Assign the
 * queue to this drawable.
 *
 * @param draw     Drawable for queue
 */
static Chox_queue *Chox11Queue_AllocateQueue(XDrawable *draw) {
  unsigned int queue, empty = -1;

//  printf("allocating queue for drawable %p\n", draw);

  for (queue = 0; queue < QNUM; queue++) {
    if (!queues[queue].items) {
      empty = queue;
    } else if (queues[queue].draw == draw) {
      break;
    }
  }

//  if (queue == QNUM)
//    printf("all entries used\n");
//  else
//    printf("allocating queue at position %d (items = %d)\n", queue, queues[queue].items);

  if (queue == QNUM) {
    if (empty == -1) {
      queue = last_flushed = (last_flushed + 1) % QNUM;
//      printf("Freeing queue entry (last_flushed = %d)", last_flushed);
      Chox11Queue_Empty(&queues[queue]);
    } else {
      queue = empty;
    }
    queues[queue].draw = draw;
    queues[queue].set_mask = QSET_ALL;
    queues[queue].needed_mask = QALL_NEEDED;
    queues[queue].fill_sprite = NULL;
    queues[queue].minx = 32767;
    queues[queue].maxx = 0;
    queues[queue].miny = 32767;
    queues[queue].maxy = 0;  
  }

  return &queues[queue];
}

/**
 * Setup the clipmask for the drawable
 */

static void Chox11Queue_SetupClipMask(Chox_queue *queue, XDrawable *draw, XGContext *xgc)
{
  if (xgc->values.clip_mask == None) {
    if (draw->gcinfo.values.clip_mask != None) {
      Chox11Queue_Empty(queue);
      queue->draw = draw;
      draw->gcinfo.values.clip_mask = None;
      free(draw->gcinfo.clip_sprite);
      draw->gcinfo.clip_sprite = NULL;
    }
  } else if (xgc->values.clip_mask == draw->gcinfo.values.clip_mask) {
    if (xgc->values.clip_x_origin != draw->gcinfo.values.clip_x_origin
        || xgc->values.clip_y_origin != draw->gcinfo.values.clip_y_origin) {
      Chox11Queue_Empty(queue);
      queue->draw = draw;
      draw->gcinfo.values.clip_x_origin = xgc->values.clip_x_origin;
      draw->gcinfo.values.clip_y_origin = xgc->values.clip_y_origin;
    }

  } else {
    Chox11Queue_Empty(queue);
    queue->draw = draw;
    if (draw->gcinfo.values.clip_mask != None) {
      draw->gcinfo.values.clip_mask = None;
      free(draw->gcinfo.clip_sprite);
      draw->gcinfo.clip_sprite = NULL;
    }

    draw->gcinfo.values.clip_x_origin = xgc->values.clip_x_origin;
    draw->gcinfo.values.clip_y_origin = xgc->values.clip_y_origin;

    Chox11Queue_EmptyFor((XDrawable *)(xgc->values.clip_mask));
    free(draw->gcinfo.clip_sprite);
    draw->gcinfo.clip_sprite = Chox11Image_CreateClipSprite(xgc->values.clip_mask);
    if (draw->gcinfo.clip_sprite != NULL) draw->gcinfo.values.clip_mask = xgc->values.clip_mask;

  }
}

/**
 * Check if details of tile or stipple have changed and set up parameter for drawing
 */
static void Chox11Queue_SetupTileOrStipple(Chox_queue *queue, XDrawable *draw, XGContext *xgc) {
  int rebuild_image = 0;

  if (queue->fill_sprite == 0
      || xgc->values.fill_style != draw->gcinfo.values.fill_style
      || (xgc->values.fill_style == FillTiled && xgc->values.tile != draw->gcinfo.values.tile)
      || (xgc->values.fill_style != FillTiled && xgc->values.stipple != draw->gcinfo.values.stipple)
    ) {
      rebuild_image = 1;
  }
   
  if (draw->gcinfo.values.ts_x_origin != xgc->values.ts_x_origin
      || draw->gcinfo.values.ts_y_origin != xgc->values.ts_y_origin
      || rebuild_image
      || draw->gcinfo.values.function != xgc->values.function
      || (xgc->values.fill_style != FillTiled && (draw->gcinfo.values.foreground != xgc->values.foreground
      || draw->gcinfo.values.background != xgc->values.background))
    ) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;
    rebuild_image = 1; /* Emptying the queue frees the fill sprite */

    draw->gcinfo.values.tile = xgc->values.tile;
    draw->gcinfo.values.stipple = xgc->values.stipple;
    draw->gcinfo.values.ts_x_origin = xgc->values.ts_x_origin;
    draw->gcinfo.values.ts_y_origin = xgc->values.ts_y_origin;
  }

  draw->gcinfo.values.fill_style = xgc->values.fill_style;
  draw->gcinfo.values.function = xgc->values.function;
  draw->gcinfo.values.foreground = xgc->values.foreground;
  draw->gcinfo.values.background = xgc->values.background;

  if (rebuild_image) {
    free(queue->fill_sprite);

    if (xgc->values.fill_style == FillTiled) {
      XDrawable *tile_draw = ((XDrawable*)xgc->values.tile);
      sprite_areainfo *sarea = &(tile_draw->data.image.pixmap);
      int size = sizeof(sprite_areainfo) + Sprite_MemorySizeBpp(tile_draw->width, tile_draw->height, 32, sprite_HASNOMASKPAL);
      int size_with_mask = sizeof(sprite_areainfo) + Sprite_MemorySizeBpp(tile_draw->width, tile_draw->height, 32, sprite_HASMASK);

      Chox11Queue_EmptyFor(tile_draw); /* Ensure tile is up to date*/
      queue->fill_sprite = malloc(size_with_mask);
      if (queue->fill_sprite) memcpy(queue->fill_sprite, sarea, size);
    } else {
      XDrawable *stipple_draw = ((XDrawable *)xgc->values.stipple);
      sprite_areainfo *sarea = &(stipple_draw->data.image.pixmap);
      int size;

      size = sizeof(sprite_areainfo) + Sprite_MemorySizeBpp(stipple_draw->width, stipple_draw->height, 1, sprite_HASNOMASKPAL);
      Chox11Queue_EmptyFor(stipple_draw); /* Ensure stipple is up to date*/
      queue->fill_sprite = malloc(size);
      if (queue->fill_sprite) memcpy(queue->fill_sprite, sarea, size);
    }
  }

  /* Safety check in case we can't get the memory for the fill sprite */
  if (queue->fill_sprite == NULL) draw->gcinfo.values.fill_style = FillSolid;
}


/**
 * Synchronise the Drawable's context information relevant to the action with the current context.
 *
 * Certain context differences will cause the queue to be flushed.
 */
static void Chox11Queue_SyncContext(Chox_queue *queue, XDrawable *draw, XGContext *xgc, Chox_gaction action) {
  int gc_mask = Chox_ginfo[action].gc_mask;
  int set_mask = Chox_ginfo[action].set_mask;

  /* Clip mask processing - May cause a queue flush */
  if (xgc->values.clip_mask != None || draw->gcinfo.values.clip_mask != None)
    Chox11Queue_SetupClipMask(queue, draw, xgc);

  if (draw->gcinfo.clip_rects != NULL || xgc->clip_rects != NULL) {
    int clips_equal = Chox11ClipRects_Equal(draw->gcinfo.clip_rects, xgc->clip_rects);
    if (clips_equal) {
      clips_equal = (draw->gcinfo.values.clip_x_origin == xgc->values.clip_x_origin
	                && draw->gcinfo.values.clip_y_origin == xgc->values.clip_y_origin);
    }
    if (!clips_equal) {
      Chox11Queue_Empty(queue);
      queue->draw = draw;
 
      draw->gcinfo.values.clip_x_origin = xgc->values.clip_x_origin;
	draw->gcinfo.values.clip_y_origin = xgc->values.clip_y_origin;
      if (draw->gcinfo.clip_rects != NULL) Chox11ClipRects_Release(draw->gcinfo.clip_rects);
      draw->gcinfo.clip_rects = xgc->clip_rects;
      if (xgc->clip_rects) Chox11ClipRects_AddRef(xgc->clip_rects);
    }
  }
 
  /* Fill style processing */
  if (xgc->values.fill_style != draw->gcinfo.values.fill_style) {
    /* Queue must always be flushed on fill style change */
    Chox11Queue_Empty(queue);
    queue->draw = draw;
  }

  if (xgc->values.fill_style != FillSolid) {
    if (action == cg_area || action == cg_area_clip) {
      /* XCopyArea doesn't use fill style so ignore setting */
      Chox11Queue_Empty(queue);
      queue->draw = draw;
      draw->gcinfo.values.fill_style = FillSolid;

    } else {
      Chox11Queue_SetupTileOrStipple(queue, draw, xgc);
      /* Foreground/background and function are dealt with when the stipple is painted */
      gc_mask &= ~(GCForeground|GCBackground|GCFunction);
      set_mask &= ~QSET_FOREGROUND;
      draw->gcinfo.values.fill_style = xgc->values.fill_style;
    }
  } else {
    draw->gcinfo.values.fill_style = FillSolid;
  }

  if (gc_mask & GCFunction) {
    if ((queue->needed_mask & GCFunction) || xgc->values.function != draw->gcinfo.values.function) {
      draw->gcinfo.values.function = xgc->values.function;
      queue->action[queue->items++] = cg_function;
      queue->action[queue->items++] = xgc->values.function;
      queue->needed_mask &= ~GCFunction;
      queue->set_mask |= QSET_FOREGROUND;
    }
  }

  if (gc_mask & GCForeground) {
    if ((queue->needed_mask & GCForeground) || xgc->values.foreground != draw->gcinfo.values.foreground) {
      draw->gcinfo.values.foreground = xgc->values.foreground;
      queue->action[queue->items++] = cg_foreground;
      queue->action[queue->items++] = xgc->values.foreground;
      queue->needed_mask &= ~GCForeground;
      queue->set_mask |= QSET_FOREGROUND | QSET_FONT;
    }
  }

  if (gc_mask & GCBackground) {
    if ((queue->needed_mask & GCBackground) || xgc->values.background != draw->gcinfo.values.background) {
      draw->gcinfo.values.background = xgc->values.background;
      queue->action[queue->items++] = cg_background;
      queue->action[queue->items++] = xgc->values.background;
      queue->needed_mask &= ~GCBackground;
      queue->set_mask |= QSET_FONT;
    }
  }

  if (gc_mask & GCFont) {
    if ((queue->needed_mask & GCFont) || xgc->values.font != draw->gcinfo.values.font) {
      draw->gcinfo.values.font = xgc->values.font;
      queue->action[queue->items++] = cg_font;
      queue->action[queue->items++] = xgc->values.font;
      queue->needed_mask &= ~GCFont;
      queue->set_mask |= QSET_FONT;
      Chox11Font_AddRef((Chox_Font *)xgc->values.font);
    }
  }

  set_mask &= queue->set_mask;
  if (set_mask & QSET_FOREGROUND) {
    queue->action[queue->items++] = cg_set_foreground;
    queue->set_mask &= ~QSET_FOREGROUND;
  }

  if (set_mask & QSET_FONT) {
    queue->action[queue->items++] = cg_set_font;
    queue->set_mask &= ~QSET_FONT;
  }

  if (action == cg_string_background) {
    /* String background changes the graphic foreground
       so it must be reset if a following graphics command
       is used*/
    queue->set_mask |= QSET_FOREGROUND;
  }
}


/**
 * Return a queue position suitable for placing items in.
 *
 * A queue is found for this drawable, and items are allocated
 * in it for placing queued graphics commands.  If there isn't
 * enough room, then the queue is first flushed.
 *
 * @param draw     Drawable for queue
 * @param items    Action type, determining how many items in the queue
 */
static Chox_gaction *Chox11Queue_GetQueue(XDrawable *draw, XGContext *xgc,
                                          Chox_gaction action, Chox_queue **ret_queue) {
  Chox_queue *queue;
  int items = Chox_ginfo[action].size;
  int max_items = Chox_ginfo[action].max_needed;

  assert(items <= QSIZE);
  assert(action >=0 && action < cg_limit);

  queue = Chox11Queue_AllocateQueue(draw);

  if (queue->items + max_items > QSIZE) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;
  }

  Chox11Queue_SyncContext(queue, draw, xgc, action);
  queue->action[queue->items] = action;
  queue->items += items;

  if (ret_queue != NULL) *ret_queue = queue;

  return queue->action + queue->items - items;
}


extern XDrawLine(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x1,
    int			 y1,
    int			 x2,
    int			 y2

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;

//  printf("XDrawLine: (%d %d) to (%d, %d) in Drawable %X\n", x1, y1, x2, y2, d);

  action = Chox11Queue_GetQueue(draw, xgc, cg_line, &queue);

  action[1] = x1;
  action[2] = y1;
  action[3] = x2;
  action[4] = y2;

  Chox11Queue_IncludePoint(queue, x1, y1);
  Chox11Queue_IncludePoint(queue, x2, y2);
}


extern XDrawPoint(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y

)  {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;

//  printf("XDrawPoint: %p %p %d %d\n", draw, xgc, x, y);

  action = Chox11Queue_GetQueue(draw, xgc, cg_point, &queue);

  action[1] = x;
  action[2] = y;

  Chox11Queue_IncludePoint(queue, x, y);
}


extern XDrawLines(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XPoint*		 points,
    int			 npoints,
    int			 mode

) {
  int point;
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_queue *queue;
  Chox_gaction *action;
  int fit;

  queue = Chox11Queue_AllocateQueue(draw);

//  printf("XDrawLines: %d\n", npoints);

  /* Number of items that will fit in the queue */
  fit = (QSIZE - (int)queue->items - 7) / 2;

  /* If they won't fit, then flush the buffer.  If there's a silly
     large number, then draw immediately, don't queue */
  if (fit < npoints || npoints > 32) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;

    if (npoints > 32) {
      Chox11Queue_SyncContext(queue, draw, xgc, cg_lines_imm);
      action = queue->action + queue->items;

      action[0] = (mode == CoordModePrevious) ? cg_lines_prev_imm : cg_lines_imm;
      action[1] = npoints;
      action[2] = (Chox_gaction)points;

      queue->items += 3;

      if (mode == CoordModePrevious) {
        int x = points[0].x;
        int y = points[0].y; 
        Chox11Queue_IncludePoint(queue, x, y);
        for (point = 1; point < npoints; point++) {
          x += points[point].x;
          y += points[point].y;
          Chox11Queue_IncludePoint(queue, x,y);
        }
      } else {
        for (point = 0; point < npoints; point++) {
          Chox11Queue_IncludePoint(queue, points[point].x, points[point].y);
        }
      }
      Chox11Queue_Empty(queue);
      return;
    }
  }

  Chox11Queue_SyncContext(queue, draw, xgc, cg_lines);
  action = &queue->action[queue->items];

  action[0] = cg_lines;
  action[1] = npoints;

  action = &queue->action[queue->items += 2];

  if (mode == CoordModePrevious) {
    action[0] = points[0].x;
    action[1] = points[0].y;
    for (point = 1; point < npoints; point++) {
      action[point * 2]     = points[point].x + action[point * 2 - 2];
      action[point * 2 + 1] = points[point].y + action[point * 2 - 1];
      Chox11Queue_IncludePoint(queue, action[point*2], action[point*2+1]);
    }
  } else {
    for (point = 0; point < npoints; point++) {
      action[point * 2]     = points[point].x;
      action[point * 2 + 1] = points[point].y;
      Chox11Queue_IncludePoint(queue, points[point].x, points[point].y);
    }
  }

  queue->items += npoints * 2;
}


extern XFillRectangle(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;

//   printf("XFillRectangle d=%x x,y=%d,%d w,h=%d,%d\n", d, x, y, width, height);

  action = Chox11Queue_GetQueue(draw, xgc, cg_fillrect, &queue);

  action[1] = x;
  action[2] = y;
  action[3] = width;
  action[4] = height;

  Chox11Queue_IncludePoint(queue, x, y);
  Chox11Queue_IncludePoint(queue, x+width, y+height);

}


extern XFillRectangles(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XRectangle*		 rectangles,
    int			 nrectangles

) {
  puts("XFillRectangles - not implemented");
}


extern XClearArea(

    Display*		 display,
    Window		 w,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height,
    Bool		 exposures

) {
  XDrawable *draw = (XDrawable *)w;
  window_info info;
  Chox_queue *queue;
  Chox_gaction *action;

//  puts("XClearArea");

  Window_GetInfo(draw->data.window.handle, &info);

  /* If sizes are zero, they are extended to the edge of the window */
  if (width == 0)
    width = draw->width - x;

  if (height == 0)
    height = draw->height - y;

  /* Can't use GetQueue as we have no graphics context */
  queue = Chox11Queue_AllocateQueue(draw);

  /* We must be using a solid fill for clear area - so flush
     any existing drawing that has been using a non-solid fill.
  */
  if (draw->gcinfo.values.fill_style != FillSolid) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;
    draw->gcinfo.values.fill_style = FillSolid;
  }

  if (queue->items + Chox_ginfo[cg_cleararea].max_needed > QSIZE) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;
  }

  action = queue->action + queue->items;
  queue->items += Chox_ginfo[cg_cleararea].size;

  action[0] = cg_cleararea;
  action[1] = x;
  action[2] = y;
  action[3] = width;
  action[4] = height;
  action[5] = draw->data.window.background;
  /* This command may change the foreground and function so we
     need to ensure following commands put it back if they need
     to. */
  draw->gcinfo.values.function = 0;
  queue->needed_mask &= ~GCFunction;
  draw->gcinfo.values.foreground = draw->data.window.background;
  queue->needed_mask &= ~GCForeground;

  Chox11Queue_IncludePoint(queue, x, y);
  Chox11Queue_IncludePoint(queue, x+width, y+height);
}

extern XClearWindow(

    Display*		 display,
    Window		 w

) {
  puts("XClearWindow");
  XClearArea(display, w, 0, 0, 0, 0, False);
}


XFillPolygon(display, d, gc, points, npoints, shape, mode)
      Display *display;
      Drawable d;
      GC gc;
      XPoint *points;
      int npoints;
      int shape;
      int mode;
{
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;

  Chox_queue *queue;
  Chox_gaction *action;
  int fit;
  int point;

  queue = Chox11Queue_AllocateQueue(draw);

/*  printf("XFillPolygon: %d\n", npoints);*/

  /* Number of items that will fit in the queue */
  fit = QSIZE - (int)queue->items - 9;

  /* If they won't fit, then flush the buffer.  If there's a silly
     large number, then draw immediately, don't queue */
  if (fit < npoints || npoints > 32) {
    Chox11Queue_Empty(queue);
    queue->draw = draw;

    if (npoints > 32) {
      Chox11Queue_SyncContext(queue, draw, xgc, cg_fillpoly_imm);
      action = queue->action + queue->items;

      action[0] = cg_fillpoly_imm;
      action[1] = npoints;
      action[2] = shape;
      action[3] = mode;
      action[4] = (Chox_gaction)points;

      queue->items += 5;

      for (point = 0; point < npoints; point++) {
        Chox11Queue_IncludePoint(queue, points[point].x, points[point].y);
      }
      Chox11Queue_Empty(queue);
      return;
    }
  }

  Chox11Queue_SyncContext(queue, draw, xgc, cg_fillpoly);
  action = &queue->action[queue->items];

  action[0] = cg_fillpoly;
  action[1] = npoints;
  action[2] = shape;
  action[3] = mode;

  action = &queue->action[queue->items += 4];

  for (point = 0; point < npoints; point++) {
    ((XPoint *)action)[point].x = points[point].x;
    ((XPoint *)action)[point].y = points[point].y;
    Chox11Queue_IncludePoint(queue, points[point].x, points[point].y);
  }

  queue->items += npoints;
}


void Chox11Queue_DrawString(
    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst char*	 string,
    int			 length,
    Bool                 background

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;
  int bounds[4];
  int coord_base = 4 + QSTRING/4;

  /* Chox11Queue_GetQueue has special processing in it now for if the
     background has been painted as any following graphics command would
     have to reset the foreground colour */
  action = Chox11Queue_GetQueue(draw, xgc, (background) ? cg_string_background : cg_string, &queue);

  action[1] = length;
  action[2] = x;
  action[3] = y;

  /* Try and fit in 20 letters for a max length of 11 4-byte actions */
  if (length <= QSTRING) {
    memcpy((char *)&action[4], string, length);
    if (length < QSTRING)
      ((char *)&action[4])[length] = '\0';
  } else {
    char *todraw = malloc(length + 1);
    memcpy(todraw, string, length);
    todraw[length] = '\0';
    action[4] = (Chox_gaction)todraw;
  }

  CalculateTextBounds(xgc->values.font, x, y, string, length, bounds);
  if (background) {
    int bound;
    for (bound = 0; bound < 4; bound++) {
      action[coord_base + bound] = bounds[bound];
    }
  }

  Chox11Queue_IncludePoint(queue, bounds[0], bounds[1]);
  Chox11Queue_IncludePoint(queue, bounds[2], bounds[3]);
}


extern XDrawArc(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height,
    int			 angle1,
    int			 angle2

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;

  if (angle1 == 0 && angle2 == 360*64 && width == height) {
    // Special case full circle  
    action = Chox11Queue_GetQueue(draw, xgc, cg_circle, &queue);
    action[1] = x + width/2;
    action[2] = y + height/2;
    action[3] = width/2;

    Chox11Queue_IncludePoint(queue, x, y);
    Chox11Queue_IncludePoint(queue, x+width, y+height);

  } else
    puts("XDrawArc non-circle (not implemented)");
}


extern XDrawArcs(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XArc*		 arcs,
    int			 narcs

) {
  puts("XDrawArcs (not implemented)");
}


extern XDrawPoints(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XPoint*		 points,
    int			 npoints,
    int			 mode

) {
  puts("XDrawPoints (not implemented)");
}


extern XDrawRectangle(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;
  
  printf("XDrawRectangle: gc=%p, x,y=%i,%i width,height=%i,%i\n",
         gc, x, y, width, height);

   action = Chox11Queue_GetQueue(draw, xgc, cg_rectangle, &queue);
   action[1] = x;
   action[2] = y;
   action[3] = width;
   action[4] = height;

  Chox11Queue_IncludePoint(queue, x, y);
  Chox11Queue_IncludePoint(queue, x+width, y+height);
}

       
extern XDrawRectangles(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XRectangle*		 rectangles,
    int			 nrectangles

) {
  puts("XDrawRectangles (not implemented)");
}


extern XDrawSegments(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XSegment*		 segments,
    int			 nsegments

) {
  int segment;

  puts("XDrawSegments");

  for (segment = 0; segment < nsegments; segment++) {
    XDrawLine(display, d, gc,
              segments[segment].x1, segments[segment].y1,
              segments[segment].x2, segments[segment].y2);
  }
}


extern XFillArc(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height,
    int			 angle1,
    int			 angle2

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;

  printf("XFillArc %d %d %d %d %d %d\n", x, y, width, height, angle1, angle2);

  action = Chox11Queue_GetQueue(draw, xgc, cg_fillarc, &queue);

  action[1] = x;
  action[2] = y;
  action[3] = width;
  action[4] = height;
  action[5] = angle1;
  action[6] = angle2;

  Chox11Queue_IncludePoint(queue, x, y);
  Chox11Queue_IncludePoint(queue, x+width, y+height);
}


extern XFillArcs(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XArc*		 arcs,
    int			 narcs

) {
  XDrawable *draw = (XDrawable *)d;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_gaction *action;
  Chox_queue *queue;
  int arc;

  action = Chox11Queue_GetQueue(draw, xgc, cg_fillarc_imm, &queue);

  action[1] = narcs;
  action[2] = (Chox_gaction)arcs;

  for (arc = 0; arc < narcs; arc++) {
    Chox11Queue_IncludePoint(queue, arcs[arc].x, arcs[arc].y);
    Chox11Queue_IncludePoint(queue, arcs[arc].x + arcs[arc].width, arcs[arc].y + arcs[arc].height);
  }
  Chox11Queue_EmptyFor(draw);
}


extern XCopyArea(

    Display*		 display,
    Drawable		 src,
    Drawable		 dest,
    GC			 gc,
    int			 src_x,
    int			 src_y,
    unsigned int	 width,
    unsigned int	 height,
    int			 dest_x,
    int			 dest_y

) {
  XDrawable *srcd, *destd;
  Chox_gaction *action;
  XGContext *xgc = (XGContext *)gc->gid;
  Chox_queue *queue;

  srcd = (XDrawable *)src;
  destd = (XDrawable *)dest;

//  printf("XCopyArea src = %p dest = %p src_iswin = %d dest_iswin = %d\n",
//                  src, dest, srcd->isWindow, destd->isWindow);
                  
//  printf("srcx = %d srcy = %d w = %d h = %d destx = %d desty = %d\n",
//         src_x, src_y, width, height, dest_x, dest_y);

  if (srcd->isWindow) {
     puts("XCopyArea - copy from window not implemented\n");
     return;
  }

  /* First flush source to ensure it is up to date before drawing it */
  Chox11Queue_EmptyFor(srcd);

  if (src_x == 0 && src_y == 0 && width >= srcd->width && height == srcd->height) {
    /* Simple case of plotting the whole sprite */
    action = Chox11Queue_GetQueue(destd, xgc, cg_area, &queue);

    action[1] = (Chox_gaction)srcd;
    action[2] = dest_x;
    action[3] = dest_y + height - 1; /* Sprites are plotted from the bottom up */
  } else {
    // Need to specify a clip area for the plot as well
    action = Chox11Queue_GetQueue(destd, xgc, cg_area_clip, &queue);
    action[1] = (Chox_gaction)srcd;
    action[2] = dest_x - src_x;
    action[3] = dest_y + srcd->height - 1 - src_y; /* Sprites are plotted from the bottom up */
    action[4] = dest_x; /* Clip area in pixels */
    action[5] = dest_y + height - 1;
    action[6] = dest_x + width - 1;
    action[7] = dest_y;

printf("clip area %d,%d - %d,%d,%d,%d\n", action[2],action[3],action[4],action[5],action[6], action[7]);
  }

  Chox11Queue_IncludePoint(queue, dest_x, dest_y);
  Chox11Queue_IncludePoint(queue, dest_x + width - 1, dest_y + height - 1);

  /* Now draw it, before it changes */
  Chox11Queue_EmptyFor(destd);
}
