[gegl/wip/pippin/ctx: 115/115] gegl/ctx: update ctx




commit 98c0689bba7d3b39cb3e185805890aeb48a5f466
Author: Øyvind Kolås <pippin gimp org>
Date:   Fri Apr 30 17:05:03 2021 +0200

    gegl/ctx: update ctx

 gegl/ctx/ctx.h       | 30389 +++++++++++++++++++++++++++----------------------
 gegl/ctx/meson.build |     3 +
 2 files changed, 17016 insertions(+), 13376 deletions(-)
---
diff --git a/gegl/ctx/ctx.h b/gegl/ctx/ctx.h
index 0aa4a12ca..3bdf02b7c 100644
--- a/gegl/ctx/ctx.h
+++ b/gegl/ctx/ctx.h
@@ -21,13 +21,14 @@
  * #define CTX_IMPLEMENTATION
  * #include "ctx.h"
  *
- * Ctx does not - yet - contain a minimal default fallback font, so
+ * Ctx contains a minimal default fallback font with only ascii, so
  * you probably want to also include a font, and perhaps enable
  * the cairo or SDL2 optional renderers, a more complete example
  * could be:
  *
  * #include <cairo.h>
  * #include <SDL.h>
+ * #include "ctx-font-regular.h"
  * #define CTX_IMPLEMENTATION
  * #include "ctx.h"
  *
@@ -54,39 +55,23 @@ typedef struct _Ctx            Ctx;
 enum _CtxPixelFormat
 {
   CTX_FORMAT_NONE=0,
-  CTX_FORMAT_GRAY8,
-  CTX_FORMAT_GRAYA8,
-  CTX_FORMAT_RGB8,
-  CTX_FORMAT_RGBA8,
-  CTX_FORMAT_BGRA8,
-  CTX_FORMAT_RGB565,
-  CTX_FORMAT_RGB565_BYTESWAPPED,
-  CTX_FORMAT_RGB332,
-  CTX_FORMAT_RGBAF,
-  CTX_FORMAT_GRAYF,
-  CTX_FORMAT_GRAYAF,
-  CTX_FORMAT_GRAY1,
-  CTX_FORMAT_GRAY2,
-  CTX_FORMAT_GRAY4,
-  CTX_FORMAT_CMYK8,
-  CTX_FORMAT_CMYKA8,
-  CTX_FORMAT_CMYKAF,
-  CTX_FORMAT_DEVICEN1,
-  CTX_FORMAT_DEVICEN2,
-  CTX_FORMAT_DEVICEN3,
-  CTX_FORMAT_DEVICEN4,
-  CTX_FORMAT_DEVICEN5,
-  CTX_FORMAT_DEVICEN6,
-  CTX_FORMAT_DEVICEN7,
-  CTX_FORMAT_DEVICEN8,
-  CTX_FORMAT_DEVICEN9,
-  CTX_FORMAT_DEVICEN10,
-  CTX_FORMAT_DEVICEN11,
-  CTX_FORMAT_DEVICEN12,
-  CTX_FORMAT_DEVICEN13,
-  CTX_FORMAT_DEVICEN14,
-  CTX_FORMAT_DEVICEN15,
-  CTX_FORMAT_DEVICEN16
+  CTX_FORMAT_GRAY8,  // 1  - these enum values are not coincidence
+  CTX_FORMAT_GRAYA8, // 2  -
+  CTX_FORMAT_RGB8,   // 3  -
+  CTX_FORMAT_RGBA8,  // 4  -
+  CTX_FORMAT_BGRA8,  // 5
+  CTX_FORMAT_RGB565, // 6
+  CTX_FORMAT_RGB565_BYTESWAPPED, // 7
+  CTX_FORMAT_RGB332, // 8
+  CTX_FORMAT_RGBAF,  // 9
+  CTX_FORMAT_GRAYF,  // 10
+  CTX_FORMAT_GRAYAF, // 11
+  CTX_FORMAT_GRAY1,  //12 MONO
+  CTX_FORMAT_GRAY2,  //13 DUO
+  CTX_FORMAT_GRAY4,  //14
+  CTX_FORMAT_CMYK8,  //15
+  CTX_FORMAT_CMYKA8, //16 
+  CTX_FORMAT_CMYKAF, //17
 };
 typedef enum   _CtxPixelFormat CtxPixelFormat;
 
@@ -141,7 +126,6 @@ void  ctx_dirty_rect      (Ctx *ctx, int *x, int *y, int *width, int *height);
  */
 void ctx_free (Ctx *ctx);
 
-
 /* clears and resets a context */
 void ctx_reset          (Ctx *ctx);
 void ctx_begin_path     (Ctx *ctx);
@@ -153,17 +137,25 @@ void ctx_clip           (Ctx *ctx);
 void ctx_identity       (Ctx *ctx);
 void ctx_rotate         (Ctx *ctx, float x);
 
+void ctx_image_smoothing     (Ctx *ctx, int enabled);
+int  ctx_get_image_smoothing (Ctx *ctx);
+
 #define CTX_LINE_WIDTH_HAIRLINE -1000.0
 #define CTX_LINE_WIDTH_ALIASED  -1.0
 #define CTX_LINE_WIDTH_FAST     -1.0  /* aliased 1px wide line */
 void ctx_miter_limit (Ctx *ctx, float limit);
+float ctx_get_miter_limit (Ctx *ctx);
 void ctx_line_width       (Ctx *ctx, float x);
+void ctx_line_dash_offset (Ctx *ctx, float line_dash);
+float ctx_get_line_dash_offset (Ctx *ctx);
 void ctx_apply_transform  (Ctx *ctx, float a,  float b,  // hscale, hskew
-                           float c,  float d,  // vskew,  vscale
-                           float e,  float f); // htran,  vtran
+                                     float c,  float d,  // vskew,  vscale
+                                     float e,  float f); // htran,  vtran
+void ctx_set_transform    (Ctx *ctx, float a, float b, float c, float d, float e, float f);
 void  ctx_line_dash       (Ctx *ctx, float *dashes, int count);
 void  ctx_font_size       (Ctx *ctx, float x);
 void  ctx_font            (Ctx *ctx, const char *font);
+void  ctx_font_family     (Ctx *ctx, const char *font_family);
 void  ctx_scale           (Ctx *ctx, float x, float y);
 void  ctx_translate       (Ctx *ctx, float x, float y);
 void  ctx_line_to         (Ctx *ctx, float x, float y);
@@ -202,6 +194,7 @@ void  ctx_rel_quad_to     (Ctx *ctx,
                            float x, float y);
 void  ctx_close_path      (Ctx *ctx);
 float ctx_get_font_size   (Ctx *ctx);
+const char *ctx_get_font  (Ctx *ctx);
 float ctx_get_line_width  (Ctx *ctx);
 int   ctx_width           (Ctx *ctx);
 int   ctx_height          (Ctx *ctx);
@@ -219,15 +212,6 @@ void gtx_glyph_free       (CtxGlyph *glyphs);
 
 int  ctx_glyph            (Ctx *ctx, uint32_t unichar, int stroke);
 
-void ctx_arc              (Ctx  *ctx,
-                           float x, float y,
-                           float radius,
-                           float angle1, float angle2,
-                           int   direction);
-
-void ctx_arc_to           (Ctx *ctx, float x1, float y1,
-                           float x2, float y2, float radius);
-
 void ctx_quad_to          (Ctx *ctx, float cx, float cy,
                            float x, float y);
 
@@ -243,6 +227,7 @@ void ctx_arc_to           (Ctx *ctx, float x1, float y1,
 void ctx_preserve         (Ctx *ctx);
 void ctx_fill             (Ctx *ctx);
 void ctx_stroke           (Ctx *ctx);
+
 void ctx_parse            (Ctx *ctx, const char *string);
 
 void ctx_shadow_rgba      (Ctx *ctx, float r, float g, float b, float a);
@@ -261,10 +246,24 @@ float ctx_get_global_alpha (Ctx *ctx);
 void ctx_named_source (Ctx *ctx, const char *name);
 // followed by a color, gradient or pattern definition
 
+void ctx_rgba_stroke   (Ctx *ctx, float r, float g, float b, float a);
+void ctx_rgb_stroke    (Ctx *ctx, float r, float g, float b);
+void ctx_rgba8_stroke  (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+
+void ctx_gray_stroke   (Ctx *ctx, float gray);
+void ctx_drgba_stroke  (Ctx *ctx, float r, float g, float b, float a);
+void ctx_cmyka_stroke  (Ctx *ctx, float c, float m, float y, float k, float a);
+void ctx_cmyk_stroke   (Ctx *ctx, float c, float m, float y, float k);
+void ctx_dcmyka_stroke (Ctx *ctx, float c, float m, float y, float k, float a);
+void ctx_dcmyk_stroke  (Ctx *ctx, float c, float m, float y, float k);
+
+
+
 void ctx_rgba   (Ctx *ctx, float r, float g, float b, float a);
 void ctx_rgb    (Ctx *ctx, float r, float g, float b);
-void ctx_gray   (Ctx *ctx, float gray);
 void ctx_rgba8  (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+
+void ctx_gray   (Ctx *ctx, float gray);
 void ctx_drgba  (Ctx *ctx, float r, float g, float b, float a);
 void ctx_cmyka  (Ctx *ctx, float c, float m, float y, float k, float a);
 void ctx_cmyk   (Ctx *ctx, float c, float m, float y, float k);
@@ -292,39 +291,67 @@ void ctx_gradient_add_stop (Ctx *ctx, float pos, float r, float g, float b, floa
 void ctx_gradient_add_stop_u8 (Ctx *ctx, float pos, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 
 
-
-/*ctx_texture_init:
+/*
  *
- * return value: the actual id assigned, if id is out of range - or later
- * when -1 as id will mean auto-assign.
  */
-int ctx_texture_init (Ctx *ctx,
-                      int id,
-                      int width,
-                      int height,
-                      int stride,
-                      CtxPixelFormat format,
-                      uint8_t *pixels,
-                      void (*freefunc) (void *pixels, void *user_data),
-                      void *user_data);
+void ctx_define_texture (Ctx *ctx,
+                         const char *eid,
+                         int         width,
+                         int         height,
+                         int         stride,
+                         int         format,
+                         void       *data,
+                         char       *ret_eid);
 
-int  ctx_texture_load       (Ctx *ctx, int id, const char *path, int *width, int *height);
-void ctx_texture_release    (Ctx *ctx, int id);
+void
+ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
+                    CtxPixelFormat format, int dst_stride,
+                    uint8_t *dst_data);
 
-/* sets the paint source to be a texture from the texture bank*/
-void ctx_texture            (Ctx *ctx, int id, float x, float y);
+void
+ctx_put_image_data (Ctx *ctx, int w, int h, int stride, int format,
+                    uint8_t *data,
+                    int ox, int oy,
+                    int dirtyX, int dirtyY,
+                    int dirtyWidth, int dirtyHeight);
+
+
+/* loads an image file from disk into texture, returning pixel width, height
+ * and eid, the eid is based on the path; not the contents - avoiding doing
+ * sha1 checksum of contents. The width and height of the image is returned
+ * along with the used eid, width height or eid can be NULL if we
+ * do not care about their values.
+ */
+void ctx_texture_load (Ctx        *ctx,
+                       const char *path,
+                       int        *width,
+                       int        *height,
+                       char       *eid);
 
-/* global used to use the textures from a different context, used
- * by the render threads of fb and sdl backends.
+/* sets the paint source to be a texture by eid
  */
-void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source);
+void ctx_texture              (Ctx *ctx, const char *eid, float x, float y);
+
+void ctx_draw_texture         (Ctx *ctx, const char *eid, float x, float y, float w, float h);
+
+void ctx_draw_texture_clipped (Ctx *ctx, const char *eid, float x, float y, float w, float h, float sx, 
float sy, float swidth, float sheight);
+
+void ctx_draw_image           (Ctx *ctx, const char *path, float x, float y, float w, float h);
 
-void ctx_image_path (Ctx *ctx, const char *path, float x, float y);
+void ctx_draw_image_clipped   (Ctx *ctx, const char *path, float x, float y, float w, float h, float sx, 
float sy, float swidth, float sheight);
+
+/* used by the render threads of fb and sdl backends.
+ */
+void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source);
+/* used when sharing cache state of eids between clients
+ */
+void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache);
 
 typedef struct _CtxDrawlist CtxDrawlist;
 typedef void (*CtxFullCb) (CtxDrawlist *drawlist, void *data);
 
-int ctx_pixel_format_bpp        (CtxPixelFormat format);
+int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format); // bits per pixel
+int ctx_pixel_format_get_stride (CtxPixelFormat format, int width);
 int ctx_pixel_format_components (CtxPixelFormat format);
 
 void _ctx_set_store_clear (Ctx *ctx);
@@ -500,6 +527,17 @@ _CtxGlyph
   float    y;
 };
 
+CtxTextAlign ctx_get_text_align (Ctx *ctx);
+CtxTextBaseline ctx_get_text_baseline (Ctx *ctx);
+CtxTextDirection ctx_get_text_direction (Ctx *ctx);
+CtxFillRule ctx_get_fill_rule (Ctx *ctx);
+CtxLineCap ctx_get_line_cap (Ctx *ctx);
+CtxLineJoin ctx_get_line_join (Ctx *ctx);
+CtxCompositingMode ctx_get_compositing_mode (Ctx *ctx);
+CtxBlend ctx_get_blend_mode (Ctx *ctx);
+
+void ctx_gradient_add_stop_string (Ctx *ctx, float pos, const char *color);
+
 void ctx_text_align           (Ctx *ctx, CtxTextAlign      align);
 void ctx_text_baseline        (Ctx *ctx, CtxTextBaseline   baseline);
 void ctx_text_direction       (Ctx *ctx, CtxTextDirection  direction);
@@ -510,7 +548,7 @@ void ctx_compositing_mode     (Ctx *ctx, CtxCompositingMode mode);
 int  ctx_set_drawlist     (Ctx *ctx, void *data, int length);
 typedef struct _CtxEntry CtxEntry;
 /* we only care about the tight packing for this specific
- * structx as we do indexing across members in arrays of it,
+ * struct as we do indexing across members in arrays of it,
  * to make sure its size becomes 9bytes -
  * the pack pragma is also sufficient on recent gcc versions
  */
@@ -551,10 +589,19 @@ void  ctx_glyphs_stroke (Ctx       *ctx,
 
 void  ctx_text          (Ctx        *ctx,
                          const char *string);
-
 void  ctx_text_stroke   (Ctx        *ctx,
                          const char *string);
 
+void  ctx_fill_text     (Ctx        *ctx,
+                         const char *string,
+                         float       x,
+                         float       y);
+
+void  ctx_stroke_text   (Ctx        *ctx,
+                         const char *string,
+                         float       x,
+                         float       y);
+
 /* returns the total horizontal advance if string had been rendered */
 float ctx_text_width    (Ctx        *ctx,
                          const char *string);
@@ -592,6 +639,11 @@ void ctx_set_renderer (Ctx *ctx,
                        void *renderer);
 void *ctx_get_renderer (Ctx *ctx);
 
+int ctx_renderer_is_sdl (Ctx *ctx);
+int ctx_renderer_is_fb (Ctx *ctx);
+int ctx_renderer_is_ctx (Ctx *ctx);
+int ctx_renderer_is_term (Ctx *ctx);
+
 /* the following API is only available when CTX_EVENTS is defined to 1
  *
  * it provides the ability to register callbacks with the current path
@@ -600,8 +652,8 @@ void *ctx_get_renderer (Ctx *ctx);
 unsigned long ctx_ticks (void);
 int ctx_is_dirty (Ctx *ctx);
 void ctx_set_dirty (Ctx *ctx, int dirty);
-float ctx_get_float (Ctx *ctx, uint32_t hash);
-void ctx_set_float (Ctx *ctx, uint32_t hash, float value);
+float ctx_get_float (Ctx *ctx, uint64_t hash);
+void ctx_set_float (Ctx *ctx, uint64_t hash, float value);
 
 unsigned long ctx_ticks (void);
 void ctx_flush (Ctx *ctx);
@@ -610,8 +662,8 @@ void ctx_set_clipboard (Ctx *ctx, const char *text);
 char *ctx_get_clipboard (Ctx *ctx);
 
 void _ctx_events_init     (Ctx *ctx);
-typedef struct _CtxRectangle CtxRectangle;
-struct _CtxRectangle {
+typedef struct _CtxIntRectangle CtxIntRectangle;
+struct _CtxIntRectangle {
   int x;
   int y;
   int width;
@@ -627,26 +679,27 @@ typedef void (*CtxCb) (CtxEvent *event,
 typedef void (*CtxDestroyNotify) (void *data);
 
 enum _CtxEventType {
-  CTX_PRESS          = 1 << 0,
-  CTX_MOTION         = 1 << 1,
-  CTX_RELEASE        = 1 << 2,
-  CTX_ENTER          = 1 << 3,
-  CTX_LEAVE          = 1 << 4,
-  CTX_TAP            = 1 << 5,
-  CTX_TAP_AND_HOLD   = 1 << 6,
+  CTX_PRESS        = 1 << 0,
+  CTX_MOTION       = 1 << 1,
+  CTX_RELEASE      = 1 << 2,
+  CTX_ENTER        = 1 << 3,
+  CTX_LEAVE        = 1 << 4,
+  CTX_TAP          = 1 << 5,
+  CTX_TAP_AND_HOLD = 1 << 6,
 
   /* NYI: SWIPE, ZOOM ROT_ZOOM, */
 
-  CTX_DRAG_PRESS     = 1 << 7,
-  CTX_DRAG_MOTION    = 1 << 8,
-  CTX_DRAG_RELEASE   = 1 << 9,
-  CTX_KEY_DOWN       = 1 << 10,
-  CTX_KEY_UP         = 1 << 11,
-  CTX_SCROLL         = 1 << 12,
-  CTX_MESSAGE        = 1 << 13,
-  CTX_DROP           = 1 << 14,
+  CTX_DRAG_PRESS   = 1 << 7,
+  CTX_DRAG_MOTION  = 1 << 8,
+  CTX_DRAG_RELEASE = 1 << 9,
+  CTX_KEY_PRESS    = 1 << 10,
+  CTX_KEY_DOWN     = 1 << 11,
+  CTX_KEY_UP       = 1 << 12,
+  CTX_SCROLL       = 1 << 13,
+  CTX_MESSAGE      = 1 << 14,
+  CTX_DROP         = 1 << 15,
 
-  CTX_SET_CURSOR= 1 << 15, // used internally
+  CTX_SET_CURSOR   = 1 << 16, // used internally
 
   /* client should store state - preparing
                                  * for restart
@@ -655,7 +708,7 @@ enum _CtxEventType {
   CTX_TAPS     = (CTX_TAP | CTX_TAP_AND_HOLD),
   CTX_CROSSING = (CTX_ENTER | CTX_LEAVE),
   CTX_DRAG     = (CTX_DRAG_PRESS | CTX_DRAG_MOTION | CTX_DRAG_RELEASE),
-  CTX_KEY      = (CTX_KEY_DOWN | CTX_KEY_UP),
+  CTX_KEY      = (CTX_KEY_DOWN | CTX_KEY_UP | CTX_KEY_PRESS),
   CTX_MISC     = (CTX_MESSAGE),
   CTX_ANY      = (CTX_POINTER | CTX_DRAG | CTX_CROSSING | CTX_KEY | CTX_MISC | CTX_TAPS),
 };
@@ -671,10 +724,10 @@ struct _CtxEvent {
 
   CtxModifierState state;
 
-  int      device_no; /* 0 = left mouse button / virtual focus */
-                      /* 1 = middle mouse button */
-                      /* 2 = right mouse button */
-                      /* 3 = first multi-touch .. (NYI) */
+  int     device_no; /* 0 = left mouse button / virtual focus */
+                     /* 1 = middle mouse button */
+                     /* 2 = right mouse button */
+                     /* 3 = first multi-touch .. (NYI) */
 
   float   device_x; /* untransformed (device) coordinates  */
   float   device_y;
@@ -690,7 +743,7 @@ struct _CtxEvent {
   float   delta_y; /* y - prev_y, redundant - ..  */
 
 
-  unsigned int unicode; /* only valid for key-events */
+  unsigned int unicode; /* only valid for key-events, re-use as keycode? */
   const char *string;   /* as key can be "up" "down" "space" "backspace" "a" "b" "ø" etc .. */
                         /* this is also where the message is delivered for
                          * MESSAGE events
@@ -742,6 +795,8 @@ int   ctx_add_idle           (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data
 
 void ctx_add_hit_region (Ctx *ctx, const char *id);
 
+void ctx_set_title (Ctx *ctx, const char *title);
+
 void ctx_listen_full (Ctx     *ctx,
                       float    x,
                       float    y,
@@ -787,8 +842,14 @@ int   ctx_events_height (Ctx *ctx);
  * are called in response to these being called.
  */
 
+int ctx_key_down  (Ctx *ctx, unsigned int keyval,
+                   const char *string, uint32_t time);
+int ctx_key_up    (Ctx *ctx, unsigned int keyval,
+                   const char *string, uint32_t time);
 int ctx_key_press (Ctx *ctx, unsigned int keyval,
                    const char *string, uint32_t time);
+
+
 int ctx_scrolled  (Ctx *ctx, float x, float y, CtxScrollDirection scroll_direction, uint32_t time);
 void ctx_incoming_message (Ctx *ctx, const char *message, long time);
 int ctx_pointer_motion    (Ctx *ctx, float x, float y, int device_no, uint32_t time);
@@ -797,7 +858,6 @@ int ctx_pointer_press     (Ctx *ctx, float x, float y, int device_no, uint32_t t
 int ctx_pointer_drop      (Ctx *ctx, float x, float y, int device_no, uint32_t time,
                            char *string);
 
-
 typedef enum
 {
   CTX_CONT             = '\0', // - contains args from preceding entry
@@ -836,7 +896,7 @@ typedef enum
   CTX_FILL             = 'F', //
   CTX_RESTORE          = 'G', //
   CTX_HOR_LINE_TO      = 'H', // x
-  CTX_BITPIX           = 'I', // x, y, width, height, scale // NYI
+  CTX_DEFINE_TEXTURE   = 'I', // "eid" width height format "data"
   CTX_ROTATE           = 'J', // radians
   CTX_COLOR            = 'K', // model, c1 c2 c3 ca - has a variable set of
   // arguments.
@@ -883,7 +943,7 @@ typedef enum
   CTX_RECTANGLE        = 'r', // x y width height
   CTX_REL_SMOOTH_TO    = 's', // cx cy x y
   CTX_REL_SMOOTHQ_TO   = 't', // x y
-  CTX_TEXT_STROKE      = 'u', // string - utf8 string
+  CTX_STROKE_TEXT      = 'u', // string - utf8 string
   CTX_REL_VER_LINE_TO  = 'v', // y
   CTX_GLYPH            = 'w', // unichar fontsize
   CTX_TEXT             = 'x', // string | kern - utf8 data to shape or horizontal kerning amount
@@ -920,9 +980,14 @@ typedef enum
   CTX_SHADOW_COLOR     = 141, // kC
   CTX_SHADOW_OFFSET_X  = 142, // kx
   CTX_SHADOW_OFFSET_Y  = 143, // ky
+  CTX_IMAGE_SMOOTHING  = 144, // kS
+  CTX_LINE_DASH_OFFSET = 145, // kD lineDashOffset
   // items marked with % are currently only for the parser
   // for instance for svg compatibility or simulated/converted color spaces
   // not the serialization/internal render stream
+  //
+  CTX_STROKE_RECT      = 200, // strokeRect - only exist in long form
+  CTX_FILL_RECT        = 201, // fillRect   - only exist in long form
 } CtxCode;
 
 
@@ -1027,6 +1092,33 @@ struct
       uint8_t  data[8]; /* .. and continues */
     } colorspace;
     struct
+    {
+      uint8_t  code;
+      float    x;
+      float    y;
+      uint8_t  code_data;
+      uint32_t stringlen;
+      uint32_t blocklen;
+      uint8_t  code_cont;
+      char     eid[8]; /* .. and continues */
+    } texture;
+    struct
+    {
+      uint8_t  code;
+      uint32_t width;
+      uint32_t height;
+      uint8_t  code_cont0;
+      uint16_t format;
+      uint16_t pad0;
+      uint32_t pad1;
+      uint8_t  code_data;
+      uint32_t stringlen;
+      uint32_t blocklen;
+      uint8_t  code_cont1;
+      char     eid[8]; /* .. and continues */
+      // followed by - in variable offset code_Data, data_len, datablock_len, cont, pixeldata
+    } define_texture;
+    struct
     {
       uint8_t  code;
       float    pad;
@@ -1180,6 +1272,14 @@ struct
       uint8_t pad1;
       float radius;
     } rectangle;
+    struct {
+      uint8_t code;
+      float x;
+      float y;
+      uint8_t pad0;
+      float width;
+      float height;
+    } view_box;
 
     struct
     {
@@ -1570,7 +1670,7 @@ typedef struct _CtxParser CtxParser;
   float      cell_height,
   int        cursor_x,
   int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
   int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
   void  *prop_data,
   void (*exit) (void *exit_data),
@@ -1583,13 +1683,23 @@ enum _CtxColorSpace
   CTX_COLOR_SPACE_DEVICE_CMYK,
   CTX_COLOR_SPACE_USER_RGB,
   CTX_COLOR_SPACE_USER_CMYK,
+  CTX_COLOR_SPACE_TEXTURE
 };
 typedef enum _CtxColorSpace CtxColorSpace;
 
-void ctx_colorspace (Ctx           *ctx,
-                     CtxColorSpace  space_slot,
-                     unsigned char *data,
-                     int            data_length);
+/* sets the color space for a slot, the space is either a string of
+ * "sRGB" "rec2020" .. etc or an icc profile.
+ *
+ * The slots device_rgb and device_cmyk is mostly to be handled outside drawing 
+ * code, and user_rgb and user_cmyk is to be used. With no user_cmyk set
+ * user_cmyk == device_cmyk.
+ *
+ * The set profiles follows the graphics state.
+ */
+void ctx_color_space (Ctx           *ctx,
+                      CtxColorSpace  space_slot,
+                      unsigned char *data,
+                      int            data_length);
 
 void
 ctx_parser_set_size (CtxParser *parser,
@@ -1600,12 +1710,29 @@ ctx_parser_set_size (CtxParser *parser,
 
 void ctx_parser_feed_byte (CtxParser *parser, int byte);
 
+int
+ctx_get_contents (const char     *path,
+                   unsigned char **contents,
+                   long           *length);
+
 void ctx_parser_free (CtxParser *parser);
+typedef struct _CtxSHA1 CtxSHA1;
+
+void
+ctx_bin2base64 (const void *bin,
+                int         bin_length,
+                char       *ascii);
+int
+ctx_base642bin (const char    *ascii,
+                int           *length,
+                unsigned char *bin);
+float ctx_term_get_cell_width (Ctx *ctx);
+float ctx_term_get_cell_height (Ctx *ctx);
 
 #ifndef CTX_CODEC_CHAR
 //#define CTX_CODEC_CHAR '\035'
 //#define CTX_CODEC_CHAR 'a'
-#define CTX_CODEC_CHAR '\077'
+#define CTX_CODEC_CHAR '\020' // datalink escape
 //#define CTX_CODEC_CHAR '^'
 #endif
 
@@ -1619,23 +1746,6 @@ void ctx_parser_free (CtxParser *parser);
 #endif
 #ifndef __CTX_H__
 #define __CTX_H__
-/* mrg - MicroRaptor Gui
- * Copyright (c) 2014 Øyvind Kolås <pippin hodefoting com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
 #ifndef CTX_STRING_H
 #define CTX_STRING_H
 
@@ -1678,155 +1788,6 @@ char       *ctx_strdup_printf         (const char *format, ...);
 #define FALSE 0
 #endif
 
-#endif
- /* Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
- */
-
-#if CTX_FORMATTER
-
-/* returns the maximum string length including terminating \0 */
-static int ctx_a85enc_len (int input_length)
-{
-  return (input_length / 4 + 1) * 5;
-}
-
-static int ctx_a85enc (const void *srcp, char *dst, int count)
-{
-  const uint8_t *src = (uint8_t*)srcp;
-  int out_len = 0;
-
-  int padding = 4-(count % 4);
-  if (padding == 4) padding = 0;
-
-  for (int i = 0; i < (count+3)/4; i ++)
-  {
-    uint32_t input = 0;
-    for (int j = 0; j < 4; j++)
-    {
-      input = (input << 8);
-      if (i*4+j<=count)
-        input += src[i*4+j];
-    }
-
-    int divisor = 85 * 85 * 85 * 85;
-    if (input == 0)
-    {
-        dst[out_len++] = 'z';
-    }
-    /* todo: encode 4 spaces as 'y' */
-    else
-    {
-      for (int j = 0; j < 5; j++)
-      {
-        dst[out_len++] = ((input / divisor) % 85) + '!';
-        divisor /= 85;
-      }
-    }
-  }
-  out_len -= padding;
-  dst[out_len]=0;
-  return out_len;
-}
-#endif
-
-#if CTX_PARSER
-
-static int ctx_a85dec (const char *src, char *dst, int count)
-{
-  int out_len = 0;
-  uint32_t val = 0;
-  int k = 0;
-  int i = 0;
-  for (i = 0; i < count; i ++)
-  {
-    val *= 85;
-    if (src[i] == '~')
-    {
-      break;
-    }
-    else if (src[i] == 'z')
-    {
-      for (int j = 0; j < 4; j++)
-        dst[out_len++] = 0;
-      k = 0;
-    }
-    else if (src[i] == 'y') /* lets support this extension */
-    {
-      for (int j = 0; j < 4; j++)
-        dst[out_len++] = 32;
-      k = 0;
-    }
-    else if (src[i] >= '!' && src[i] <= 'u')
-    {
-      val += src[i]-'!';
-      if (k % 5 == 4)
-      {
-         for (int j = 0; j < 4; j++)
-         {
-           dst[out_len++] = (val & (0xff << 24)) >> 24;
-           val <<= 8;
-         }
-         val = 0;
-      }
-      k++;
-    }
-    // we treat all other chars as whitespace
-  }
-  if (src[i] != '~')
-  { 
-    val *= 85;
-  }
-  k = k % 5;
-  if (k)
-  {
-    val += 84;
-    for (int j = k; j < 4; j++)
-    {
-      val *= 85;
-      val += 84;
-    }
-
-    for (int j = 0; j < k-1; j++)
-    {
-      dst[out_len++] = (val & (0xff << 24)) >> 24;
-      val <<= 8;
-    }
-    val = 0;
-  }
-  dst[out_len] = 0;
-  return out_len;
-}
-
-#if 0
-static int ctx_a85len (const char *src, int count)
-{
-  int out_len = 0;
-  int k = 0;
-  for (int i = 0; i < count; i ++)
-  {
-    if (src[i] == '~')
-      break;
-    else if (src[i] == 'z')
-    {
-      for (int j = 0; j < 4; j++)
-        out_len++;
-      k = 0;
-    }
-    else if (src[i] >= '!' && src[i] <= 'u')
-    {
-      if (k % 5 == 4)
-        out_len += 4;
-      k++;
-    }
-    // we treat all other chars as whitespace
-  }
-  k = k % 5;
-  if (k)
-    out_len += k-1;
-  return out_len;
-}
-#endif
-
 #endif
 #ifndef _CTX_INTERNAL_FONT_
 #define _CTX_INTERNAL_FONT_
@@ -3618,8 +3579,19 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 
+#ifndef CTX_PARSER_FIXED_TEMP
+#define CTX_PARSER_FIXED_TEMP 0
+         // when 1  CTX_PARSER_MAXLEN is the fixed max stringlen
+#endif   // and no allocations happens beyond creating the parser,
+         // when 0 the scratchbuf for parsing is a separate dynamically
+         // growing buffer, that maxes out at CTX_PARSER_MAXLEN
+         //
 #ifndef CTX_PARSER_MAXLEN
-#define CTX_PARSER_MAXLEN  1024 // this is the largest text string we support
+#if CTX_PARSER_FIXED_TEMP
+#define CTX_PARSER_MAXLEN  1024*128        // This is the maximum texture/string size supported
+#else
+#define CTX_PARSER_MAXLEN  1024*1024*16    // 16mb
+#endif
 #endif
 
 #ifndef CTX_COMPOSITING_GROUPS
@@ -3699,14 +3671,15 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
  * edgelist and drawlist.
  */
 #ifndef CTX_MIN_JOURNAL_SIZE
-#define CTX_MIN_JOURNAL_SIZE   1024*64
+#define CTX_MIN_JOURNAL_SIZE      512
 #endif
 
 /* The maximum size we permit the drawlist to grow to,
  * the memory used is this number * 9, where 9 is sizeof(CtxEntry)
  */
 #ifndef CTX_MAX_JOURNAL_SIZE
-#define CTX_MAX_JOURNAL_SIZE   CTX_MIN_JOURNAL_SIZE
+//#define CTX_MAX_JOURNAL_SIZE   CTX_MIN_JOURNAL_SIZE
+#define CTX_MAX_JOURNAL_SIZE 1024*1024*16
 #endif
 
 #ifndef CTX_DRAWLIST_STATIC
@@ -3733,7 +3706,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
   //     properties still make use of it)
   //     
   //     for desktop-use this should be fully dynamic, possibly
-  //     with chained pools
+  //     with chained pools, gradients are stored here.
 #define CTX_STRINGPOOL_SIZE     1000 //
 #endif
 
@@ -3977,7 +3950,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 
 #define CTX_PI                              3.141592653589793f
 #ifndef CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS
-#define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS  100
+#define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS  120
 #endif
 
 #ifndef CTX_MAX_FONTS
@@ -4034,7 +4007,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 
 #ifdef CTX_RASTERIZER
 #if CTX_RASTERIZER==0
-#elif CTX_RASTERIZER==1
 #else
 #undef CTX_RASTERIZER
 #define CTX_RASTERIZER 1
@@ -4068,15 +4040,178 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #ifndef CTX_SCREENSHOT
 #define CTX_SCREENSHOT 0
 #endif
-#ifndef __CTX_EXTRA_H
-#define __CTX_EXTRA_H
-
-
-#define CTX_CLAMP(val,min,max) ((val)<(min)?(min):(val)>(max)?(max):(val))
-static inline int   ctx_mini (int a, int b)     { if (a < b) return a; return b; }
-static inline float ctx_minf (float a, float b) { if (a < b) return a; return b; }
-static inline int   ctx_maxi (int a, int b)     { if (a > b) return a; return b; }
-static inline float ctx_maxf (float a, float b) { if (a > b) return a; return b; }
+
+
+#ifndef CTX_TILED
+#if CTX_SDL || CTX_FB
+#define CTX_TILED 1
+#else
+#define CTX_TILED 0
+#endif
+#endif
+
+ /* Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
+ */
+
+#if CTX_FORMATTER
+
+/* returns the maximum string length including terminating \0 */
+static int ctx_a85enc_len (int input_length)
+{
+  return (input_length / 4 + 1) * 5;
+}
+
+static int ctx_a85enc (const void *srcp, char *dst, int count)
+{
+  const uint8_t *src = (uint8_t*)srcp;
+  int out_len = 0;
+
+  int padding = 4-(count % 4);
+  if (padding == 4) padding = 0;
+
+  for (int i = 0; i < (count+3)/4; i ++)
+  {
+    uint32_t input = 0;
+    for (int j = 0; j < 4; j++)
+    {
+      input = (input << 8);
+      if (i*4+j<=count)
+        input += src[i*4+j];
+    }
+
+    int divisor = 85 * 85 * 85 * 85;
+#if 0
+    if (input == 0)
+    {
+        dst[out_len++] = 'z';
+    }
+    /* todo: encode 4 spaces as 'y' */
+    else
+#endif
+    {
+      for (int j = 0; j < 5; j++)
+      {
+        dst[out_len++] = ((input / divisor) % 85) + '!';
+        divisor /= 85;
+      }
+    }
+  }
+  out_len -= padding;
+  dst[out_len]=0;
+  return out_len;
+}
+#endif
+
+#if CTX_PARSER
+
+static int ctx_a85dec (const char *src, char *dst, int count)
+{
+  int out_len = 0;
+  uint32_t val = 0;
+  int k = 0;
+  int i = 0;
+  for (i = 0; i < count; i ++)
+  {
+    val *= 85;
+    if (src[i] == '~')
+    {
+      break;
+    }
+#if 0
+    else if (src[i] == 'z')
+    {
+      for (int j = 0; j < 4; j++)
+        dst[out_len++] = 0;
+      k = 0;
+    }
+    else if (src[i] == 'y') /* lets support this extension */
+    {
+      for (int j = 0; j < 4; j++)
+        dst[out_len++] = 32;
+      k = 0;
+    }
+#endif
+    else if (src[i] >= '!' && src[i] <= 'u')
+    {
+      val += src[i]-'!';
+      if (k % 5 == 4)
+      {
+         for (int j = 0; j < 4; j++)
+         {
+           dst[out_len++] = (val & (0xff << 24)) >> 24;
+           val <<= 8;
+         }
+         val = 0;
+      }
+      k++;
+    }
+    // we treat all other chars as whitespace
+  }
+  if (src[i] != '~')
+  { 
+    val *= 85;
+  }
+  k = k % 5;
+  if (k)
+  {
+    val += 84;
+    for (int j = k; j < 4; j++)
+    {
+      val *= 85;
+      val += 84;
+    }
+
+    for (int j = 0; j < k-1; j++)
+    {
+      dst[out_len++] = (val & (0xff << 24)) >> 24;
+      val <<= 8;
+    }
+    val = 0;
+  }
+  dst[out_len] = 0;
+  return out_len;
+}
+
+#if 0
+static int ctx_a85len (const char *src, int count)
+{
+  int out_len = 0;
+  int k = 0;
+  for (int i = 0; i < count; i ++)
+  {
+    if (src[i] == '~')
+      break;
+    else if (src[i] == 'z')
+    {
+      for (int j = 0; j < 4; j++)
+        out_len++;
+      k = 0;
+    }
+    else if (src[i] >= '!' && src[i] <= 'u')
+    {
+      if (k % 5 == 4)
+        out_len += 4;
+      k++;
+    }
+    // we treat all other chars as whitespace
+  }
+  k = k % 5;
+  if (k)
+    out_len += k-1;
+  return out_len;
+}
+#endif
+
+#endif
+#ifndef __CTX_EXTRA_H
+#define __CTX_EXTRA_H
+
+
+#define CTX_CLAMP(val,min,max) ((val)<(min)?(min):(val)>(max)?(max):(val))
+static inline int   ctx_mini (int a, int b)     { if (a < b) return a; return b; }
+static inline float ctx_minf (float a, float b) { if (a < b) return a; return b; }
+static inline int   ctx_maxi (int a, int b)     { if (a > b) return a; return b; }
+static inline float ctx_maxf (float a, float b) { if (a > b) return a; return b; }
 
 
 typedef enum CtxOutputmode
@@ -4090,84 +4225,9 @@ typedef enum CtxOutputmode
   CTX_OUTPUT_MODE_UI
 } CtxOutputmode;
 
-#define CTX_NORMALIZE(a)            (((a)=='-')?'_':(a))
-#define CTX_NORMALIZE_CASEFOLDED(a) (((a)=='-')?'_':((((a)>='A')&&((a)<='Z'))?(a)+32:(a)))
 
 
-/* We use the preprocessor to compute case invariant hashes
- * of strings directly, if there is collisions in our vocabulary
- * the compiler tells us.
- */
 
-#define CTX_STRHash(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a27,a12,a13) (\
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a0))+ \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a1))*27)+ \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a2))*27*27)+ \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a3))*27*27*27)+ \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a4))*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a5))*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a6))*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a7))*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a8))*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a9))*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a10))*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a27))*27*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a12))*27*27*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE_CASEFOLDED(a13))*27*27*27*27*27*27*27*27*27*27*27*27*27)))
-
-#define CTX_STRH(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a27,a12,a13) (\
-          (((uint32_t)CTX_NORMALIZE(a0))+ \
-          (((uint32_t)CTX_NORMALIZE(a1))*27)+ \
-          (((uint32_t)CTX_NORMALIZE(a2))*27*27)+ \
-          (((uint32_t)CTX_NORMALIZE(a3))*27*27*27)+ \
-          (((uint32_t)CTX_NORMALIZE(a4))*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a5))*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a6))*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a7))*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a8))*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a9))*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a10))*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a27))*27*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a12))*27*27*27*27*27*27*27*27*27*27*27*27) + \
-          (((uint32_t)CTX_NORMALIZE(a13))*27*27*27*27*27*27*27*27*27*27*27*27*27)))
-
-static inline uint32_t ctx_strhash (const char *str, int case_insensitive)
-{
-  uint32_t ret;
-  if (!str) return 0;
-  int len = strlen (str);
-  if (case_insensitive)
-    ret =CTX_STRHash(len>=0?str[0]:0,
-                       len>=1?str[1]:0,
-                       len>=2?str[2]:0,
-                       len>=3?str[3]:0,
-                       len>=4?str[4]:0,
-                       len>=5?str[5]:0,
-                       len>=6?str[6]:0,
-                       len>=7?str[7]:0,
-                       len>=8?str[8]:0,
-                       len>=9?str[9]:0,
-                       len>=10?str[10]:0,
-                       len>=11?str[11]:0,
-                       len>=12?str[12]:0,
-                       len>=13?str[13]:0);
-  else
-    ret =CTX_STRH(len>=0?str[0]:0,
-                    len>=1?str[1]:0,
-                    len>=2?str[2]:0,
-                    len>=3?str[3]:0,
-                    len>=4?str[4]:0,
-                    len>=5?str[5]:0,
-                    len>=6?str[6]:0,
-                    len>=7?str[7]:0,
-                    len>=8?str[8]:0,
-                    len>=9?str[9]:0,
-                    len>=10?str[10]:0,
-                    len>=11?str[11]:0,
-                    len>=12?str[12]:0,
-                    len>=13?str[13]:0);
-                  return ret;
-}
 
 #if CTX_FORCE_INLINES
 #define CTX_INLINE inline __attribute__((always_inline))
@@ -4353,8 +4413,8 @@ static inline float _ctx_parse_float (const char *str, char **endptr)
   return strtod (str, endptr); /* XXX: , vs . problem in some locales */
 }
 
-const char *ctx_get_string (Ctx *ctx, uint32_t hash);
-void ctx_set_string (Ctx *ctx, uint32_t hash, const char *value);
+const char *ctx_get_string (Ctx *ctx, uint64_t hash);
+void ctx_set_string (Ctx *ctx, uint64_t hash, const char *value);
 typedef struct _CtxColor CtxColor;
 typedef struct _CtxBuffer CtxBuffer;
 
@@ -4366,15 +4426,16 @@ struct
 };
 void ctx_get_matrix (Ctx *ctx, CtxMatrix *matrix);
 
-int ctx_color (Ctx *ctx, const char *string);
+int ctx_color_fill (Ctx *ctx, const char *string);
+int ctx_color_stroke (Ctx *ctx, const char *string);
 typedef struct _CtxState CtxState;
 CtxColor *ctx_color_new ();
 CtxState *ctx_get_state (Ctx *ctx);
 void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out);
 void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a);
 void ctx_color_free (CtxColor *color);
-void ctx_set_color (Ctx *ctx, uint32_t hash, CtxColor *color);
-int  ctx_get_color (Ctx *ctx, uint32_t hash, CtxColor *color);
+void ctx_set_color (Ctx *ctx, uint64_t hash, CtxColor *color);
+int  ctx_get_color (Ctx *ctx, uint64_t hash, CtxColor *color);
 int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string);
 
 int ctx_color_is_transparent (CtxColor *color);
@@ -4394,9 +4455,17 @@ void ctx_matrix_multiply (CtxMatrix       *result,
                           const CtxMatrix *s);
 void
 ctx_matrix_translate (CtxMatrix *matrix, float x, float y);
-int ctx_is_set_now (Ctx *ctx, uint32_t hash);
+int ctx_is_set_now (Ctx *ctx, uint64_t hash);
 void ctx_set_size (Ctx *ctx, int width, int height);
 
+static inline float ctx_matrix_get_scale (CtxMatrix *matrix)
+{
+   return ctx_maxf (ctx_maxf (ctx_fabsf (matrix->m[0][0]),
+                         ctx_fabsf (matrix->m[0][1]) ),
+               ctx_maxf (ctx_fabsf (matrix->m[1][0]),
+                         ctx_fabsf (matrix->m[1][1]) ) );
+}
+
 #if CTX_FONTS_FROM_FILE
 int   ctx_load_font_ttf_file (const char *name, const char *path);
 int
@@ -4409,221 +4478,249 @@ _ctx_file_get_contents (const char     *path,
 #ifndef __CTX_CONSTANTS
 #define __CTX_CONSTANTS
 
-#define CTX_add_stop       CTX_STRH('a','d','d','_','s','t','o','p',0,0,0,0,0,0)
-#define CTX_addStop        CTX_STRH('a','d','d','S','t','o','p',0,0,0,0,0,0,0)
-#define CTX_alphabetic     CTX_STRH('a','l','p','h','a','b','e','t','i','c',0,0,0,0)
-#define CTX_arc            CTX_STRH('a','r','c',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_arc_to         CTX_STRH('a','r','c','_','t','o',0,0,0,0,0,0,0,0)
-#define CTX_arcTo          CTX_STRH('a','r','c','T','o',0,0,0,0,0,0,0,0,0)
-#define CTX_begin_path     CTX_STRH('b','e','g','i','n','_','p','a','t','h',0,0,0,0)
-#define CTX_beginPath      CTX_STRH('b','e','g','i','n','P','a','t','h',0,0,0,0,0)
-#define CTX_bevel          CTX_STRH('b','e','v','e','l',0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_bottom         CTX_STRH('b','o','t','t','o','m',0,0,0,0,0,0,0,0)
-#define CTX_cap            CTX_STRH('c','a','p',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_center         CTX_STRH('c','e','n','t','e','r', 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_clear          CTX_STRH('c','l','e','a','r',0,0,0,0,0,0,0,0,0)
-#define CTX_color          CTX_STRH('c','o','l','o','r',0,0,0,0,0,0,0,0,0)
-#define CTX_copy           CTX_STRH('c','o','p','y',0,0,0,0,0,0,0,0,0,0)
-#define CTX_clip           CTX_STRH('c','l','i','p',0,0,0,0,0,0,0,0,0,0)
-#define CTX_close_path     CTX_STRH('c','l','o','s','e','_','p','a','t','h',0,0,0,0)
-#define CTX_closePath      CTX_STRH('c','l','o','s','e','P','a','t','h',0,0,0,0,0)
-#define CTX_cmyka          CTX_STRH('c','m','y','k','a',0,0,0,0,0,0,0,0,0)
-#define CTX_cmyk           CTX_STRH('c','m','y','k',0,0,0,0,0,0,0,0,0,0)
-#define CTX_color          CTX_STRH('c','o','l','o','r',0,0,0,0,0,0,0,0,0)
-
-#define CTX_blending       CTX_STRH('b','l','e','n','d','i','n','g',0,0,0,0,0,0)
-#define CTX_blend          CTX_STRH('b','l','e','n','d',0,0,0,0,0,0,0,0,0)
-#define CTX_blending_mode  CTX_STRH('b','l','e','n','d','i','n','g','_','m','o','d','e',0)
-#define CTX_blendingMode   CTX_STRH('b','l','e','n','d','i','n','g','M','o','d','e',0,0)
-#define CTX_blend_mode     CTX_STRH('b','l','e','n','d','_','m','o','d','e',0,0,0,0)
-#define CTX_blendMode      CTX_STRH('b','l','e','n','d','M','o','d','e',0,0,0,0,0)
-
-#define CTX_composite      CTX_STRH('c','o','m','p','o','s','i','t','i','e',0,0,0,0)
-#define CTX_compositing_mode CTX_STRH('c','o','m','p','o','s','i','t','i','n','g','_','m','o')
-#define CTX_compositingMode CTX_STRH('c','o','m','p','o','s','i','t','i','n','g','M','o','d')
-#define CTX_curve_to       CTX_STRH('c','u','r','v','e','_','t','o',0,0,0,0,0,0)
-#define CTX_curveTo        CTX_STRH('c','u','r','v','e','T','o',0,0,0,0,0,0,0)
-#define CTX_darken         CTX_STRH('d','a','r','k','e','n',0,0,0,0,0,0,0,0)
-#define CTX_defineGlyph    CTX_STRH('d','e','f','i','n','e','G','l','y','p','h',0,0,0)
-#define CTX_kerningPair    CTX_STRH('k','e','r','n','i','n','g','P','a','i','r',0,0,0)
-#define CTX_destinationIn  CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','I','n',0)
-#define CTX_destination_in CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','_','i','n')
-#define CTX_destinationAtop CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','A','t','o')
-#define CTX_destination_atop CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','_','a','t')
-#define CTX_destinationOver CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','O','v','e')
-#define CTX_destination_over CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','-','o','v')
-#define CTX_destinationOut CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','O','u','t')
-#define CTX_destination_out CTX_STRH('d','e','s','t','i','n','a','t','i','o','n','_','o','u')
-#define CTX_difference     CTX_STRH('d','i','f','f','e','r','e','n','c','e',0,0,0,0)
-#define CTX_done           CTX_STRH('d','o','n','e',0,0,0,0,0,0,0,0,0,0)
-#define CTX_drgba          CTX_STRH('d','r','g','b','a',0,0,0,0,0,0,0,0,0)
-#define CTX_drgb           CTX_STRH('d','r','g','b',0,0,0,0,0,0,0,0,0,0)
-#define CTX_end            CTX_STRH('e','n','d',0,0,0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_endfun         CTX_STRH('e','n','d','f','u','n',0,0,0,0,0,0,0,0)
-
-#define CTX_end_group      CTX_STRH('e','n','d','_','G','r','o','u','p',0,0,0,0,0)
-#define CTX_endGroup       CTX_STRH('e','n','d','G','r','o','u','p',0,0,0,0,0,0)
-
-#define CTX_even_odd       CTX_STRH('e','v','e','n','_','o','d','d',0,0,0,0,0,0)
-#define CTX_evenOdd        CTX_STRH('e','v','e','n','O','d','d',0,0,0,0,0,0,0)
-
-
-
-#define CTX_exit           CTX_STRH('e','x','i','t',0,0,0,0,0,0,0,0,0,0)
-#define CTX_fill           CTX_STRH('f','i','l','l',0,0,0,0,0,0,0,0,0,0)
-#define CTX_fill_rule      CTX_STRH('f','i','l','l','_','r','u','l','e',0,0,0,0,0)
-#define CTX_fillRule       CTX_STRH('f','i','l','l','R','u','l','e',0,0,0,0,0,0)
-#define CTX_flush          CTX_STRH('f','l','u','s','h',0,0,0,0,0,0,0,0,0)
-#define CTX_font           CTX_STRH('f','o','n','t',0,0,0,0,0,0,0,0,0,0)
-#define CTX_font_size      CTX_STRH('f','o','n','t','_','s','i','z','e',0,0,0,0,0)
-#define CTX_setFontSize       CTX_STRH('s','e','t','F','o','n','t','S','i','z','e',0,0,0)
-#define CTX_fontSize       CTX_STRH('f','o','n','t','S','i','z','e',0,0,0,0,0,0)
-#define CTX_function       CTX_STRH('f','u','n','c','t','i','o','n',0,0,0,0,0,0)
-#define CTX_getkey         CTX_STRH('g','e','t','k','e','y',0,0,0,0,0,0,0,0)
-#define CTX_global_alpha   CTX_STRH('g','l','o','b','a','l','_','a','l','p','h','a',0,0)
-#define CTX_globalAlpha    CTX_STRH('g','l','o','b','a','l','A','l','p','h','a',0,0,0)
-#define CTX_glyph          CTX_STRH('g','l','y','p','h',0,0,0,0,0,0,0,0,0)
-#define CTX_gradient_add_stop CTX_STRH('g','r','a','d','i','e','n','t','_','a','d','d','_','s')
-#define CTX_gradientAddStop CTX_STRH('g','r','a','d','i','e','n','t','A','d','d','S','t','o')
-#define CTX_graya          CTX_STRH('g','r','a','y','a',0,0,0,0,0,0,0,0,0)
-#define CTX_gray           CTX_STRH('g','r','a','y',0,0,0,0,0,0,0,0,0,0)
-#define CTX_H
-#define CTX_hanging        CTX_STRH('h','a','n','g','i','n','g',0,0,0,0,0,0,0)
-#define CTX_height         CTX_STRH('h','e','i','g','h','t',0,0,0,0,0,0,0,0)
-#define CTX_hor_line_to    CTX_STRH('h','o','r','_','l','i','n','e','_','t','o',0,0,0)
-#define CTX_horLineTo      CTX_STRH('h','o','r','L','i','n','e','T','o',0,0,0,0,0)
-#define CTX_hue            CTX_STRH('h','u','e',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_identity       CTX_STRH('i','d','e','n','t','i','t','y',0,0,0,0,0,0)
-#define CTX_ideographic    CTX_STRH('i','d','e','o','g','r','a','p','h','i','c',0,0,0)
-#define CTX_join           CTX_STRH('j','o','i','n',0,0,0,0,0,0,0,0,0,0)
-#define CTX_laba           CTX_STRH('l','a','b','a',0,0,0,0,0,0,0,0,0,0)
-#define CTX_lab            CTX_STRH('l','a','b',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_lcha           CTX_STRH('l','c','h','a',0,0,0,0,0,0,0,0,0,0)
-#define CTX_lch            CTX_STRH('l','c','h',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_left           CTX_STRH('l','e','f','t',0,0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_lighter        CTX_STRH('l','i','g','h','t','e','r',0,0,0,0,0,0,0)
-#define CTX_lighten        CTX_STRH('l','i','g','h','t','e','n',0,0,0,0,0,0,0)
-#define CTX_linear_gradient CTX_STRH('l','i','n','e','a','r','_','g','r','a','d','i','e','n')
-#define CTX_linearGradient CTX_STRH('l','i','n','e','a','r','G','r','a','d','i','e','n','t')
-#define CTX_line_cap       CTX_STRH('l','i','n','e','_','c','a','p',0,0,0,0,0,0)
-#define CTX_lineCap        CTX_STRH('l','i','n','e','C','a','p',0,0,0,0,0,0,0)
-#define CTX_setLineCap     CTX_STRH('s','e','t','L','i','n','e','C','a','p',0,0,0,0)
-#define CTX_line_height    CTX_STRH('l','i','n','e','_','h','e','i','h','t',0,0,0,0)
-#define CTX_line_join      CTX_STRH('l','i','n','e','_','j','o','i','n',0,0,0,0,0)
-#define CTX_lineJoin       CTX_STRH('l','i','n','e','J','o','i','n',0,0,0,0,0,0)
-#define CTX_setLineJoin    CTX_STRH('s','e','t','L','i','n','e','J','o','i','n',0,0,0)
-#define CTX_line_spacing   CTX_STRH('l','i','n','e','_','s','p','a','c','i','n','g',0,0)
-#define CTX_line_to        CTX_STRH('l','i','n','e','_','t','o',0,0,0,0,0,0,0)
-#define CTX_lineTo         CTX_STRH('l','i','n','e','T','o',0,0,0,0,0,0,0,0)
-#define CTX_lineDash       CTX_STRH('l','i','n','e','D','a','s','h',0,0,0,0,0,0)
-#define CTX_line_width     CTX_STRH('l','i','n','e','_','w','i','d','t','h',0,0,0,0)
-#define CTX_lineWidth      CTX_STRH('l','i','n','e','W','i','d','t','h',0,0,0,0,0)
-#define CTX_setLineWidth   CTX_STRH('s','e','t','L','i','n','e','W','i','d','t','h',0,0)
-#define CTX_view_box       CTX_STRH('v','i','e','w','_','b','o','x',0,0,0,0,0,0)
-#define CTX_viewBox        CTX_STRH('v','i','e','w','B','o','x',0,0,0,0,0,0,0)
-#define CTX_middle         CTX_STRH('m','i','d','d','l','e',0, 0, 0, 0, 0, 0,0,0)
-#define CTX_miter          CTX_STRH('m','i','t','e','r',0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_miter_limit    CTX_STRH('m','i','t','e','r','_','l','i','m','i','t',0,0,0)
-#define CTX_miterLimit     CTX_STRH('m','i','t','e','r','L','i','m','i','t',0,0,0,0)
-#define CTX_move_to        CTX_STRH('m','o','v','e','_','t','o',0,0,0,0,0,0,0)
-#define CTX_moveTo         CTX_STRH('m','o','v','e','T','o',0,0,0,0,0,0,0,0)
-#define CTX_multiply       CTX_STRH('m','u','l','t','i','p','l','y',0,0,0,0,0,0)
-#define CTX_new_page       CTX_STRH('n','e','w','_','p','a','g','e',0,0,0,0,0,0)
-#define CTX_newPage        CTX_STRH('n','e','w','P','a','g','e',0,0,0,0,0,0,0)
-#define CTX_new_path       CTX_STRH('n','e','w','_','p','a','t','h',0,0,0,0,0,0)
-#define CTX_newPath        CTX_STRH('n','e','w','P','a','t','h',0,0,0,0,0,0,0)
-#define CTX_new_state      CTX_STRH('n','e','w','_','s','t','a','t','e',0,0,0,0,0)
-#define CTX_none           CTX_STRH('n','o','n','e', 0 ,0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_normal         CTX_STRH('n','o','r','m','a','l',0,0,0,0,0,0,0,0)
-#define CTX_quad_to        CTX_STRH('q','u','a','d','_','t','o',0,0,0,0,0,0,0)
-#define CTX_quadTo         CTX_STRH('q','u','a','d','T','o',0,0,0,0,0,0,0,0)
-#define CTX_radial_gradient CTX_STRH('r','a','d','i','a','l','_','g','r','a','d','i','e','n')
-#define CTX_radialGradient  CTX_STRH('r','a','d','i','a','l','G','r','a','d','i','e','n','t')
-#define CTX_rectangle      CTX_STRH('r','e','c','t','a','n','g','l','e',0,0,0,0,0)
-#define CTX_rect           CTX_STRH('r','e','c','t',0,0,0,0,0,0,0,0,0,0)
-#define CTX_rel_arc_to     CTX_STRH('r','e','l','_','a','r','c','_','t','o',0,0,0,0)
-#define CTX_relArcTo       CTX_STRH('r','e','l','A','r','c','T','o',0,0,0,0,0,0)
-#define CTX_rel_curve_to   CTX_STRH('r','e','l','_','c','u','r','v','e','_','t','o',0,0)
-#define CTX_relCurveTo     CTX_STRH('r','e','l','C','u','r','v','e','T','o',0,0,0,0)
-#define CTX_rel_hor_line_to CTX_STRH('r','e','l','_','h','o','r','_','l','i','n','e',0,0)
-#define CTX_relHorLineTo   CTX_STRH('r','e','l','H','o','r','L','i','n','e','T','o',0,0)
-#define CTX_rel_line_to    CTX_STRH('r','e','l','_','l','i','n','e','_','t','o',0,0,0)
-#define CTX_relLineTo      CTX_STRH('r','e','l','L','i','n','e','T','o',0,0,0,0,0)
-#define CTX_rel_move_to    CTX_STRH('r','e','l','_','m','o','v','e','_','t','o',0,0,0)
-#define CTX_relMoveTo      CTX_STRH('r','e','l','M','o','v','e','T','o',0,0,0,0,0)
-#define CTX_rel_quad_to    CTX_STRH('r','e','l','_','q','u','a','d','_','t','o',0,0,0)
-#define CTX_relQuadTo      CTX_STRH('r','e','l','Q','u','a','d','T','o',0,0,0,0,0)
-#define CTX_rel_smoothq_to CTX_STRH('r','e','l','_','s','m','o','o','t','h','q','_','t','o')
-#define CTX_relSmoothqTo   CTX_STRH('r','e','l','S','m','o','o','t','h','q','T','o',0,0)
-#define CTX_rel_smooth_to  CTX_STRH('r','e','l','_','s','m','o','o','t','h','_','t','o',0)
-#define CTX_relSmoothTo    CTX_STRH('r','e','l','S','m','o','o','t','h','T','o',0,0,0)
-#define CTX_rel_ver_line_to CTX_STRH('r','e','l','_','v','e','r','_','l','i','n','e',0,0)
-#define CTX_relVerLineTo   CTX_STRH('r','e','l','V','e','r','L','i','n','e','T','o',0,0)
-#define CTX_restore        CTX_STRH('r','e','s','t','o','r','e',0,0,0,0,0,0,0)
-#define CTX_reset          CTX_STRH('r','e','s','e','t',0,0,0,0,0,0,0,0,0)
-#define CTX_rgba           CTX_STRH('r','g','b','a',0,0,0,0,0,0,0,0,0,0)
-#define CTX_rgb            CTX_STRH('r','g','b',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_right          CTX_STRH('r','i','g','h','t',0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_rotate         CTX_STRH('r','o','t','a','t','e',0,0,0,0,0,0,0,0)
-#define CTX_round          CTX_STRH('r','o','u','n','d',0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_round_rectangle CTX_STRH('r','o','u','n','d','_','r','e','c','t','a','n','g','l')
-#define CTX_roundRectangle  CTX_STRH('r','o','u','n','d','R','e','c','t','a','n','g','l','e')
-#define CTX_save           CTX_STRH('s','a','v','e',0,0,0,0,0,0,0,0,0,0)
-#define CTX_save           CTX_STRH('s','a','v','e',0,0,0,0,0,0,0,0,0,0)
-#define CTX_scale          CTX_STRH('s','c','a','l','e',0,0,0,0,0,0,0,0,0)
-#define CTX_screen         CTX_STRH('s','c','r','e','e','n',0,0,0,0,0,0,0,0)
-#define CTX_setkey         CTX_STRH('s','e','t','k','e','y',0,0,0,0,0,0,0,0)
-#define CTX_shadowBlur     CTX_STRH('s','h','a','d','o','w','B','l','u','r',0,0,0,0)
-#define CTX_shadowColor    CTX_STRH('s','h','a','d','o','w','C','o','l','o','r',0,0,0)
-#define CTX_shadowOffsetX  CTX_STRH('s','h','a','d','o','w','O','f','f','s','e','t','X',0)
-#define CTX_shadowOffsetY  CTX_STRH('s','h','a','d','o','w','O','f','f','s','e','t','Y',0)
-#define CTX_smooth_quad_to CTX_STRH('s','m','o','o','t','h','_','q','u','a','d','_','t','o')
-#define CTX_smoothQuadTo   CTX_STRH('s','m','o','o','t','h','Q','u','a','d','T','o',0,0)
-#define CTX_smooth_to      CTX_STRH('s','m','o','o','t','h','_','t','o',0,0,0,0,0)
-#define CTX_smoothTo       CTX_STRH('s','m','o','o','t','h','T','o',0,0,0,0,0,0)
-#define CTX_sourceIn       CTX_STRH('s','o','u','r','c','e','I','n',0,0,0,0,0,0)
-#define CTX_source_in      CTX_STRH('s','o','u','r','c','e','_','i','n',0,0,0,0,0)
-#define CTX_sourceAtop     CTX_STRH('s','o','u','r','c','e','A','t','o','p',0,0,0,0)
-#define CTX_source_atop    CTX_STRH('s','o','u','r','c','e','_','a','t','o','p',0,0,0)
-#define CTX_sourceOut      CTX_STRH('s','o','u','r','c','e','O','u','t',0,0,0,0,0)
-#define CTX_source_out     CTX_STRH('s','o','u','r','c','e','_','o','u','t',0,0,0,0)
-#define CTX_sourceOver     CTX_STRH('s','o','u','r','c','e','O','v','e','r',0,0,0,0)
-#define CTX_source_over    CTX_STRH('s','o','u','r','c','e','_','o','v','e','r',0,0,0)
-#define CTX_square         CTX_STRH('s','q','u','a','r','e', 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_start          CTX_STRH('s','t','a','r','t',0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_start_move     CTX_STRH('s','t','a','r','t','_','m','o','v','e',0,0,0,0)
-#define CTX_start_group    CTX_STRH('s','t','a','r','t','_','G','r','o','u','p',0,0,0)
-#define CTX_startGroup     CTX_STRH('s','t','a','r','t','G','r','o','u','p',0,0,0,0)
-#define CTX_stroke         CTX_STRH('s','t','r','o','k','e',0,0,0,0,0,0,0,0)
-#define CTX_text_align     CTX_STRH('t','e','x','t','_','a','l','i','g','n',0, 0,0,0)
-#define CTX_textAlign      CTX_STRH('t','e','x','t','A','l','i','g','n',0, 0, 0,0,0)
-#define CTX_texture        CTX_STRH('t','e','x','t','u','r','e',0,0,0, 0, 0,0,0)
-#define CTX_text_baseline  CTX_STRH('t','e','x','t','_','b','a','s','e','l','i','n','e',0)
-#define CTX_text_baseline  CTX_STRH('t','e','x','t','_','b','a','s','e','l','i','n','e',0)
-#define CTX_textBaseline   CTX_STRH('t','e','x','t','B','a','s','e','l','i','n','e',0,0)
-#define CTX_text           CTX_STRH('t','e','x','t',0,0,0,0,0,0,0,0,0,0)
-#define CTX_text_direction CTX_STRH('t','e','x','t','_','d','i','r','e','c','t','i','o','n')
-#define CTX_textDirection  CTX_STRH('t','e','x','t','D','i','r','e','c','t','i','o','n',0)
-#define CTX_text_indent    CTX_STRH('t','e','x','t','_','i','n','d','e','n','t', 0,0,0)
-#define CTX_text_stroke    CTX_STRH('t','e','x','t','_','s','t','r','o','k','e', 0,0,0)
-#define CTX_textStroke     CTX_STRH('t','e','x','t','S','t','r','o','k','e', 0, 0,0,0)
-#define CTX_top            CTX_STRH('t','o','p',0,0,0, 0, 0, 0, 0, 0, 0,0,0)
-#define CTX_transform      CTX_STRH('t','r','a','n','s','f','o','r','m',0,0,0,0,0)
-#define CTX_translate      CTX_STRH('t','r','a','n','s','l','a','t','e',0,0,0,0,0)
-#define CTX_verLineTo      CTX_STRH('v','e','r','L','i','n','e','T','o',0,0,0,0,0)
-#define CTX_ver_line_to    CTX_STRH('v','e','r','_','l','i','n','e','_','t','o',0,0,0)
-#define CTX_width          CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0)
-#define CTX_winding        CTX_STRH('w','i','n','d','i','n', 'g', 0, 0, 0, 0, 0,0,0)
-#define CTX_x              CTX_STRH('x',0,0,0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_xor            CTX_STRH('x','o','r',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_y              CTX_STRH('y',0,0,0,0,0,0,0,0,0,0,0,0,0)
-
-
-#define CTX_colorSpace     CTX_STRH('c','o','l','o','r','S','p','a','c','e',0,0,0,0)
-#define CTX_userRGB        CTX_STRH('u','s','e','r','R','G','B',0,0,0,0,0,0,0)
-#define CTX_userCMYK       CTX_STRH('u','s','e','r','C','M','Y','K',0,0,0,0,0,0)
-#define CTX_deviceRGB      CTX_STRH('d','e','v','i','c','e','r','R','G','B',0,0,0,0)
-#define CTX_deviceCMYK     CTX_STRH('d','e','v','i','c','e','r','C','M','Y','K',0,0,0)
+#define TOKENHASH(a)    ((uint64_t)a)
+
+#define CTX_add_stop   TOKENHASH(1274978316678)
+#define CTX_addStop    TOKENHASH(40799943078278)
+#define CTX_alphabetic         TOKENHASH(2629359926678406)
+#define CTX_arc        TOKENHASH(11526)
+#define CTX_arc_to     TOKENHASH(1187065094)
+#define CTX_arcTo      TOKENHASH(38558051590)
+#define CTX_begin_path         TOKENHASH(3004110681622984)
+#define CTX_beginPath  TOKENHASH(8437143659599196)
+#define CTX_bevel      TOKENHASH(29868488)
+#define CTX_bottom     TOKENHASH(1043772488)
+#define CTX_cap        TOKENHASH(37066)
+#define CTX_center     TOKENHASH(1358332362)
+#define CTX_clear      TOKENHASH(42154890)
+#define CTX_color      TOKENHASH(43086922)
+#define CTX_copy       TOKENHASH(1807434)
+#define CTX_clip       TOKENHASH(1203082)
+#define CTX_close_path         TOKENHASH(3004110663420810)
+#define CTX_closePath  TOKENHASH(8437144279135038)
+#define CTX_cmyka      TOKENHASH(7199690)
+#define CTX_cmyk       TOKENHASH(908234)
+#define CTX_cmykaS     TOKENHASH(36313095114)
+#define CTX_cmykS      TOKENHASH(1135467466)
+#define CTX_color      TOKENHASH(43086922)
+#define CTX_blending   TOKENHASH(653586873224)
+#define CTX_blend      TOKENHASH(13646728)
+#define CTX_blending_mode      TOKENHASH(8147360531130856)
+#define CTX_blendingMode       TOKENHASH(7483585768187540)
+#define CTX_blend_mode         TOKENHASH(2758775686577032)
+#define CTX_blendMode  TOKENHASH(7773213171090182)
+#define CTX_composite  TOKENHASH(16930059746378)
+#define CTX_compositing_mode   TOKENHASH(2417816728103524)
+#define CTX_compositingMode    TOKENHASH(2807194446992106)
+#define CTX_curve_to   TOKENHASH(1215559149002)
+#define CTX_curveTo    TOKENHASH(39483449320906)
+#define CTX_darken     TOKENHASH(1089315020)
+#define CTX_defineGlyph        TOKENHASH(2497926167421194)
+#define CTX_defineTexture      TOKENHASH(2623744577477404)
+#define CTX_kerningPair        TOKENHASH(6964644556489058)
+#define CTX_destinationIn      TOKENHASH(8153299143600102)
+#define CTX_destination_in     TOKENHASH(3824201982576824)
+#define CTX_destinationAtop    TOKENHASH(8185118415574560)
+#define CTX_destination_atop   TOKENHASH(7742210324901698)
+#define CTX_destinationOver    TOKENHASH(3261713333438500)
+#define CTX_destination_over   TOKENHASH(7742210324728474)
+#define CTX_destinationOut     TOKENHASH(7742210322269456)
+#define CTX_destination_out    TOKENHASH(8153299143489102)
+#define CTX_difference         TOKENHASH(2756492040618700)
+#define CTX_done       TOKENHASH(492620)
+#define CTX_drgba      TOKENHASH(6573324)
+#define CTX_drgb       TOKENHASH(281868)
+#define CTX_drgbaS     TOKENHASH(36312468748)
+#define CTX_drgbS      TOKENHASH(1134841100)
+#define CTX_end        TOKENHASH(13326)
+#define CTX_endfun     TOKENHASH(1122513934)
+#define CTX_end_group  TOKENHASH(41200834917390)
+#define CTX_endGroup   TOKENHASH(3570227948106766)
+#define CTX_even_odd   TOKENHASH(426345774606)
+#define CTX_evenOdd    TOKENHASH(13671748091406)
+#define CTX_exit       TOKENHASH(1465998)
+#define CTX_fill       TOKENHASH(946896)
+#define CTX_fill_rule  TOKENHASH(16405972808400)
+#define CTX_fillRule   TOKENHASH(2776813389378256)
+#define CTX_flush      TOKENHASH(22395792)
+#define CTX_font       TOKENHASH(1475664)
+#define CTX_font_size  TOKENHASH(17342343316560)
+#define CTX_setFontSize        TOKENHASH(8657699789799734)
+#define CTX_fontSize   TOKENHASH(2806775148872784)
+#define CTX_function   TOKENHASH(1136803546576)
+#define CTX_getkey     TOKENHASH(1827516882)
+#define CTX_global_alpha       TOKENHASH(6945103263242432)
+#define CTX_globalAlpha        TOKENHASH(2684560928159160)
+#define CTX_glyph      TOKENHASH(22207378)
+#define CTX_gradient_add_stop  TOKENHASH(7829524561074416)
+#define CTX_gradientAddStop    TOKENHASH(8126442749593072)
+#define CTX_graya      TOKENHASH(8068370)
+#define CTX_gray       TOKENHASH(1776914)
+#define CTX_grayaS     TOKENHASH(36313963794)
+#define CTX_grayS      TOKENHASH(1136336146)
+#define CTX_hanging    TOKENHASH(20424786132)
+#define CTX_height     TOKENHASH(1497979348)
+#define CTX_hor_line_to        TOKENHASH(8345271542735158)
+#define CTX_horLineTo  TOKENHASH(3629696407754856)
+#define CTX_hue        TOKENHASH(15828)
+#define CTX_identity   TOKENHASH(1903455910294)
+#define CTX_ideographic        TOKENHASH(4370819675496700)
+#define CTX_imageSmoothing     TOKENHASH(4268778175825416)
+#define CTX_join       TOKENHASH(1072216)
+#define CTX_laba       TOKENHASH(205020)
+#define CTX_lab        TOKENHASH(8412)
+#define CTX_lcha       TOKENHASH(217436)
+#define CTX_lch        TOKENHASH(20828)
+#define CTX_labaS      TOKENHASH(1134764252)
+#define CTX_labS       TOKENHASH(35463388)
+#define CTX_lchaS      TOKENHASH(1134776668)
+#define CTX_lchS       TOKENHASH(35475804)
+#define CTX_left       TOKENHASH(1458652)
+#define CTX_lighter    TOKENHASH(43466246876)
+#define CTX_lighten    TOKENHASH(34876312284)
+#define CTX_linear_gradient    TOKENHASH(7801595375834212)
+#define CTX_linearGradient     TOKENHASH(4439260636789186)
+#define CTX_line_cap   TOKENHASH(1243731165916)
+#define CTX_lineCap    TOKENHASH(3436510399409980)
+#define CTX_setLineCap         TOKENHASH(7897176123029482)
+#define CTX_line_height        TOKENHASH(3300223516389168)
+#define CTX_line_join  TOKENHASH(35977601450716)
+#define CTX_lineJoin   TOKENHASH(3403122163024604)
+#define CTX_setLineJoin        TOKENHASH(2768281536656332)
+#define CTX_line_spacing       TOKENHASH(2519451230887150)
+#define CTX_line_to    TOKENHASH(37986206428)
+#define CTX_lineTo     TOKENHASH(1233857774300)
+#define CTX_lineDash   TOKENHASH(3001937455186652)
+#define CTX_lineDashOffset     TOKENHASH(3704120356324362)
+#define CTX_line_width         TOKENHASH(3004303386575580)
+#define CTX_lineWidth  TOKENHASH(8241159254028040)
+#define CTX_setLineWidth       TOKENHASH(8037913618228476)
+#define CTX_view_box   TOKENHASH(1823485803248)
+#define CTX_viewBox    TOKENHASH(3915860941641152)
+#define CTX_middle     TOKENHASH(499528414)
+#define CTX_miter      TOKENHASH(42447582)
+#define CTX_miter_limit        TOKENHASH(4281255327472850)
+#define CTX_miterLimit         TOKENHASH(7937453649653124)
+#define CTX_move_to    TOKENHASH(37986223198)
+#define CTX_moveTo     TOKENHASH(1233857791070)
+#define CTX_multiply   TOKENHASH(1886723143134)
+#define CTX_new_page   TOKENHASH(500602882528)
+#define CTX_newPage    TOKENHASH(16020123011552)
+#define CTX_new_path   TOKENHASH(734678600160)
+#define CTX_newPath    TOKENHASH(23510545975776)
+#define CTX_new_state  TOKENHASH(16912954280416)
+#define CTX_none       TOKENHASH(492640)
+#define CTX_normal     TOKENHASH(946840672)
+#define CTX_quad_to    TOKENHASH(37986115046)
+#define CTX_quadTo     TOKENHASH(1233857682918)
+#define CTX_radial_gradient    TOKENHASH(8267515704460560)
+#define CTX_radialGradient     TOKENHASH(4399889250822134)
+#define CTX_rectangle  TOKENHASH(16375644301800)
+#define CTX_rect       TOKENHASH(1452520)
+#define CTX_rel_arc_to         TOKENHASH(3496527781786088)
+#define CTX_relArcTo   TOKENHASH(3209152175601038)
+#define CTX_rel_curve_to       TOKENHASH(4439651822639910)
+#define CTX_relCurveTo         TOKENHASH(7294415873689320)
+#define CTX_rel_hor_line_to    TOKENHASH(7051067105640810)
+#define CTX_relHorLineTo       TOKENHASH(8737419863647946)
+#define CTX_relVerLineTo       TOKENHASH(8737441317512906)
+#define CTX_rel_line_to        TOKENHASH(8345271542378314)
+#define CTX_relLineTo  TOKENHASH(3629696197927444)
+#define CTX_rel_move_to        TOKENHASH(8344984486309706)
+#define CTX_relMoveTo  TOKENHASH(3571677202293268)
+#define CTX_rel_quad_to        TOKENHASH(8343627754794826)
+#define CTX_relQuadTo  TOKENHASH(7894357900599828)
+#define CTX_rel_smoothq_to     TOKENHASH(7340038162167138)
+#define CTX_relSmoothqTo       TOKENHASH(3188040406230844)
+#define CTX_rel_smooth_to      TOKENHASH(8144941131301668)
+#define CTX_relSmoothTo        TOKENHASH(8947422784198618)
+#define CTX_rel_ver_line_to    TOKENHASH(8148126344839530)
+#define CTX_restore    TOKENHASH(16411699688)
+#define CTX_reset      TOKENHASH(46639592)
+#define CTX_rgba       TOKENHASH(205416)
+#define CTX_rgb        TOKENHASH(8808)
+#define CTX_rgbaS      TOKENHASH(1134764648)
+#define CTX_rgbS       TOKENHASH(35463784)
+#define CTX_right      TOKENHASH(46811880)
+#define CTX_rotate     TOKENHASH(516142184)
+#define CTX_round      TOKENHASH(13679720)
+#define CTX_round_rectangle    TOKENHASH(4332080966833870)
+#define CTX_roundRectangle     TOKENHASH(8317255488676642)
+#define CTX_save       TOKENHASH(508138)
+#define CTX_scale      TOKENHASH(15604074)
+#define CTX_screen     TOKENHASH(1088921962)
+#define CTX_setkey     TOKENHASH(1827516906)
+#define CTX_shadowBlur         TOKENHASH(2924056626980284)
+#define CTX_shadowColor        TOKENHASH(3509599043947446)
+#define CTX_shadowOffsetX      TOKENHASH(8499312693589794)
+#define CTX_shadowOffsetY      TOKENHASH(8499312693589796)
+#define CTX_smooth_quad_to     TOKENHASH(6832232668547050)
+#define CTX_smoothQuadTo       TOKENHASH(8278352345012646)
+#define CTX_smooth_to  TOKENHASH(38898089692138)
+#define CTX_smoothTo   TOKENHASH(3515270388878314)
+#define CTX_sourceIn   TOKENHASH(3444145493687402)
+#define CTX_source_in  TOKENHASH(35942915423338)
+#define CTX_sourceAtop         TOKENHASH(2920281959978332)
+#define CTX_source_atop        TOKENHASH(3007410591464110)
+#define CTX_sourceOut  TOKENHASH(7371294932695718)
+#define CTX_source_out         TOKENHASH(3851660580666474)
+#define CTX_sourceOver         TOKENHASH(7584784067385004)
+#define CTX_source_over        TOKENHASH(8690648756484770)
+#define CTX_square     TOKENHASH(511950058)
+#define CTX_start      TOKENHASH(47455658)
+#define CTX_start_move         TOKENHASH(2798358138985898)
+#define CTX_start_group        TOKENHASH(7836274863228782)
+#define CTX_startGroup         TOKENHASH(3812645199786240)
+#define CTX_stroke     TOKENHASH(498181546)
+#define CTX_text_align         TOKENHASH(3398277113762284)
+#define CTX_textAlign  TOKENHASH(3063795447820748)
+#define CTX_texture    TOKENHASH(16424292844)
+#define CTX_text_baseline      TOKENHASH(2589194334827348)
+#define CTX_text_baseline      TOKENHASH(2589194334827348)
+#define CTX_textBaseline       TOKENHASH(8381669925369340)
+#define CTX_fillRect   TOKENHASH(3811453831115472)
+#define CTX_text       TOKENHASH(1495532)
+#define CTX_text_direction     TOKENHASH(3614589880641524)
+#define CTX_textDirection      TOKENHASH(6790122975782654)
+#define CTX_text_indent        TOKENHASH(3633795456290560)
+#define CTX_text_stroke        TOKENHASH(8259523149811490)
+#define CTX_strokeText         TOKENHASH(8131451867629426)
+#define CTX_strokeRect         TOKENHASH(8165399289138988)
+#define CTX_top        TOKENHASH(37996)
+#define CTX_transform  TOKENHASH(34396827557164)
+#define CTX_translate  TOKENHASH(16912418348332)
+#define CTX_verLineTo  TOKENHASH(3629696413166220)
+#define CTX_ver_line_to        TOKENHASH(8345271542726354)
+#define CTX_width      TOKENHASH(22426354)
+#define CTX_winding    TOKENHASH(20424590066)
+#define CTX_x  TOKENHASH(52)
+#define CTX_xor        TOKENHASH(42100)
+#define CTX_y  TOKENHASH(54)
+#define CTX_colorSpace         TOKENHASH(3674150843793134)
+#define CTX_userRGB    TOKENHASH(59177128181102)
+#define CTX_userCMYK   TOKENHASH(3354734206905240)
+#define CTX_deviceRGB  TOKENHASH(7818727413767480)
+#define CTX_deviceCMYK         TOKENHASH(8943291245184210)
+#define CTX_silver     TOKENHASH(1358459626)
+#define CTX_fuchsia    TOKENHASH(7225355728)
+#define CTX_gray       TOKENHASH(1776914)
+#define CTX_yellow     TOKENHASH(1714319862)
+#define CTX_white      TOKENHASH(16145074)
+#define CTX_maroon     TOKENHASH(1110548702)
+#define CTX_magenta    TOKENHASH(7952877790)
+#define CTX_blue       TOKENHASH(506760)
+#define CTX_green      TOKENHASH(34028818)
+#define CTX_red        TOKENHASH(12776)
+#define CTX_purple     TOKENHASH(500344292)
+#define CTX_olive      TOKENHASH(16276386)
+#define CTX_teal       TOKENHASH(924140)
+#define CTX_black      TOKENHASH(27597704)
+#define CTX_cyan       TOKENHASH(1056458)
+#define CTX_navy       TOKENHASH(1818848)
+#define CTX_lime       TOKENHASH(490204)
+#define CTX_aqua       TOKENHASH(244934)
+#define CTX_transparent        TOKENHASH(3654078210101184)
+#define CTX_currentColor       TOKENHASH(7501877057638746)
 
 #endif
 #ifndef __CTX_LIBC_H
@@ -4744,9 +4841,6 @@ typedef struct _CtxSource CtxSource;
 #define CTX_VALID_GRAYA_U8    (1<<6)
 #define CTX_VALID_LABA        ((1<<7) | CTX_VALID_GRAYA)
 
-//_ctx_target_space (ctx, icc);
-//_ctx_space (ctx);
-
 struct _CtxColor
 {
   uint8_t magic; // for colors used in keydb, set to a non valid start of
@@ -4801,7 +4895,7 @@ struct _CtxGradientStop
 enum _CtxSourceType
 {
   CTX_SOURCE_COLOR = 0,
-  CTX_SOURCE_IMAGE,
+  CTX_SOURCE_TEXTURE,
   CTX_SOURCE_LINEAR_GRADIENT,
   CTX_SOURCE_RADIAL_GRADIENT,
 };
@@ -4817,11 +4911,25 @@ struct _CtxBuffer
   int                 width;
   int                 height;
   int                 stride;
-  int                 revision; // XXX NYI, number to update when contents change
-                                //
+  char               *eid;        // might be NULL, when not - should be unique for pixel contents
+  int                 frame;      // last frame used in, everything > 3 can be removed,
+                                  // as clients wont rely on it.
   CtxPixelFormatInfo *format;
   void (*free_func) (void *pixels, void *user_data);
   void               *user_data;
+
+#if CTX_ENABLE_CM
+#if CTX_BABL
+  const Babl *space;
+#else
+  void       *space; 
+#endif
+#endif
+#if 1
+  CtxBuffer          *color_managed; /* only valid for one render target, cache
+                                        for a specific space
+                                        */
+#endif
 };
 
 //void _ctx_user_to_device          (CtxState *state, float *x, float *y);
@@ -4848,7 +4956,7 @@ struct _CtxSource
       float x0;
       float y0;
       CtxBuffer *buffer;
-    } image;
+    } texture;
     struct
     {
       float x0;
@@ -4881,12 +4989,13 @@ struct _CtxGState
   int           stringpool_pos;
 
   CtxMatrix     transform;
-  //CtxSource   source_stroke;
-  CtxSource     source;
+  CtxSource     source_stroke;
+  CtxSource     source_fill;
   float         global_alpha_f;
   uint8_t       global_alpha_u8;
 
   float         line_width;
+  float         line_dash_offset;
   float         miter_limit;
   float         font_size;
 #if CTX_ENABLE_SHADOW_BLUR
@@ -4904,16 +5013,21 @@ struct _CtxGState
 #if CTX_ENABLE_CM
 #if CTX_BABL
   const Babl   *device_space;
+  const Babl   *texture_space;
   const Babl   *rgb_space;       
   const Babl   *cmyk_space;
 
   const Babl   *fish_rgbaf_user_to_device;
+  const Babl   *fish_rgbaf_texture_to_device;
   const Babl   *fish_rgbaf_device_to_user;
+
 #else
   void         *device_space;
+  void         *texture_space;
   void         *rgb_space;       
   void         *cmyk_space;
   void         *fish_rgbaf_user_to_device; // dummy padding
+  void         *fish_rgbaf_texture_to_device; // dummy padding
   void         *fish_rgbaf_device_to_user; // dummy padding
 #endif
 #endif
@@ -4923,14 +5037,15 @@ struct _CtxGState
   float dashes[CTX_PARSER_MAX_ARGS];
   int n_dashes;
 
-  CtxColorModel color_model;
+  CtxColorModel    color_model;
   /* bitfield-pack small state-parts */
-  CtxLineCap                  line_cap:2;
-  CtxLineJoin                line_join:2;
-  CtxFillRule                fill_rule:1;
-  unsigned int                    font:6;
-  unsigned int                    bold:1;
-  unsigned int                  italic:1;
+  CtxLineCap          line_cap:2;
+  CtxLineJoin        line_join:2;
+  CtxFillRule        fill_rule:1;
+  unsigned int image_smoothing:1;
+  unsigned int            font:6;
+  unsigned int            bold:1;
+  unsigned int          italic:1;
 };
 
 typedef enum
@@ -4958,8 +5073,6 @@ struct _CtxDrawlist
   int       bitpack_pos;  // stream is bitpacked up to this offset
 };
 
-
-
 #define CTX_MAX_KEYDB 64 // number of entries in keydb
                          // entries are "copy-on-change" between states
 
@@ -4970,7 +5083,7 @@ struct _CtxDrawlist
 typedef struct _CtxKeyDbEntry CtxKeyDbEntry;
 struct _CtxKeyDbEntry
 {
-  uint32_t key;
+  uint64_t key;
   float value;
   //union { float f[1]; uint8_t u8[4]; }value;
 };
@@ -5150,16 +5263,26 @@ struct _CtxEvents
 
 #endif
 
-struct
-_Ctx
+typedef struct _CtxEidInfo
+{
+  char *eid;
+  int   frame;
+  int   width;
+  int   height;
+} CtxEidInfo;
+
+struct _Ctx
 {
   CtxImplementation *renderer;
   CtxDrawlist        drawlist;
   int                transformation;
   CtxBuffer          texture[CTX_MAX_TEXTURES];
+  Ctx               *texture_cache;
+  CtxList           *eid_db;
   int                rev;
   void              *backend;
   CtxState           state;        /**/
+  int                frame; /* used for texture lifetime */
 #if CTX_EVENTS 
   CtxCursor          cursor;
   int                quit;
@@ -5184,12 +5307,17 @@ void ctx_buffer_free (CtxBuffer *buffer);
 void
 ctx_state_gradient_clear_stops (CtxState *state);
 
-
 void ctx_interpret_style         (CtxState *state, CtxEntry *entry, void *data);
 void ctx_interpret_transforms    (CtxState *state, CtxEntry *entry, void *data);
 void ctx_interpret_pos           (CtxState *state, CtxEntry *entry, void *data);
 void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data);
 
+struct _CtxInternalFsEntry
+{
+  char *path;
+  int   length;
+  char *data;
+};
 
 struct _CtxPixelFormatInfo
 {
@@ -5237,6 +5365,8 @@ ctx_unichar_to_utf8 (uint32_t  ch,
 
 uint32_t
 ctx_utf8_to_unichar (const char *input);
+
+
 typedef struct _CtxHasher CtxHasher;
 
 typedef struct CtxEdge
@@ -5306,9 +5436,6 @@ struct _CtxRasterizer
   int        col_min;
   int        col_max;
 
-#if CTX_BRAILLE_TEXT
-  CtxList   *glyphs;
-#endif
 
   CtxDrawlist edge_list;
 
@@ -5318,11 +5445,13 @@ struct _CtxRasterizer
 
   void      *buf;
 
+
 #if CTX_COMPOSITING_GROUPS
   void      *saved_buf; // when group redirected
   CtxBuffer *group[CTX_GROUP_MAX];
 #endif
 
+
   float      x;  // < redundant? use state instead?
   float      y;
 
@@ -5333,9 +5462,6 @@ struct _CtxRasterizer
   int        has_prev:2;
   int        preserve:1;
   int        uses_transforms:1;
-#if CTX_BRAILLE_TEXT
-  int        term_glyphs:1; // store appropriate glyphs for redisplay
-#endif
 
   int16_t    blit_x;
   int16_t    blit_y;
@@ -5363,10 +5489,32 @@ struct _CtxRasterizer
   CtxBuffer *clip_buffer;
 #endif
 
-  uint8_t *clip_mask;
+  int clip_rectangle;
+
 #if CTX_SHAPE_CACHE
   CtxShapeCache shape_cache;
 #endif
+#if CTX_BRAILLE_TEXT
+  int        term_glyphs:1; // store appropriate glyphs for redisplay
+  CtxList   *glyphs;
+#endif
+};
+
+struct _CtxSHA1 {
+    uint64_t length;
+    uint32_t state[5], curlen;
+    unsigned char buf[64];
+};
+
+
+struct _CtxHasher
+{
+  CtxRasterizer rasterizer;
+  int           cols;
+  int           rows;
+  uint8_t      *hashes;
+  CtxSHA1       sha1_fill; 
+  CtxSHA1       sha1_stroke;
 };
 
 #if CTX_RASTERIZER
@@ -5426,40 +5574,20 @@ struct _CtxCtx
    int  was_down;
 };
 
-// XXX common members of sdl and fbdev, it is more!
-typedef struct _CtxThreaded CtxThreaded;
-struct _CtxThreaded
-{
-   void (*render) (void *fb, CtxCommand *command);
-   void (*reset)  (void *fb);
-   void (*flush)  (void *fb);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)   (void *fb);
-   Ctx          *ctx;
-   Ctx          *ctx_copy;
-   int           width;
-   int           height;
-   int           cols;
-   int           rows;
-   int           was_down;
-   Ctx          *host[CTX_MAX_THREADS];
-   CtxAntialias  antialias;
-};
-
 
 extern int _ctx_max_threads;
 extern int _ctx_enable_hash_cache;
 void
-ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len);
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
 const char *
 ctx_get (Ctx *ctx, const char *key);
 
-int ctx_renderer_is_braille (Ctx *ctx);
+int ctx_renderer_is_term (Ctx *ctx);
 Ctx *ctx_new_ctx (int width, int height);
 Ctx *ctx_new_fb (int width, int height, int drm);
 Ctx *ctx_new_sdl (int width, int height);
-Ctx *ctx_new_braille (int width, int height);
+Ctx *ctx_new_term (int width, int height);
+Ctx *ctx_new_termimg (int width, int height);
 
 int ctx_resolve_font (const char *name);
 extern float ctx_u8_float[256];
@@ -5514,11 +5642,11 @@ static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, f
 
 int ctx_color_model_get_components (CtxColorModel model);
 
-void ctx_state_set (CtxState *state, uint32_t key, float value);
+void ctx_state_set (CtxState *state, uint64_t key, float value);
 static void
 ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f);
 static void ctx_font_setup ();
-float ctx_state_get (CtxState *state, uint32_t hash);
+float ctx_state_get (CtxState *state, uint64_t hash);
 
 #if CTX_RASTERIZER
 
@@ -5645,827 +5773,1052 @@ static inline void *ctx_calloc (size_t size, size_t count);
 
 void ctx_screenshot (Ctx *ctx, const char *output_path);
 
-typedef struct _CtxSHA1 CtxSHA1;
 
 CtxSHA1 *ctx_sha1_new (void);
 void ctx_sha1_free (CtxSHA1 *sha1);
 int ctx_sha1_process(CtxSHA1 *sha1, const unsigned char * msg, unsigned long len);
 int ctx_sha1_done(CtxSHA1 * sha1, unsigned char *out);
 
+void _ctx_texture_lock (void);
+void _ctx_texture_unlock (void);
+uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry);
+void ctx_buffer_pixels_free (void *pixels, void *userdata);
 
-#endif
-
+/*ctx_texture_init:
+ * return value: eid, as passed in or if NULL generated by hashing pixels and width/height
+ * XXX  this is low-level and not to be used directly use define_texture instead.  XXX
+ */
+const char *ctx_texture_init (
+                      Ctx        *ctx,
+                      const char *eid,
+                      int         width,
+                      int         height,
+                      int         stride,
+                      CtxPixelFormat format,
+                      void       *space,
+                      uint8_t    *pixels,
+                      void (*freefunc) (void *pixels, void *user_data),
+                      void *user_data);
 
-#if CTX_IMPLEMENTATION
-#ifndef CTX_DRAWLIST_H
-#define CTX_DRAWLIST_H
+#if CTX_TILED
+#include <threads.h>
+#endif
+typedef struct _CtxTiled CtxTiled;
 
-static int
-ctx_conts_for_entry (CtxEntry *entry);
-static void
-ctx_iterator_init (CtxIterator      *iterator,
-                   CtxDrawlist  *drawlist,
-                   int               start_pos,
-                   int               flags);
-
-CtxCommand *
-ctx_iterator_next (CtxIterator *iterator);
-int ctx_iterator_pos (CtxIterator *iterator);
-
-static void ctx_drawlist_compact (CtxDrawlist *drawlist);
-static void
-ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size);
-static int
-ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry);
-int
-ctx_add_single (Ctx *ctx, void *entry);
-int ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry);
-int
-ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry);
-int ctx_append_drawlist (Ctx *ctx, void *data, int length);
-int ctx_set_drawlist (Ctx *ctx, void *data, int length);
-int ctx_get_drawlist_count (Ctx *ctx);
-const CtxEntry *ctx_get_drawlist (Ctx *ctx);
-int
-ctx_add_data (Ctx *ctx, void *data, int length);
+struct _CtxTiled
+{
+   void (*render)    (void *term, CtxCommand *command);
+   void (*reset)     (void *term);
+   void (*flush)     (void *term);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard) (void *ctxctx, const char *text);
+   void (*free)      (void *term);
+   Ctx          *ctx;
+   int           width;
+   int           height;
+   int           cols;
+   int           rows;
+   int           was_down;
+   uint8_t      *pixels;
+   Ctx          *ctx_copy;
+   Ctx          *host[CTX_MAX_THREADS];
+   CtxAntialias  antialias;
+   int           quit;
+#if CTX_TILED
+   _Atomic int   thread_quit;
+#endif
+   int           shown_frame;
+   int           render_frame;
+   int           rendered_frame[CTX_MAX_THREADS];
+   int           frame;
+   int       min_col; // hasher cols and rows
+   int       min_row;
+   int       max_col;
+   int       max_row;
+   uint8_t  hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
+   int8_t    tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
+                                                           // responsible for a tile
+                                                           //
 
-int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2]);
-int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length);
+   int           pointer_down[3];
 
-static CtxEntry
-ctx_void (CtxCode code);
-static CtxEntry
-ctx_f (CtxCode code, float x, float y);
-static CtxEntry
-ctx_u32 (CtxCode code, uint32_t x, uint32_t y);
-#if 0
-static CtxEntry
-ctx_s32 (CtxCode code, int32_t x, int32_t y);
+   CtxCursor     shown_cursor;
+#if CTX_TILED
+   cnd_t  cond;
+   mtx_t  mtx;
 #endif
+};
 
-CtxEntry
-ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1);
-static CtxEntry
-ctx_u8 (CtxCode code,
-        uint8_t a, uint8_t b, uint8_t c, uint8_t d,
-        uint8_t e, uint8_t f, uint8_t g, uint8_t h);
+void
+_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
+                                      CtxBuffer     *buffer);
 
-#define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = ctx_void (cmd); \
-  ctx_process (ctx, &command);}while(0) \
+#endif
 
-#define CTX_PROCESS_F(cmd, x, y) do {\
-  CtxEntry command = ctx_f(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
 
-#define CTX_PROCESS_F1(cmd, x) do {\
-  CtxEntry command = ctx_f(cmd, x, 0);\
-  ctx_process (ctx, &command);}while(0)
+#if CTX_IMPLEMENTATION
 
-#define CTX_PROCESS_U32(cmd, x, y) do {\
-  CtxEntry command = ctx_u32(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
+#define SHA1_IMPLEMENTATION
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis gmail com, http://libtom.org
+ *
+ * The plain ANSIC sha1 functionality has been extracted from libtomcrypt,
+ * and is included directly in the sources. /Øyvind K. - since libtomcrypt
+ * is public domain the adaptations done here to make the sha1 self contained
+ * also is public domain.
+ */
+#ifndef __SHA1_H
+#define __SHA1_H
+#include <inttypes.h>
 
-#define CTX_PROCESS_U8(cmd, x) do {\
-  CtxEntry command = ctx_u8(cmd, x,0,0,0,0,0,0,0);\
-  ctx_process (ctx, &command);}while(0)
 
+int ctx_sha1_init(CtxSHA1 * sha1);
+CtxSHA1 *ctx_sha1_new (void)
+{
+  CtxSHA1 *state = (CtxSHA1*)calloc (sizeof (CtxSHA1), 1);
+  ctx_sha1_init (state);
+  return state;
+}
+void ctx_sha1_free (CtxSHA1 *sha1)
+{
+  free (sha1);
+}
 
-#if CTX_BITPACK_PACKER
-static int
-ctx_last_history (CtxDrawlist *drawlist);
+#if 0
+          CtxSHA1 sha1;
+          ctx_sha1_init (&sha1);
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
 #endif
 
-#if CTX_BITPACK_PACKER
-static void
-ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos);
-
-static void
-ctx_drawlist_bitpack (CtxDrawlist *drawlist, int start_pos);
+#ifdef FF0
+#undef FF0
 #endif
-
-static void
-ctx_drawlist_compact (CtxDrawlist *drawlist);
-static void
-ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1);
-static void
-ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len);
-
+#ifdef FF1
+#undef FF1
 #endif
 
-#ifndef __CTX_UTIL_H
-#define __CTX_UTIL_H
+#ifdef SHA1_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h>
 
-inline static float ctx_fast_hypotf (float x, float y)
-{
-  if (x < 0) { x = -x; }
-  if (y < 0) { y = -y; }
-  if (x < y)
-    { return 0.96f * y + 0.4f * x; }
-  else
-    { return 0.96f * x + 0.4f * y; }
-}
+#define STORE64H(x,                                                             y)                           
                                          \
+   { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned                char)(((x)>>48)&255);     \
+     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned                char)(((x)>>32)&255);     \
+     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned                char)(((x)>>16)&255);     \
+     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
 
-static int ctx_str_is_number (const char *str)
-{
-  int got_digit = 0;
-  for (int i = 0; str[i]; i++)
-  {
-    if (str[i] >= '0' && str[i] <= '9')
-    {
-       got_digit ++;
-    }
-    else if (str[i] == '.')
-    {
-    }
-    else
-      return 0;
-  }
-  if (got_digit)
-    return 1;
-  return 0;
-}
+#define STORE32H(x,                                                             y)                           
                                          \
+     { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned              char)(((x)>>16)&255);   \
+       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned               char)((x)&255); }
 
-#if CTX_FONTS_FROM_FILE
-int
-_ctx_file_get_contents (const char     *path,
-                        unsigned char **contents,
-                        long           *length)
-{
-  FILE *file;
-  long  size;
-  long  remaining;
-  char *buffer;
-  file = fopen (path, "rb");
-  if (!file)
-    { return -1; }
-  fseek (file, 0, SEEK_END);
-  size = remaining = ftell (file);
-  if (length)
-    { *length =size; }
-  rewind (file);
-  buffer = (char*)malloc (size + 8);
-  if (!buffer)
-    {
-      fclose (file);
-      return -1;
-    }
-  remaining -= fread (buffer, 1, remaining, file);
-  if (remaining)
-    {
-      fclose (file);
-      free (buffer);
-      return -1;
-    }
-  fclose (file);
-  *contents = (unsigned char*) buffer;
-  buffer[size] = 0;
-  return 0;
-}
+#define LOAD32H(x, y)                            \
+     { x = ((unsigned long)((y)[0] & 255)<<24) | \
+           ((unsigned long)((y)[1] & 255)<<16) | \
+           ((unsigned long)((y)[2] & 255)<<8)  | \
+           ((unsigned long)((y)[3] & 255)); }
 
-void
-_ctx_file_set_contents (const char     *path,
-                        const unsigned char  *contents,
-                        long            length)
-{
-  FILE *file;
-  file = fopen (path, "wb");
-  if (!file)
-    { return; }
-  if (length < 0) length = strlen ((const char*)contents);
-  fwrite (contents, 1, length, file);
-  fclose (file);
-}
+/* rotates the hard way */
+#define ROL(x, y)  ((((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned 
long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROLc(x, y) ROL(x,y)
 
+#define CRYPT_OK     0
+#define CRYPT_ERROR  1
+#define CRYPT_NOP    2
 
+#ifndef MAX
+   #define MAX(x, y) ( ((x)>(y))?(x):(y) )
+#endif
+#ifndef MIN
+   #define MIN(x, y) ( ((x)<(y))?(x):(y) )
 #endif
 
+/* a simple macro for making hash "process" functions */
+#define HASH_PROCESS(func_name, compress_name, state_var, block_size)               \
+int func_name (CtxSHA1 *sha1, const unsigned char *in, unsigned long inlen)      \
+{                                                                                   \
+    unsigned long n;                                                                \
+    int           err;                                                              \
+    assert (sha1 != NULL);                                                          \
+    assert (in != NULL);                                                            \
+    if (sha1->curlen > sizeof(sha1->buf)) {                                         \
+       return -1;                                                                   \
+    }                                                                               \
+    while (inlen > 0) {                                                             \
+        if (sha1->curlen == 0 && inlen >= block_size) {                             \
+           if ((err = compress_name (sha1, (unsigned char *)in)) != CRYPT_OK) {     \
+              return err;                                                           \
+           }                                                                        \
+           sha1->length += block_size * 8;                                          \
+           in             += block_size;                                            \
+           inlen          -= block_size;                                            \
+        } else {                                                                    \
+           n = MIN(inlen, (block_size - sha1->curlen));                             \
+           memcpy(sha1->buf + sha1->curlen, in, (size_t)n);                         \
+           sha1->curlen += n;                                                       \
+           in             += n;                                                     \
+           inlen          -= n;                                                     \
+           if (sha1->curlen == block_size) {                                        \
+              if ((err = compress_name (sha1, sha1->buf)) != CRYPT_OK) {            \
+                 return err;                                                        \
+              }                                                                     \
+              sha1->length += 8*block_size;                                         \
+              sha1->curlen = 0;                                                     \
+           }                                                                        \
+       }                                                                            \
+    }                                                                               \
+    return CRYPT_OK;                                                                \
+}
 
-#endif
+/**********************/
 
+#define F0(x,y,z)  (z ^ (x & (y ^ z)))
+#define F1(x,y,z)  (x ^ y ^ z)
+#define F2(x,y,z)  ((x & y) | (z & (x | y)))
+#define F3(x,y,z)  (x ^ y ^ z)
 
-static int
-ctx_conts_for_entry (CtxEntry *entry)
+static int  ctx_sha1_compress(CtxSHA1 *sha1, unsigned char *buf)
 {
-    switch (entry->code)
-    {
-      case CTX_DATA:
-        return entry->data.u32[1];
-      case CTX_LINEAR_GRADIENT:
-        return 1;
-      case CTX_RADIAL_GRADIENT:
-      case CTX_ARC:
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_APPLY_TRANSFORM:
-      case CTX_COLOR:
-      case CTX_ROUND_RECTANGLE:
-      case CTX_SHADOW_COLOR:
-        return 2;
-      case CTX_RECTANGLE:
-      case CTX_VIEW_BOX:
-      case CTX_REL_QUAD_TO:
-      case CTX_QUAD_TO:
-      case CTX_TEXTURE:
-        return 1;
-      default:
-        return 0;
+    uint32_t a,b,c,d,e,W[80],i;
+
+    /* copy the state into 512-bits into W[0..15] */
+    for (i = 0; i < 16; i++) {
+        LOAD32H(W[i], buf + (4*i));
     }
-}
 
-// expanding arc_to to arc can be the job
-// of a layer in front of renderer?
-//   doing:
-//     rectangle
-//     arc
-//     ... etc reduction to beziers
-//     or even do the reduction to
-//     polylines directly here...
-//     making the rasterizer able to
-//     only do poly-lines? will that be faster?
+    /* copy state */
+    a = sha1->state[0];
+    b = sha1->state[1];
+    c = sha1->state[2];
+    d = sha1->state[3];
+    e = sha1->state[4];
 
-/* the iterator - should decode bitpacked data as well -
- * making the rasterizers simpler, possibly do unpacking
- * all the way to absolute coordinates.. unless mixed
- * relative/not are wanted.
- */
+    /* expand it */
+    for (i = 16; i < 80; i++) {
+        W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); 
+    }
 
+    /* compress */
+    /* round one */
+    #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
+    #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
+    #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
+    #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
+ 
+    for (i = 0; i < 20; ) {
+       FF0(a,b,c,d,e,i++);
+       FF0(e,a,b,c,d,i++);
+       FF0(d,e,a,b,c,i++);
+       FF0(c,d,e,a,b,i++);
+       FF0(b,c,d,e,a,i++);
+    }
 
-static void
-ctx_iterator_init (CtxIterator      *iterator,
-                   CtxDrawlist  *drawlist,
-                   int               start_pos,
-                   int               flags)
-{
-  iterator->drawlist   = drawlist;
-  iterator->flags          = flags;
-  iterator->bitpack_pos    = 0;
-  iterator->bitpack_length = 0;
-  iterator->pos            = start_pos;
-  iterator->end_pos        = drawlist->count;
-  iterator->first_run      = 1; // -1 is a marker used for first run
-  ctx_memset (iterator->bitpack_command, 0, sizeof (iterator->bitpack_command) );
-}
+    /* round two */
+    for (; i < 40; )  { 
+       FF1(a,b,c,d,e,i++);
+       FF1(e,a,b,c,d,i++);
+       FF1(d,e,a,b,c,i++);
+       FF1(c,d,e,a,b,i++);
+       FF1(b,c,d,e,a,i++);
+    }
 
-int ctx_iterator_pos (CtxIterator *iterator)
-{
-  return iterator->pos;
-}
+    /* round three */
+    for (; i < 60; )  { 
+       FF2(a,b,c,d,e,i++);
+       FF2(e,a,b,c,d,i++);
+       FF2(d,e,a,b,c,i++);
+       FF2(c,d,e,a,b,i++);
+       FF2(b,c,d,e,a,i++);
+    }
 
-static CtxEntry *_ctx_iterator_next (CtxIterator *iterator)
-{
-  int ret = iterator->pos;
-  CtxEntry *entry = &iterator->drawlist->entries[ret];
-  if (ret >= iterator->end_pos)
-    { return NULL; }
-  if (iterator->first_run == 0)
-    { iterator->pos += (ctx_conts_for_entry (entry) + 1); }
-  iterator->first_run = 0;
-  if (iterator->pos >= iterator->end_pos)
-    { return NULL; }
-  return &iterator->drawlist->entries[iterator->pos];
-}
+    /* round four */
+    for (; i < 80; )  { 
+       FF3(a,b,c,d,e,i++);
+       FF3(e,a,b,c,d,i++);
+       FF3(d,e,a,b,c,i++);
+       FF3(c,d,e,a,b,i++);
+       FF3(b,c,d,e,a,i++);
+    }
 
-// 6024x4008
-#if CTX_BITPACK
-static void
-ctx_iterator_expand_s8_args (CtxIterator *iterator, CtxEntry *entry)
-{
-  int no = 0;
-  for (int cno = 0; cno < 4; cno++)
-    for (int d = 0; d < 2; d++, no++)
-      iterator->bitpack_command[cno].data.f[d] =
-        entry->data.s8[no] * 1.0f / CTX_SUBDIV;
-  iterator->bitpack_command[0].code =
-    iterator->bitpack_command[1].code =
-      iterator->bitpack_command[2].code =
-        iterator->bitpack_command[3].code = CTX_CONT;
-  iterator->bitpack_length = 4;
-  iterator->bitpack_pos = 0;
+    #undef FF0
+    #undef FF1
+    #undef FF2
+    #undef FF3
+
+    /* store */
+    sha1->state[0] = sha1->state[0] + a;
+    sha1->state[1] = sha1->state[1] + b;
+    sha1->state[2] = sha1->state[2] + c;
+    sha1->state[3] = sha1->state[3] + d;
+    sha1->state[4] = sha1->state[4] + e;
+
+    return CRYPT_OK;
 }
 
-static void
-ctx_iterator_expand_s16_args (CtxIterator *iterator, CtxEntry *entry)
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+int ctx_sha1_init(CtxSHA1 * sha1)
 {
-  int no = 0;
-  for (int cno = 0; cno < 2; cno++)
-    for (int d = 0; d < 2; d++, no++)
-      iterator->bitpack_command[cno].data.f[d] = entry->data.s16[no] * 1.0f /
-          CTX_SUBDIV;
-  iterator->bitpack_command[0].code =
-    iterator->bitpack_command[1].code = CTX_CONT;
-  iterator->bitpack_length = 2;
-  iterator->bitpack_pos    = 0;
+   assert(sha1 != NULL);
+   sha1->state[0] = 0x67452301UL;
+   sha1->state[1] = 0xefcdab89UL;
+   sha1->state[2] = 0x98badcfeUL;
+   sha1->state[3] = 0x10325476UL;
+   sha1->state[4] = 0xc3d2e1f0UL;
+   sha1->curlen = 0;
+   sha1->length = 0;
+   return CRYPT_OK;
 }
-#endif
 
-CtxCommand *
-ctx_iterator_next (CtxIterator *iterator)
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+HASH_PROCESS(ctx_sha1_process, ctx_sha1_compress, sha1, 64)
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (20 bytes)
+   @return CRYPT_OK if successful
+*/
+int ctx_sha1_done(CtxSHA1 * sha1, unsigned char *out)
 {
-  CtxEntry *ret;
-#if CTX_BITPACK
-  int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK;
-again:
-  if (iterator->bitpack_length)
-    {
-      ret = &iterator->bitpack_command[iterator->bitpack_pos];
-      iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1);
-      if (iterator->bitpack_pos >= iterator->bitpack_length)
-        {
-          iterator->bitpack_length = 0;
+    int i;
+
+    assert(sha1 != NULL);
+    assert(out != NULL);
+
+    if (sha1->curlen >= sizeof(sha1->buf)) {
+       return -1;
+    }
+
+    /* increase the length of the message */
+    sha1->length += sha1->curlen * 8;
+
+    /* append the '1' bit */
+    sha1->buf[sha1->curlen++] = (unsigned char)0x80;
+
+    /* if the length is currently above 56 bytes we append zeros
+     * then compress.  Then we can fall back to padding zeros and length
+     * encoding like normal.
+     */
+    if (sha1->curlen > 56) {
+        while (sha1->curlen < 64) {
+            sha1->buf[sha1->curlen++] = (unsigned char)0;
         }
-      return (CtxCommand *) ret;
+        ctx_sha1_compress(sha1, sha1->buf);
+        sha1->curlen = 0;
+    }
+
+    /* pad upto 56 bytes of zeroes */
+    while (sha1->curlen < 56) {
+        sha1->buf[sha1->curlen++] = (unsigned char)0;
+    }
+
+    /* store length */
+    STORE64H(sha1->length, sha1->buf+56);
+    ctx_sha1_compress(sha1, sha1->buf);
+
+    /* copy output */
+    for (i = 0; i < 5; i++) {
+        STORE32H(sha1->state[i], out+(4*i));
     }
+    return CRYPT_OK;
+}
 #endif
-  ret = _ctx_iterator_next (iterator);
-#if CTX_BITPACK
-  if (ret && expand_bitpack)
-    switch ((CtxCode)(ret->code))
-      {
-        case CTX_REL_CURVE_TO_REL_LINE_TO:
-          ctx_iterator_expand_s8_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_REL_CURVE_TO;
-          iterator->bitpack_command[1].code =
-          iterator->bitpack_command[2].code = CTX_CONT;
-          iterator->bitpack_command[3].code = CTX_REL_LINE_TO;
-          // 0.0 here is a common optimization - so check for it
-          if (ret->data.s8[6]== 0 && ret->data.s8[7] == 0)
-            { iterator->bitpack_length = 3; }
-          else
-            iterator->bitpack_length          = 4;
-          goto again;
-        case CTX_REL_LINE_TO_REL_CURVE_TO:
-          ctx_iterator_expand_s8_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_REL_LINE_TO;
-          iterator->bitpack_command[1].code = CTX_REL_CURVE_TO;
-          iterator->bitpack_length          = 2;
-          goto again;
-        case CTX_REL_CURVE_TO_REL_MOVE_TO:
-          ctx_iterator_expand_s8_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_REL_CURVE_TO;
-          iterator->bitpack_command[3].code = CTX_REL_MOVE_TO;
-          iterator->bitpack_length          = 4;
-          goto again;
-        case CTX_REL_LINE_TO_X4:
-          ctx_iterator_expand_s8_args (iterator, ret);
-          iterator->bitpack_command[0].code =
-          iterator->bitpack_command[1].code =
-          iterator->bitpack_command[2].code =
-          iterator->bitpack_command[3].code = CTX_REL_LINE_TO;
-          iterator->bitpack_length          = 4;
-          goto again;
-        case CTX_REL_QUAD_TO_S16:
-          ctx_iterator_expand_s16_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_REL_QUAD_TO;
-          iterator->bitpack_length          = 1;
-          goto again;
-        case CTX_REL_QUAD_TO_REL_QUAD_TO:
-          ctx_iterator_expand_s8_args (iterator, ret);
-          iterator->bitpack_command[0].code =
-          iterator->bitpack_command[2].code = CTX_REL_QUAD_TO;
-          iterator->bitpack_length          = 3;
-          goto again;
-        case CTX_REL_LINE_TO_X2:
-          ctx_iterator_expand_s16_args (iterator, ret);
-          iterator->bitpack_command[0].code =
-          iterator->bitpack_command[1].code = CTX_REL_LINE_TO;
-          iterator->bitpack_length          = 2;
-          goto again;
-        case CTX_REL_LINE_TO_REL_MOVE_TO:
-          ctx_iterator_expand_s16_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_REL_LINE_TO;
-          iterator->bitpack_command[1].code = CTX_REL_MOVE_TO;
-          iterator->bitpack_length          = 2;
-          goto again;
-        case CTX_MOVE_TO_REL_LINE_TO:
-          ctx_iterator_expand_s16_args (iterator, ret);
-          iterator->bitpack_command[0].code = CTX_MOVE_TO;
-          iterator->bitpack_command[1].code = CTX_REL_MOVE_TO;
-          iterator->bitpack_length          = 2;
-          goto again;
-        case CTX_FILL_MOVE_TO:
-          iterator->bitpack_command[1]      = *ret;
-          iterator->bitpack_command[0].code = CTX_FILL;
-          iterator->bitpack_command[1].code = CTX_MOVE_TO;
-          iterator->bitpack_pos             = 0;
-          iterator->bitpack_length          = 2;
-          goto again;
-        case CTX_LINEAR_GRADIENT:
-        case CTX_QUAD_TO:
-        case CTX_REL_QUAD_TO:
-        case CTX_TEXTURE:
-        case CTX_RECTANGLE:
-        case CTX_VIEW_BOX:
-        case CTX_ARC:
-        case CTX_ARC_TO:
-        case CTX_REL_ARC_TO:
-        case CTX_COLOR:
-        case CTX_SHADOW_COLOR:
-        case CTX_RADIAL_GRADIENT:
-        case CTX_CURVE_TO:
-        case CTX_REL_CURVE_TO:
-        case CTX_APPLY_TRANSFORM:
-        case CTX_ROUND_RECTANGLE:
-        case CTX_TEXT:
-        case CTX_TEXT_STROKE:
-        case CTX_FONT:
-        case CTX_LINE_DASH:
-        case CTX_FILL:
-        case CTX_NOP:
-        case CTX_MOVE_TO:
-        case CTX_LINE_TO:
-        case CTX_REL_MOVE_TO:
-        case CTX_REL_LINE_TO:
-        case CTX_VER_LINE_TO:
-        case CTX_REL_VER_LINE_TO:
-        case CTX_HOR_LINE_TO:
-        case CTX_REL_HOR_LINE_TO:
-        case CTX_ROTATE:
-        case CTX_FLUSH:
-        case CTX_TEXT_ALIGN:
-        case CTX_TEXT_BASELINE:
-        case CTX_TEXT_DIRECTION:
-        case CTX_MITER_LIMIT:
-        case CTX_GLOBAL_ALPHA:
-        case CTX_COMPOSITING_MODE:
-        case CTX_BLEND_MODE:
-        case CTX_SHADOW_BLUR:
-        case CTX_SHADOW_OFFSET_X:
-        case CTX_SHADOW_OFFSET_Y:
-        case CTX_RESET:
-        case CTX_EXIT:
-        case CTX_BEGIN_PATH:
-        case CTX_CLOSE_PATH:
-        case CTX_SAVE:
-        case CTX_CLIP:
-        case CTX_PRESERVE:
-        case CTX_DEFINE_GLYPH:
-        case CTX_IDENTITY:
-        case CTX_FONT_SIZE:
-        case CTX_START_GROUP:
-        case CTX_END_GROUP:
-        case CTX_RESTORE:
-        case CTX_LINE_WIDTH:
-        case CTX_STROKE:
-        case CTX_KERNING_PAIR:
-        case CTX_SCALE:
-        case CTX_GLYPH:
-        case CTX_SET_PIXEL:
-        case CTX_FILL_RULE:
-        case CTX_LINE_CAP:
-        case CTX_LINE_JOIN:
-        case CTX_NEW_PAGE:
-        case CTX_SET_KEY:
-        case CTX_TRANSLATE:
-        case CTX_GRADIENT_STOP:
-        case CTX_CONT: // shouldnt happen
-          iterator->bitpack_length = 0;
-          return (CtxCommand *) ret;
-#if 1
-        default: // XXX remove - and get better warnings
-          iterator->bitpack_command[0] = ret[0];
-          iterator->bitpack_command[1] = ret[1];
-          iterator->bitpack_command[2] = ret[2];
-          iterator->bitpack_command[3] = ret[3];
-          iterator->bitpack_command[4] = ret[4];
-          iterator->bitpack_pos = 0;
-          iterator->bitpack_length = 1;
-          goto again;
+
 #endif
-      }
 #endif
-  return (CtxCommand *) ret;
+
+#if CTX_IMPLEMENTATION
+#ifndef THASH_H
+#define THASH_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#define THASH_NO_INTERNING   // ctx doesn't make use of thash_decode
+
+#define THASH_ENTER_DIRECT     16
+
+#define THASH_SPACE            0
+#define THASH_DEC_OFFSET       29
+#define THASH_INC_OFFSET       30
+#define THASH_ENTER_UTF5       31
+#define THASH_START_OFFSET     'l'
+#define THASH_JUMP_OFFSET      27
+#define THASH_MAXLEN           10
+
+// todo: better whitespace handling for double version
+
+
+static inline int thash_new_offset (uint32_t unichar)
+{
+   int offset = unichar % 32;
+   return unichar - offset + 14; // this gives ~85% compression on test corpus
+   return unichar; // this gives 88% compression on test corpus
 }
 
-static void ctx_drawlist_compact (CtxDrawlist *drawlist);
-static void
-ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
+static int thash_is_in_range (uint32_t offset, uint32_t unichar)
 {
-#if CTX_DRAWLIST_STATIC
-  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
+  if (unichar == 32)
+    return 1;
+  if (offset - unichar <= 13 ||
+      unichar - offset <= 14)
+      return 1;
+  return 0;
+}
+
+static int thash_is_in_jump_range_dec (uint32_t offset, uint32_t unichar)
+{
+  return thash_is_in_range (offset - THASH_JUMP_OFFSET, unichar);
+}
+
+static int thash_is_in_jump_range_inc (uint32_t offset, uint32_t unichar)
+{
+  return thash_is_in_range (offset + THASH_JUMP_OFFSET, unichar);
+}
+
+uint32_t ctx_utf8_to_unichar (const char *input);
+int ctx_unichar_to_utf8      (uint32_t ch, uint8_t  *dest);
+int ctx_utf8_len             (const unsigned char first_byte);
+
+static int thash_utf5_length (uint32_t unichar)
+{
+  int octets = 0;
+  if (unichar == 0) return 1;
+  while (unichar)
+  {  octets ++;
+     unichar /= 16;
+  }
+  return octets;
+}
+
+typedef struct EncodeUtf5 {
+  int      is_utf5;
+  int      offset;
+  int      length;
+  void    *write_data;
+  uint32_t current;
+} EncodeUtf5;
+
+void thash_encode_utf5 (const char *input, int inlen,
+                   char *output, int *r_outlen)
+{
+  uint32_t offset = THASH_START_OFFSET;
+
+  int is_utf5 = 1;
+  int len = 0;
+
+  for (int i = 0; i < inlen; i+= ctx_utf8_len (input[i]))
+  {
+    int val = ctx_utf8_to_unichar(&input[i]);
+    int next_val = ' '; // always in range
+    int next_next_val = ' ';
+    int first_len = ctx_utf8_len (input[i]);
+    if (i + first_len < inlen)
     {
-      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
-      drawlist->entries = &sbuf[0];
-      drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
+        int next_len = ctx_utf8_to_unichar (&input[i + first_len]);
+        if (i + first_len + next_len < inlen)
+        {
+          next_next_val = ctx_utf8_to_unichar (&input[i + first_len + next_len]);
+        }
     }
-  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
+
+    if (is_utf5)
     {
-      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
-      drawlist->entries = &sbuf[0];
-      drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
-    }
-  else
+      int in_range = 
+          thash_is_in_range (offset, val) +
+          thash_is_in_range (offset, next_val) +
+          thash_is_in_range (offset, next_next_val);
+      int change_cost = 4;
+      int no_change_cost = thash_utf5_length (val) + thash_utf5_length (next_val)
+                                                   + thash_utf5_length (next_next_val);
+
+      if (in_range > 2 && change_cost < no_change_cost)
+      {
+        output[len++] = THASH_ENTER_DIRECT;
+        is_utf5 = 0;
+      }
+    } else
     {
-      static CtxEntry sbuf[CTX_MAX_JOURNAL_SIZE];
-      drawlist->entries = &sbuf[0];
-      drawlist->size = CTX_MAX_JOURNAL_SIZE;
-      ctx_drawlist_compact (drawlist);
-    }
-#else
-  int new_size = desired_size;
-  int min_size = CTX_MIN_JOURNAL_SIZE;
-  int max_size = CTX_MAX_JOURNAL_SIZE;
-  if ((drawlist->flags & CTX_DRAWLIST_EDGE_LIST))
-    {
-      min_size = CTX_MIN_EDGE_LIST_SIZE;
-      max_size = CTX_MAX_EDGE_LIST_SIZE;
-    }
-  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
-    {
-      min_size = CTX_MIN_EDGE_LIST_SIZE;
-      max_size = CTX_MAX_EDGE_LIST_SIZE;
-    }
-  else
-    {
-      ctx_drawlist_compact (drawlist);
+      if (!thash_is_in_range(offset, val))
+      {
+        if (thash_is_in_jump_range_dec (offset, val))
+        {
+            output[len++] = THASH_DEC_OFFSET;
+            offset -= THASH_JUMP_OFFSET;
+        }
+        else if (thash_is_in_jump_range_inc (offset, val))
+        {
+          output[len++] = THASH_INC_OFFSET;
+          offset += THASH_JUMP_OFFSET;
+        }
+        else
+        {
+          output[len++] = THASH_ENTER_UTF5;
+          is_utf5 = 1;
+        }
+      }
     }
 
-  if (new_size < drawlist->size)
-    { return; }
-  if (drawlist->size == max_size)
-    { return; }
-  if (new_size < min_size)
-    { new_size = min_size; }
-  if (new_size < drawlist->count)
-    { new_size = drawlist->count + 4; }
-  if (new_size >= max_size)
-    { new_size = max_size; }
-  if (new_size != drawlist->size)
+    if (is_utf5)
     {
-      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, drawlist->flags, new_size, 
drawlist->size);
-  if (drawlist->entries)
+          int octets = 0;
+          offset = thash_new_offset (val);
+          while (val)
+          {
+            int oval = val % 16;
+            int last = 0;
+            if (val / 32 == 0) last = 16;
+            output[len+ (octets++)] = oval + last;
+            val /= 16;
+          }
+          for (int j = 0; j < octets/2; j++) // mirror in-place
+          {
+            int tmp = output[len+j];
+            output[len+j] = output[len+octets-1-j];
+            output[len+octets-1-j] = tmp;
+          }
+          len += octets;
+        }
+        else 
+      {
+        if (val == 32)
+        {
+          output[len++] = THASH_SPACE;
+        }
+        else
+        {
+          output[len++]= val-offset+14;
+        }
+      }
+  }
+  if (len && output[len-1]==0)
+    output[len++] = 16;
+  output[len]=0;
+  *r_outlen = len;
+}
+
+uint64_t _thash (const char *utf8)
+{
+  char encoded[4096]="";
+  int  encoded_len=0;
+  int  wordlen = 0;
+  thash_encode_utf5 (utf8, strlen (utf8), encoded, &encoded_len);
+#if 0
+  Word word = {0};
+  word.utf5 = (encoded[0] != THASH_ENTER_DIRECT);
+  for (int i = !word.utf5; i < encoded_len; i++)
+    word_set_val (&word, wordlen++, encoded[i]);
+  return word.hash;
+#else
+  uint64_t hash = 0;
+  int  utf5 = (encoded[0] != THASH_ENTER_DIRECT);
+  for (int i = !utf5; i < encoded_len; i++)
+  {
+    uint64_t val = encoded[i];
+
+    if (wordlen < THASH_MAXLEN)
     {
-      //printf ("grow %p to %d from %d\n", drawlist, new_size, drawlist->size);
-      CtxEntry *ne =  (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
-      memcpy (ne, drawlist->entries, drawlist->size * sizeof (CtxEntry) );
-      free (drawlist->entries);
-      drawlist->entries = ne;
-      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, sizeof (CtxEntry) * new_size);
+      hash = hash | (val << (5*wordlen));
+      hash &= (((uint64_t)1<<52)-1);
     }
-  else
+    else
     {
-      //printf ("allocating for %p %d\n", drawlist, new_size);
-      drawlist->entries = (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
+      hash = hash ^ ((hash << 4) + val);
+      hash &= (((uint64_t)1<<52)-1);
     }
-  drawlist->size = new_size;
-    }
-  //fprintf (stderr, "drawlist %p is %d\n", drawlist, drawlist->size);
+    wordlen++;
+  }
+  hash <<= 1;
+  if (wordlen >= THASH_MAXLEN) 
+    hash |= ((uint64_t)1<<51); // overflowed
+  return hash |  utf5;
 #endif
 }
 
-static int
-ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
+typedef struct _Interned Interned;
+
+struct _Interned {
+    uint64_t   hash;
+    char      *string;
+};
+
+static Interned *interned = NULL;
+static int n_interned = 0;
+static int s_interned = 0;
+static int interned_sorted = 1;
+
+static int interned_compare (const void *a, const void *b)
 {
-  int max_size = CTX_MAX_JOURNAL_SIZE;
-  int ret = drawlist->count;
-  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
-    {
-      max_size = CTX_MAX_EDGE_LIST_SIZE;
-    }
-  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
-    {
-      max_size = CTX_MAX_EDGE_LIST_SIZE;
-    }
-  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
+  const Interned *ia = a;
+  const Interned *ib = b;
+  if (ia->hash < ib->hash ) return -1;
+  else if (ia->hash > ib->hash ) return 1;
+  return 0;
+}
+
+
+uint64_t thash (const char *utf8)
+{
+  uint64_t hash = _thash (utf8);
+#ifdef THASH_NO_INTERNING
+  return hash;
+#endif
+  if (hash & ((uint64_t)1<<51)) /* overflowed */
+  {
+    int i;
+    for (i = 0; i < n_interned; i++)
     {
-      return ret;
+      Interned *entry = &interned[i];
+      if (entry->hash == hash)
+        return hash;
     }
-  if (ret + 8 >= drawlist->size - 20)
+
+    if (n_interned + 1 >= s_interned)
     {
-      int new_ = CTX_MAX (drawlist->size * 2, ret + 8);
-      ctx_drawlist_resize (drawlist, new_);
+       s_interned = (s_interned + 128)*1.5;
+       //fprintf (stderr, "\r%p %i ", interned, s_interned);
+       interned = realloc (interned, s_interned * sizeof (Interned));
     }
 
-  if (drawlist->count >= max_size - 20)
     {
-      return 0;
+      Interned *entry = &interned[n_interned];
+      entry->hash = hash;
+      entry->string = strdup (utf8);
     }
-  drawlist->entries[drawlist->count] = *entry;
-  ret = drawlist->count;
-  drawlist->count++;
-  return ret;
+    n_interned++;
+    interned_sorted = 0;
+  }
+  return hash;
 }
+uint64_t ctx_strhash(const char *str, int ignored) { return thash (str);}
 
+typedef struct ThashUtf5Dec {
+  int      is_utf5;
+  int      offset;
+  void    *write_data;
+  uint32_t current;
+  void   (*append_unichar) (uint32_t unichar, void *write_data);
+} ThashUtf5Dec;
 
+typedef struct ThashUtf5DecDefaultData {
+   uint8_t *buf;
+   int     length;
+} ThashUtf5DecDefaultData;
 
+static void thash_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *write_data)
+{
+  ThashUtf5DecDefaultData *data = write_data;
+  unsigned char utf8[8]="";
+  utf8[ctx_unichar_to_utf8 (unichar, utf8)]=0;
+  for (int j = 0; utf8[j]; j++)
+    data->buf[data->length++]=utf8[j];
+  data->buf[data->length]=0;
+}
 
-int
-ctx_add_single (Ctx *ctx, void *entry)
+void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
 {
-  return ctx_drawlist_add_single (&ctx->drawlist, (CtxEntry *) entry);
+  if (dec->is_utf5)
+  {
+      if (in > 16)
+      {
+        if (dec->current)
+        {
+          dec->offset = thash_new_offset (dec->current);
+          dec->append_unichar (dec->current, dec->write_data);
+          dec->current = 0;
+        }
+      }
+      if (in == THASH_ENTER_DIRECT)
+      {
+        if (dec->current)
+        {
+          dec->offset = thash_new_offset (dec->current);
+          dec->append_unichar (dec->current, dec->write_data);
+          dec->current = 0;
+        }
+        dec->is_utf5 = 0;
+      }
+      else
+      {
+        dec->current = dec->current * 16 + (in % 16);
+      }
+  }
+  else
+  {
+      switch (in)
+      {
+        case THASH_ENTER_UTF5: dec->is_utf5 = 1;  break;
+        case THASH_SPACE:      dec->append_unichar (' ', dec->write_data); break;
+        case THASH_DEC_OFFSET: dec->offset -= THASH_JUMP_OFFSET; break;
+        case THASH_INC_OFFSET: dec->offset += THASH_JUMP_OFFSET; break;
+        default:
+          dec->append_unichar (dec->offset + in - 14, dec->write_data);
+      }
+  }
 }
 
-int
-ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry)
+void thash_decode_utf5_bytes (int is_utf5, 
+                        const unsigned char *input, int inlen,
+                        char *output, int *r_outlen)
 {
-  int length = ctx_conts_for_entry (entry) + 1;
-  int ret = 0;
-  for (int i = 0; i < length; i ++)
+  ThashUtf5DecDefaultData append_data= {(unsigned char*)output, };
+  ThashUtf5Dec dec = {is_utf5,
+                    THASH_START_OFFSET,
+                    &append_data,
+  0, thash_decode_utf5_append_unichar_as_utf8
+  };
+  for (int i = 0; i < inlen; i++)
+  {
+    thash_decode_utf5 (&dec, input[i]);
+  }
+  if (dec.current)
+    dec.append_unichar (dec.current, dec.write_data);
+  if (r_outlen)*r_outlen = append_data.length;
+}
+
+const char *thash_decode (uint64_t hash)
+{
+  if (!interned_sorted && interned)
+  {
+    qsort (interned, n_interned, sizeof (Interned), interned_compare);
+    interned_sorted = 1;
+  }
+  if (hash &  ((uint64_t)1<<51))
+  {
+
+    for (int i = 0; i < n_interned; i++)
     {
-      ret = ctx_drawlist_add_single (drawlist, &entry[i]);
+      Interned *entry = &interned[i];
+      if (entry->hash == hash)
+        return entry->string;
     }
+    return "[missing string]";
+  }
+
+  static char ret[4096]="";
+  uint8_t utf5[40]="";
+  uint64_t tmp = hash & (((uint64_t)1<<51)-1);
+  int len = 0;
+  int is_utf5 = tmp & 1;
+  tmp /= 2;
+  int in_utf5 = is_utf5;
+  while (tmp > 0)
+  {
+    uint64_t remnant = tmp % 32;
+    uint64_t val = remnant;
+
+    if      ( in_utf5 && val == THASH_ENTER_DIRECT) in_utf5 = 0;
+    else if (!in_utf5 && val == THASH_ENTER_UTF5) in_utf5 = 1;
+
+    utf5[len++] = val;
+    tmp -= remnant;
+    tmp /= 32;
+  }
+  if (in_utf5 && len && utf5[len-1] > 'G')
+  {
+    utf5[len++] = 0;//utf5_alphabet[0]; 
+  }
+  utf5[len]=0;
+  int retlen = sizeof (ret);
+  thash_decode_utf5_bytes (is_utf5, utf5, len, ret, &retlen);
+  ret[len]=0;
   return ret;
 }
 
 #if 0
-int
-ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry)
+
+#include <assert.h>
+#pragma pack(push,1)
+typedef union Word
+{
+  uint64_t hash;
+  struct {
+    unsigned int utf5:1;
+    unsigned int c0:5;
+    unsigned int c1:5;
+    unsigned int c2:5;
+    unsigned int c3:5;
+    unsigned int c4:5;
+    unsigned int c5:5;
+    unsigned int c6:5;
+    unsigned int c7:5;
+    unsigned int c8:5;
+    unsigned int c9:5;
+    unsigned int overflowed:1;
+  };
+} Word;
+
+static inline void word_set_val (Word *word, int no, int val)
 {
-  int length = ctx_conts_for_entry (entry) + 1;
-  int tmp_pos = ctx_drawlist_add_entry (drawlist, entry);
-  for (int i = 0; i < length; i++)
-  {
-    for (int j = pos + i + 1; j < tmp_pos; j++)
-      drawlist->entries[j] = entry[j-1];
-    drawlist->entries[pos + i] = entry[i];
+#if 0
+  switch(no)
+  {
+     case 0: word->c0 = val; break;
+     case 1: word->c1 = val; break;
+     case 2: word->c2 = val; break;
+     case 3: word->c3 = val; break;
+     case 4: word->c4 = val; break;
+     case 5: word->c5 = val; break;
+     case 6: word->c6 = val; break;
+     case 7: word->c7 = val; break;
+     case 8: word->c8 = val; break;
+     case 9: word->c9 = val; break;
+     default:
+       // for overflow only works when setting all in sequence
+       word->hash = word->hash + ((uint64_t)(val) << (5*no+1));
+       word->overflowed = 1;
+       break;
   }
-  return pos;
-}
+#else
+  word->hash = word->hash | ((uint64_t)(val) << (5*no+1));
+  if (no >= 9) 
+    word->hash |= ((uint64_t)1<<51);
+  word->hash &= (((uint64_t)1<<52)-1);
 #endif
-int
-ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry)
+}
+
+static inline int word_get_val (Word *word, int no)
 {
-  int length = ctx_conts_for_entry (entry) + 1;
-  int tmp_pos = ctx_drawlist_add_entry (drawlist, entry);
-#if 1
-  for (int i = 0; i < length; i++)
+  switch(no)
   {
-    for (int j = tmp_pos; j > pos + i; j--)
-      drawlist->entries[j] = drawlist->entries[j-1];
-    drawlist->entries[pos + i] = entry[i];
+     case 0: return word->c0;break;
+     case 1: return word->c1;break;
+     case 2: return word->c2;break;
+     case 3: return word->c3;break;
+     case 4: return word->c4;break;
+     case 5: return word->c5;break;
+     case 6: return word->c6;break;
+     case 7: return word->c7;break;
+     case 8: return word->c8;break;
+     case 9: return word->c9;break;
   }
-  return pos;
-#endif
-  return tmp_pos;
 }
 
-int ctx_append_drawlist (Ctx *ctx, void *data, int length)
+static inline int word_get_length (Word *word)
 {
-  CtxEntry *entries = (CtxEntry *) data;
-  if (length % sizeof (CtxEntry) )
-    {
-      ctx_log("drawlist not multiple of 9\n");
-      return -1;
-    }
-  for (unsigned int i = 0; i < length / sizeof (CtxEntry); i++)
-    {
-      ctx_drawlist_add_single (&ctx->drawlist, &entries[i]);
-    }
-  return 0;
+   int len = 0;
+   if (word->c0) len ++; else return len;
+   if (word->c1) len ++; else return len;
+   if (word->c2) len ++; else return len;
+   if (word->c3) len ++; else return len;
+   if (word->c4) len ++; else return len;
+   if (word->c5) len ++; else return len;
+   if (word->c6) len ++; else return len;
+   if (word->c7) len ++; else return len;
+   if (word->c8) len ++; else return len;
+   if (word->c9) len ++; else return len;
+   return len;
 }
 
-int ctx_set_drawlist (Ctx *ctx, void *data, int length)
+
+static Word *word_append_unichar (Word *word, uint32_t unichar)
 {
-  CtxDrawlist *drawlist = &ctx->drawlist;
-  ctx->drawlist.count = 0;
-  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
+  //word_set_char (word, word_get_length (word), unichar);
+  // append unichar - possibly advancing.
+  return word;
+}
+#endif
+
+#endif
+/* atty - audio interface and driver for terminals
+ * Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>. 
+ */
+
+static const char *base64_map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+static void bin2base64_group (const unsigned char *in, int remaining, char *out)
+{
+  unsigned char digit[4] = {0,0,64,64};
+  int i;
+  digit[0] = in[0] >> 2;
+  digit[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4);
+  if (remaining > 1)
     {
-      return -1;
+      digit[2] = ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6);
+      if (remaining > 2)
+        digit[3] = ((in[2] & 0x3f));
     }
-  if (length % 9) return -1;
-  ctx_drawlist_resize (drawlist, length/9);
-  memcpy (drawlist->entries, data, length);
-  drawlist->count = length / 9;
-  return length;
+  for (i = 0; i < 4; i++)
+    out[i] = base64_map[digit[i]];
 }
 
-
-int ctx_get_drawlist_count (Ctx *ctx)
+void
+ctx_bin2base64 (const void *bin,
+                int         bin_length,
+                char       *ascii)
 {
-  return ctx->drawlist.count;
+  /* this allocation is a hack to ensure we always produce the same result,
+   * regardless of padding data accidentally taken into account.
+   */
+  unsigned char *bin2 = calloc (bin_length + 4, 1);
+  unsigned const char *p = bin2;
+  int i;
+  memcpy (bin2, bin, bin_length);
+  for (i=0; i*3 < bin_length; i++)
+   {
+     int remaining = bin_length - i*3;
+     bin2base64_group (&p[i*3], remaining, &ascii[i*4]);
+   }
+  free (bin2);
+  ascii[i*4]=0;
 }
 
-const CtxEntry *ctx_get_drawlist (Ctx *ctx)
+static unsigned char base64_revmap[255];
+static void base64_revmap_init (void)
 {
-  return ctx->drawlist.entries;
+  static int done = 0;
+  if (done)
+    return;
+
+  for (int i = 0; i < 255; i ++)
+    base64_revmap[i]=255;
+  for (int i = 0; i < 64; i ++)
+    base64_revmap[((const unsigned char*)base64_map)[i]]=i;
+  /* include variants used in URI encodings for decoder,
+   * even if that is not how we encode
+  */
+  base64_revmap['-']=62;
+  base64_revmap['_']=63;
+  base64_revmap['+']=62;
+  base64_revmap['/']=63;
+
+  done = 1;
 }
 
+
 int
-ctx_add_data (Ctx *ctx, void *data, int length)
+ctx_base642bin (const char    *ascii,
+                int           *length,
+                unsigned char *bin)
 {
-  if (length % sizeof (CtxEntry) )
-    {
-      //ctx_log("err\n");
-      return -1;
+  int i;
+  int charno = 0;
+  int outputno = 0;
+  int carry = 0;
+  base64_revmap_init ();
+  for (i = 0; ascii[i]; i++)
+    {
+      int bits = base64_revmap[((const unsigned char*)ascii)[i]];
+      if (length && outputno > *length)
+        {
+          *length = -1;
+          return -1;
+        }
+      if (bits != 255)
+        {
+          switch (charno % 4)
+            {
+              case 0:
+                carry = bits;
+                break;
+              case 1:
+                bin[outputno] = (carry << 2) | (bits >> 4);
+                outputno++;
+                carry = bits & 15;
+                break;
+              case 2:
+                bin[outputno] = (carry << 4) | (bits >> 2);
+                outputno++;
+                carry = bits & 3;
+                break;
+              case 3:
+                bin[outputno] = (carry << 6) | bits;
+                outputno++;
+                carry = 0;
+                break;
+            }
+          charno++;
+        }
     }
-  /* some more input verification might be in order.. like
-   * verify that it is well-formed up to length?
-   *
-   * also - it would be very useful to stop processing
-   * upon flush - and do drawlist resizing.
-   */
-  return ctx_drawlist_add_entry (&ctx->drawlist, (CtxEntry *) data);
+  bin[outputno]=0;
+  if (length)
+    *length= outputno;
+  return outputno;
 }
+#ifndef CTX_DRAWLIST_H
+#define CTX_DRAWLIST_H
 
-int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2])
-{
-  CtxEntry entry = {code, {{0},}};
-  entry.data.u32[0] = u32[0];
-  entry.data.u32[1] = u32[1];
-  return ctx_drawlist_add_single (drawlist, &entry);
-}
+static int
+ctx_conts_for_entry (CtxEntry *entry);
+static void
+ctx_iterator_init (CtxIterator      *iterator,
+                   CtxDrawlist  *drawlist,
+                   int               start_pos,
+                   int               flags);
 
-int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
-{
-  CtxEntry entry = {CTX_DATA, {{0},}};
-  entry.data.u32[0] = 0;
-  entry.data.u32[1] = 0;
-  int ret = ctx_drawlist_add_single (drawlist, &entry);
-  if (!data) { return -1; }
-  int length_in_blocks;
-  if (length <= 0) { length = strlen ( (char *) data) + 1; }
-  length_in_blocks = length / sizeof (CtxEntry);
-  length_in_blocks += (length % sizeof (CtxEntry) ) ?1:0;
-  if (drawlist->count + length_in_blocks + 4 > drawlist->size)
-    { ctx_drawlist_resize (drawlist, drawlist->count * 1.2 + length_in_blocks + 32); }
-  if (drawlist->count >= drawlist->size)
-    { return -1; }
-  drawlist->count += length_in_blocks;
-  drawlist->entries[ret].data.u32[0] = length;
-  drawlist->entries[ret].data.u32[1] = length_in_blocks;
-  memcpy (&drawlist->entries[ret+1], data, length);
-  {
-    //int reverse = ctx_drawlist_add (drawlist, CTX_DATA_REV);
-    CtxEntry entry = {CTX_DATA_REV, {{0},}};
-    entry.data.u32[0] = length;
-    entry.data.u32[1] = length_in_blocks;
-    ctx_drawlist_add_single (drawlist, &entry);
-    /* this reverse marker exist to enable more efficient
-       front to back traversal, can be ignored in other
-       direction, is this needed after string setters as well?
-     */
-  }
-  return ret;
-}
+CtxCommand *
+ctx_iterator_next (CtxIterator *iterator);
+int ctx_iterator_pos (CtxIterator *iterator);
 
-static CtxEntry
-ctx_void (CtxCode code)
-{
-  CtxEntry command;
-  command.code = code;
-  command.data.u32[0] = 0;
-  command.data.u32[1] = 0;
-  return command;
-}
+static void ctx_drawlist_compact (CtxDrawlist *drawlist);
+static void
+ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size);
+static int
+ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry);
+int
+ctx_add_single (Ctx *ctx, void *entry);
+int ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry);
+int
+ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry);
+int ctx_append_drawlist (Ctx *ctx, void *data, int length);
+int ctx_set_drawlist (Ctx *ctx, void *data, int length);
+int ctx_get_drawlist_count (Ctx *ctx);
+const CtxEntry *ctx_get_drawlist (Ctx *ctx);
+int
+ctx_add_data (Ctx *ctx, void *data, int length);
 
-static CtxEntry
-ctx_f (CtxCode code, float x, float y)
-{
-  CtxEntry command = ctx_void (code);
-  command.data.f[0] = x;
-  command.data.f[1] = y;
-  return command;
-}
+int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2]);
+int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length);
 
 static CtxEntry
-ctx_u32 (CtxCode code, uint32_t x, uint32_t y)
-{
-  CtxEntry command = ctx_void (code);
-  command.data.u32[0] = x;
-  command.data.u32[1] = y;
-  return command;
-}
-
+ctx_void (CtxCode code);
+static CtxEntry
+ctx_f (CtxCode code, float x, float y);
+static CtxEntry
+ctx_u32 (CtxCode code, uint32_t x, uint32_t y);
 #if 0
 static CtxEntry
-ctx_s32 (CtxCode code, int32_t x, int32_t y)
-{
-  CtxEntry command = ctx_void (code);
-  command.data.s32[0] = x;
-  command.data.s32[1] = y;
-  return command;
-}
+ctx_s32 (CtxCode code, int32_t x, int32_t y);
 #endif
 
 CtxEntry
-ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1)
-{
-  CtxEntry command = ctx_void (code);
-  command.data.s16[0] = x0;
-  command.data.s16[1] = y0;
-  command.data.s16[2] = x1;
-  command.data.s16[3] = y1;
-  return command;
-}
-
+ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1);
 static CtxEntry
 ctx_u8 (CtxCode code,
         uint8_t a, uint8_t b, uint8_t c, uint8_t d,
-        uint8_t e, uint8_t f, uint8_t g, uint8_t h)
-{
-  CtxEntry command = ctx_void (code);
-  command.data.u8[0] = a;
-  command.data.u8[1] = b;
-  command.data.u8[2] = c;
-  command.data.u8[3] = d;
-  command.data.u8[4] = e;
-  command.data.u8[5] = f;
-  command.data.u8[6] = g;
-  command.data.u8[7] = h;
-  return command;
-}
+        uint8_t e, uint8_t f, uint8_t g, uint8_t h);
 
 #define CTX_PROCESS_VOID(cmd) do {\
   CtxEntry command = ctx_void (cmd); \
@@ -6488,5648 +6841,5579 @@ ctx_u8 (CtxCode code,
   ctx_process (ctx, &command);}while(0)
 
 
+#if CTX_BITPACK_PACKER
+static int
+ctx_last_history (CtxDrawlist *drawlist);
+#endif
+
+#if CTX_BITPACK_PACKER
 static void
-ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len)
-{
-  CtxEntry commands[1 + 2 + len/8];
-  ctx_memset (commands, 0, sizeof (commands) );
-  commands[0] = ctx_u32 (code, arg0, arg1);
-  commands[1].code = CTX_DATA;
-  commands[1].data.u32[0] = len;
-  commands[1].data.u32[1] = len/9+1;
-  memcpy( (char *) &commands[2].data.u8[0], string, len);
-  ( (char *) (&commands[2].data.u8[0]) ) [len]=0;
-  ctx_process (ctx, commands);
-}
+ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos);
 
 static void
-ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1)
+ctx_drawlist_bitpack (CtxDrawlist *drawlist, int start_pos);
+#endif
+
+static void
+ctx_drawlist_compact (CtxDrawlist *drawlist);
+static void
+ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1);
+static void
+ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg0, float arg1);
+static void
+ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len);
+
+#endif
+
+#ifndef __CTX_UTIL_H
+#define __CTX_UTIL_H
+
+inline static float ctx_fast_hypotf (float x, float y)
 {
-  ctx_process_cmd_str_with_len (ctx, code, string, arg0, arg1, strlen (string));
+  if (x < 0) { x = -x; }
+  if (y < 0) { y = -y; }
+  if (x < y)
+    { return 0.96f * y + 0.4f * x; }
+  else
+    { return 0.96f * x + 0.4f * y; }
 }
 
-
-#if CTX_BITPACK_PACKER
-static int
-ctx_last_history (CtxDrawlist *drawlist)
+static int ctx_str_is_number (const char *str)
 {
-  int last_history = 0;
-  int i = 0;
-  while (i < drawlist->count)
+  int got_digit = 0;
+  for (int i = 0; str[i]; i++)
+  {
+    if (str[i] >= '0' && str[i] <= '9')
     {
-      CtxEntry *entry = &drawlist->entries[i];
-      i += (ctx_conts_for_entry (entry) + 1);
+       got_digit ++;
     }
-  return last_history;
+    else if (str[i] == '.')
+    {
+    }
+    else
+      return 0;
+  }
+  if (got_digit)
+    return 1;
+  return 0;
 }
-#endif
 
-#if CTX_BITPACK_PACKER
+#if CTX_FONTS_FROM_FILE
 
-static float
-find_max_dev (CtxEntry *entry, int nentrys)
+typedef struct CtxFileContent
 {
-  float max_dev = 0.0;
-  for (int c = 0; c < nentrys; c++)
+  char *path;
+  unsigned char *contents;
+  long  length;
+  int   free_data;
+} CtxFileContent;
+
+CtxList *registered_contents = NULL;
+
+void
+ctx_register_contents (const char *path,
+                       const unsigned char *contents,
+                       long length,
+                       int  free_data)
+{
+  // if (path[0] != '/') && strchr(path, ':')) 
+  //   with this check regular use is faster, but we lose
+  //   generic filesystem overrides..
+  for (CtxList *l = registered_contents; l; l = l->next)
+  {
+    CtxFileContent *c = l->data;
+    if (!strcmp (c->path, path))
     {
-      for (int d = 0; d < 2; d++)
-        {
-          if (entry[c].data.f[d] > max_dev)
-            { max_dev = entry[c].data.f[d]; }
-          if (entry[c].data.f[d] < -max_dev)
-            { max_dev = -entry[c].data.f[d]; }
-        }
+       if (c->free_data)
+       {
+         free (c->contents);
+       }
+       c->free_data = free_data;
+       c->contents = (unsigned char*)contents;
+       c->length = length;
+       return;
     }
-  return max_dev;
+  }
+  CtxFileContent *c = calloc (sizeof (CtxFileContent), 1);
+  c->free_data = free_data;
+  c->contents = (unsigned char*)contents;
+  c->length    = length;
+  ctx_list_append (&registered_contents, c);
 }
 
-static void
-pack_s8_args (CtxEntry *entry, int npairs)
+void
+_ctx_file_set_contents (const char     *path,
+                        const unsigned char  *contents,
+                        long            length)
 {
-  for (int c = 0; c < npairs; c++)
-    for (int d = 0; d < 2; d++)
-      { entry[0].data.s8[c*2+d]=entry[c].data.f[d] * CTX_SUBDIV; }
+  FILE *file;
+  file = fopen (path, "wb");
+  if (!file)
+    { return; }
+  if (length < 0) length = strlen ((const char*)contents);
+  fwrite (contents, 1, length, file);
+  fclose (file);
 }
 
-static void
-pack_s16_args (CtxEntry *entry, int npairs)
+static int
+__ctx_file_get_contents (const char     *path,
+                        unsigned char **contents,
+                        long           *length)
 {
-  for (int c = 0; c < npairs; c++)
-    for (int d = 0; d < 2; d++)
-      { entry[0].data.s16[c*2+d]=entry[c].data.f[d] * CTX_SUBDIV; }
+  FILE *file;
+  long  size;
+  long  remaining;
+  char *buffer;
+  file = fopen (path, "rb");
+  if (!file)
+    { return -1; }
+  fseek (file, 0, SEEK_END);
+  size = remaining = ftell (file);
+  if (length)
+    { *length =size; }
+  rewind (file);
+  buffer = (char*)malloc (size + 8);
+  if (!buffer)
+    {
+      fclose (file);
+      return -1;
+    }
+  remaining -= fread (buffer, 1, remaining, file);
+  if (remaining)
+    {
+      fclose (file);
+      free (buffer);
+      return -1;
+    }
+  fclose (file);
+  *contents = (unsigned char*) buffer;
+  buffer[size] = 0;
+  return 0;
 }
+
+#include <limits.h>
+
+int
+ctx_get_contents (const char     *uri,
+                  unsigned char **contents,
+                  long           *length);
+
+
+
 #endif
 
-#if CTX_BITPACK_PACKER
-static void
-ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos)
+
+#endif
+
+
+static int
+ctx_conts_for_entry (CtxEntry *entry)
 {
-  CtxIterator iterator;
-  if ( (drawlist->flags & CTX_TRANSFORMATION_BITPACK) == 0)
-    { return; }
-  ctx_iterator_init (&iterator, drawlist, start_pos, CTX_ITERATOR_FLAT);
-  iterator.end_pos = drawlist->count - 5;
-  CtxCommand *command = NULL;
-  while ( (command = ctx_iterator_next (&iterator) ) )
+    switch (entry->code)
     {
-      CtxEntry *entry = &command->entry;
-      /* things smaller than this have probably been scaled down
-         beyond recognition, bailing for both better packing and less rasterization work
-       */
-      if (command[0].code == CTX_REL_CURVE_TO)
+      case CTX_DATA:
+        return entry->data.u32[1];
+      case CTX_LINEAR_GRADIENT:
+      //case CTX_DEFINE_TEXTURE:
+        return 1;
+      case CTX_RADIAL_GRADIENT:
+      case CTX_ARC:
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_APPLY_TRANSFORM:
+      case CTX_COLOR:
+      case CTX_ROUND_RECTANGLE:
+      case CTX_SHADOW_COLOR:
+        return 2;
+      case CTX_FILL_RECT:
+      case CTX_STROKE_RECT:
+      case CTX_RECTANGLE:
+      case CTX_VIEW_BOX:
+      case CTX_REL_QUAD_TO:
+      case CTX_QUAD_TO:
+        return 1;
+
+      case CTX_TEXT:
+      case CTX_LINE_DASH:
+      case CTX_COLOR_SPACE:
+      case CTX_STROKE_TEXT:
+      case CTX_FONT:
+      case CTX_TEXTURE:
         {
-          float max_dev = find_max_dev (entry, 3);
-          if (max_dev < 1.0)
-            {
-              entry[0].code = CTX_REL_LINE_TO;
-              entry[0].data.f[0] = entry[2].data.f[0];
-              entry[0].data.f[1] = entry[2].data.f[1];
-              entry[1].code = CTX_NOP;
-              entry[2].code = CTX_NOP;
-            }
+          int eid_len = entry[1].data.u32[1];
+          return eid_len + 1;
+        }
+      case CTX_DEFINE_TEXTURE:
+        {
+          int eid_len = entry[2].data.u32[1];
+          int pix_len = entry[2 + eid_len + 1].data.u32[1];
+          return eid_len + pix_len + 2 + 1;
         }
+
+      default:
+        return 0;
     }
 }
-#endif
 
-#if CTX_BITPACK_PACKER
+// expanding arc_to to arc can be the job
+// of a layer in front of renderer?
+//   doing:
+//     rectangle
+//     arc
+//     ... etc reduction to beziers
+//     or even do the reduction to
+//     polylines directly here...
+//     making the rasterizer able to
+//     only do poly-lines? will that be faster?
+
+/* the iterator - should decode bitpacked data as well -
+ * making the rasterizers simpler, possibly do unpacking
+ * all the way to absolute coordinates.. unless mixed
+ * relative/not are wanted.
+ */
+
+
 static void
-ctx_drawlist_bitpack (CtxDrawlist *drawlist, int start_pos)
+ctx_iterator_init (CtxIterator      *iterator,
+                   CtxDrawlist  *drawlist,
+                   int               start_pos,
+                   int               flags)
+{
+  iterator->drawlist   = drawlist;
+  iterator->flags          = flags;
+  iterator->bitpack_pos    = 0;
+  iterator->bitpack_length = 0;
+  iterator->pos            = start_pos;
+  iterator->end_pos        = drawlist->count;
+  iterator->first_run      = 1; // -1 is a marker used for first run
+  ctx_memset (iterator->bitpack_command, 0, sizeof (iterator->bitpack_command) );
+}
+
+int ctx_iterator_pos (CtxIterator *iterator)
 {
+  return iterator->pos;
+}
+
+static CtxEntry *_ctx_iterator_next (CtxIterator *iterator)
+{
+  int ret = iterator->pos;
+  CtxEntry *entry = &iterator->drawlist->entries[ret];
+  if (ret >= iterator->end_pos)
+    { return NULL; }
+  if (iterator->first_run == 0)
+    { iterator->pos += (ctx_conts_for_entry (entry) + 1); }
+  iterator->first_run = 0;
+  if (iterator->pos >= iterator->end_pos)
+    { return NULL; }
+  return &iterator->drawlist->entries[iterator->pos];
+}
+
+// 6024x4008
 #if CTX_BITPACK
-  int i = 0;
-  if ( (drawlist->flags & CTX_TRANSFORMATION_BITPACK) == 0)
-    { return; }
-  ctx_drawlist_remove_tiny_curves (drawlist, drawlist->bitpack_pos);
-  i = drawlist->bitpack_pos;
-  if (start_pos > i)
-    { i = start_pos; }
-  while (i < drawlist->count - 4) /* the -4 is to avoid looking past
-                                    initialized data we're not ready
-                                    to bitpack yet*/
+static void
+ctx_iterator_expand_s8_args (CtxIterator *iterator, CtxEntry *entry)
+{
+  int no = 0;
+  for (int cno = 0; cno < 4; cno++)
+    for (int d = 0; d < 2; d++, no++)
+      iterator->bitpack_command[cno].data.f[d] =
+        entry->data.s8[no] * 1.0f / CTX_SUBDIV;
+  iterator->bitpack_command[0].code =
+    iterator->bitpack_command[1].code =
+      iterator->bitpack_command[2].code =
+        iterator->bitpack_command[3].code = CTX_CONT;
+  iterator->bitpack_length = 4;
+  iterator->bitpack_pos = 0;
+}
+
+static void
+ctx_iterator_expand_s16_args (CtxIterator *iterator, CtxEntry *entry)
+{
+  int no = 0;
+  for (int cno = 0; cno < 2; cno++)
+    for (int d = 0; d < 2; d++, no++)
+      iterator->bitpack_command[cno].data.f[d] = entry->data.s16[no] * 1.0f /
+          CTX_SUBDIV;
+  iterator->bitpack_command[0].code =
+    iterator->bitpack_command[1].code = CTX_CONT;
+  iterator->bitpack_length = 2;
+  iterator->bitpack_pos    = 0;
+}
+#endif
+
+CtxCommand *
+ctx_iterator_next (CtxIterator *iterator)
+{
+  CtxEntry *ret;
+#if CTX_BITPACK
+  int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK;
+again:
+  if (iterator->bitpack_length)
     {
-      CtxEntry *entry = &drawlist->entries[i];
-      if (entry[0].code == CTX_SET_RGBA_U8 &&
-          entry[1].code == CTX_MOVE_TO &&
-          entry[2].code == CTX_REL_LINE_TO &&
-          entry[3].code == CTX_REL_LINE_TO &&
-          entry[4].code == CTX_REL_LINE_TO &&
-          entry[5].code == CTX_REL_LINE_TO &&
-          entry[6].code == CTX_FILL &&
-          ctx_fabsf (entry[2].data.f[0] - 1.0f) < 0.02f &&
-          ctx_fabsf (entry[3].data.f[1] - 1.0f) < 0.02f)
+      ret = &iterator->bitpack_command[iterator->bitpack_pos];
+      iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1);
+      if (iterator->bitpack_pos >= iterator->bitpack_length)
         {
-          entry[0].code = CTX_SET_PIXEL;
-          entry[0].data.u16[2] = entry[1].data.f[0];
-          entry[0].data.u16[3] = entry[1].data.f[1];
-          entry[1].code = CTX_NOP;
-          entry[2].code = CTX_NOP;
-          entry[3].code = CTX_NOP;
-          entry[4].code = CTX_NOP;
-          entry[5].code = CTX_NOP;
-          entry[6].code = CTX_NOP;
-        }
-#if 1
-      else if (entry[0].code == CTX_REL_LINE_TO)
-        {
-          if (entry[1].code == CTX_REL_LINE_TO &&
-              entry[2].code == CTX_REL_LINE_TO &&
-              entry[3].code == CTX_REL_LINE_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_LINE_TO_X4;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
-          else if (entry[1].code == CTX_REL_CURVE_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_LINE_TO_REL_CURVE_TO;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
-          else if (entry[1].code == CTX_REL_LINE_TO &&
-                   entry[2].code == CTX_REL_LINE_TO &&
-                   entry[3].code == CTX_REL_LINE_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_LINE_TO_X4;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
-          else if (entry[1].code == CTX_REL_MOVE_TO)
-            {
-              float max_dev = find_max_dev (entry, 2);
-              if (max_dev < 31000 / CTX_SUBDIV)
-                {
-                  pack_s16_args (entry, 2);
-                  entry[0].code = CTX_REL_LINE_TO_REL_MOVE_TO;
-                  entry[1].code = CTX_NOP;
-                }
-            }
-          else if (entry[1].code == CTX_REL_LINE_TO)
-            {
-              float max_dev = find_max_dev (entry, 2);
-              if (max_dev < 31000 / CTX_SUBDIV)
-                {
-                  pack_s16_args (entry, 2);
-                  entry[0].code = CTX_REL_LINE_TO_X2;
-                  entry[1].code = CTX_NOP;
-                }
-            }
+          iterator->bitpack_length = 0;
         }
+      return (CtxCommand *) ret;
+    }
 #endif
-#if 1
-      else if (entry[0].code == CTX_REL_CURVE_TO)
-        {
-          if (entry[3].code == CTX_REL_LINE_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_CURVE_TO_REL_LINE_TO;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
-          else if (entry[3].code == CTX_REL_MOVE_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_CURVE_TO_REL_MOVE_TO;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
+  ret = _ctx_iterator_next (iterator);
+#if CTX_BITPACK
+  if (ret && expand_bitpack)
+    switch ((CtxCode)(ret->code))
+      {
+        case CTX_REL_CURVE_TO_REL_LINE_TO:
+          ctx_iterator_expand_s8_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_REL_CURVE_TO;
+          iterator->bitpack_command[1].code =
+          iterator->bitpack_command[2].code = CTX_CONT;
+          iterator->bitpack_command[3].code = CTX_REL_LINE_TO;
+          // 0.0 here is a common optimization - so check for it
+          if (ret->data.s8[6]== 0 && ret->data.s8[7] == 0)
+            { iterator->bitpack_length = 3; }
           else
-            {
-              float max_dev = find_max_dev (entry, 3);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 3);
-                  ctx_arg_s8 (6) =
-                    ctx_arg_s8 (7) = 0;
-                  entry[0].code = CTX_REL_CURVE_TO_REL_LINE_TO;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                }
-            }
-        }
+            iterator->bitpack_length          = 4;
+          goto again;
+        case CTX_REL_LINE_TO_REL_CURVE_TO:
+          ctx_iterator_expand_s8_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_REL_LINE_TO;
+          iterator->bitpack_command[1].code = CTX_REL_CURVE_TO;
+          iterator->bitpack_length          = 2;
+          goto again;
+        case CTX_REL_CURVE_TO_REL_MOVE_TO:
+          ctx_iterator_expand_s8_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_REL_CURVE_TO;
+          iterator->bitpack_command[3].code = CTX_REL_MOVE_TO;
+          iterator->bitpack_length          = 4;
+          goto again;
+        case CTX_REL_LINE_TO_X4:
+          ctx_iterator_expand_s8_args (iterator, ret);
+          iterator->bitpack_command[0].code =
+          iterator->bitpack_command[1].code =
+          iterator->bitpack_command[2].code =
+          iterator->bitpack_command[3].code = CTX_REL_LINE_TO;
+          iterator->bitpack_length          = 4;
+          goto again;
+        case CTX_REL_QUAD_TO_S16:
+          ctx_iterator_expand_s16_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_REL_QUAD_TO;
+          iterator->bitpack_length          = 1;
+          goto again;
+        case CTX_REL_QUAD_TO_REL_QUAD_TO:
+          ctx_iterator_expand_s8_args (iterator, ret);
+          iterator->bitpack_command[0].code =
+          iterator->bitpack_command[2].code = CTX_REL_QUAD_TO;
+          iterator->bitpack_length          = 3;
+          goto again;
+        case CTX_REL_LINE_TO_X2:
+          ctx_iterator_expand_s16_args (iterator, ret);
+          iterator->bitpack_command[0].code =
+          iterator->bitpack_command[1].code = CTX_REL_LINE_TO;
+          iterator->bitpack_length          = 2;
+          goto again;
+        case CTX_REL_LINE_TO_REL_MOVE_TO:
+          ctx_iterator_expand_s16_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_REL_LINE_TO;
+          iterator->bitpack_command[1].code = CTX_REL_MOVE_TO;
+          iterator->bitpack_length          = 2;
+          goto again;
+        case CTX_MOVE_TO_REL_LINE_TO:
+          ctx_iterator_expand_s16_args (iterator, ret);
+          iterator->bitpack_command[0].code = CTX_MOVE_TO;
+          iterator->bitpack_command[1].code = CTX_REL_MOVE_TO;
+          iterator->bitpack_length          = 2;
+          goto again;
+        case CTX_FILL_MOVE_TO:
+          iterator->bitpack_command[1]      = *ret;
+          iterator->bitpack_command[0].code = CTX_FILL;
+          iterator->bitpack_command[1].code = CTX_MOVE_TO;
+          iterator->bitpack_pos             = 0;
+          iterator->bitpack_length          = 2;
+          goto again;
+        case CTX_LINEAR_GRADIENT:
+        case CTX_QUAD_TO:
+        case CTX_REL_QUAD_TO:
+        case CTX_TEXTURE:
+        case CTX_RECTANGLE:
+        case CTX_VIEW_BOX:
+        case CTX_ARC:
+        case CTX_ARC_TO:
+        case CTX_REL_ARC_TO:
+        case CTX_COLOR:
+        case CTX_SHADOW_COLOR:
+        case CTX_RADIAL_GRADIENT:
+        case CTX_CURVE_TO:
+        case CTX_REL_CURVE_TO:
+        case CTX_APPLY_TRANSFORM:
+        case CTX_ROUND_RECTANGLE:
+        case CTX_TEXT:
+        case CTX_STROKE_TEXT:
+        case CTX_FONT:
+        case CTX_LINE_DASH:
+        case CTX_FILL:
+        case CTX_NOP:
+        case CTX_MOVE_TO:
+        case CTX_LINE_TO:
+        case CTX_REL_MOVE_TO:
+        case CTX_REL_LINE_TO:
+        case CTX_VER_LINE_TO:
+        case CTX_REL_VER_LINE_TO:
+        case CTX_HOR_LINE_TO:
+        case CTX_REL_HOR_LINE_TO:
+        case CTX_ROTATE:
+        case CTX_FLUSH:
+        case CTX_TEXT_ALIGN:
+        case CTX_TEXT_BASELINE:
+        case CTX_TEXT_DIRECTION:
+        case CTX_MITER_LIMIT:
+        case CTX_GLOBAL_ALPHA:
+        case CTX_COMPOSITING_MODE:
+        case CTX_BLEND_MODE:
+        case CTX_SHADOW_BLUR:
+        case CTX_SHADOW_OFFSET_X:
+        case CTX_SHADOW_OFFSET_Y:
+        case CTX_RESET:
+        case CTX_EXIT:
+        case CTX_BEGIN_PATH:
+        case CTX_CLOSE_PATH:
+        case CTX_SAVE:
+        case CTX_CLIP:
+        case CTX_PRESERVE:
+        case CTX_DEFINE_GLYPH:
+        case CTX_IDENTITY:
+        case CTX_FONT_SIZE:
+        case CTX_START_GROUP:
+        case CTX_END_GROUP:
+        case CTX_RESTORE:
+        case CTX_LINE_WIDTH:
+        case CTX_LINE_DASH_OFFSET:
+        case CTX_STROKE:
+        case CTX_KERNING_PAIR:
+        case CTX_SCALE:
+        case CTX_GLYPH:
+        case CTX_SET_PIXEL:
+        case CTX_FILL_RULE:
+        case CTX_LINE_CAP:
+        case CTX_LINE_JOIN:
+        case CTX_NEW_PAGE:
+        case CTX_SET_KEY:
+        case CTX_TRANSLATE:
+        case CTX_DEFINE_TEXTURE:
+        case CTX_GRADIENT_STOP:
+        case CTX_DATA: // XXX : would be better if we hide the DATAs
+        case CTX_CONT: // shouldnt happen
+        default:
+          iterator->bitpack_length = 0;
+          return (CtxCommand *) ret;
+#if 0
+        default: // XXX remove - and get better warnings
+          iterator->bitpack_command[0] = ret[0];
+          iterator->bitpack_command[1] = ret[1];
+          iterator->bitpack_command[2] = ret[2];
+          iterator->bitpack_command[3] = ret[3];
+          iterator->bitpack_command[4] = ret[4];
+          iterator->bitpack_pos = 0;
+          iterator->bitpack_length = 1;
+          goto again;
 #endif
-#if 1
-      else if (entry[0].code == CTX_REL_QUAD_TO)
-        {
-          if (entry[2].code == CTX_REL_QUAD_TO)
-            {
-              float max_dev = find_max_dev (entry, 4);
-              if (max_dev < 114 / CTX_SUBDIV)
-                {
-                  pack_s8_args (entry, 4);
-                  entry[0].code = CTX_REL_QUAD_TO_REL_QUAD_TO;
-                  entry[1].code = CTX_NOP;
-                  entry[2].code = CTX_NOP;
-                  entry[3].code = CTX_NOP;
-                }
-            }
-          else
-            {
-              float max_dev = find_max_dev (entry, 2);
-              if (max_dev < 3100 / CTX_SUBDIV)
-                {
-                  pack_s16_args (entry, 2);
-                  entry[0].code = CTX_REL_QUAD_TO_S16;
-                  entry[1].code = CTX_NOP;
-                }
-            }
-        }
-#endif
-#if 1
-      else if (entry[0].code == CTX_FILL &&
-               entry[1].code == CTX_MOVE_TO)
-        {
-          entry[0] = entry[1];
-          entry[0].code = CTX_FILL_MOVE_TO;
-          entry[1].code = CTX_NOP;
-        }
-#endif
-#if 1
-      else if (entry[0].code == CTX_MOVE_TO &&
-               entry[1].code == CTX_MOVE_TO &&
-               entry[2].code == CTX_MOVE_TO)
-        {
-          entry[0]      = entry[2];
-          entry[0].code = CTX_MOVE_TO;
-          entry[1].code = CTX_NOP;
-          entry[2].code = CTX_NOP;
-        }
-#endif
-#if 1
-      else if ( (entry[0].code == CTX_MOVE_TO &&
-                 entry[1].code == CTX_MOVE_TO) ||
-                (entry[0].code == CTX_REL_MOVE_TO &&
-                 entry[1].code == CTX_MOVE_TO) )
-        {
-          entry[0]      = entry[1];
-          entry[0].code = CTX_MOVE_TO;
-          entry[1].code = CTX_NOP;
-        }
-#endif
-      i += (ctx_conts_for_entry (entry) + 1);
-    }
-  int source = drawlist->bitpack_pos;
-  int target = drawlist->bitpack_pos;
-  int removed = 0;
-  /* remove nops that have been inserted as part of shortenings
-   */
-  while (source < drawlist->count)
-    {
-      CtxEntry *sentry = &drawlist->entries[source];
-      CtxEntry *tentry = &drawlist->entries[target];
-      while (sentry->code == CTX_NOP && source < drawlist->count)
-        {
-          source++;
-          sentry = &drawlist->entries[source];
-          removed++;
-        }
-      if (sentry != tentry)
-        { *tentry = *sentry; }
-      source ++;
-      target ++;
-    }
-  drawlist->count -= removed;
-  drawlist->bitpack_pos = drawlist->count;
+      }
 #endif
+  return (CtxCommand *) ret;
 }
 
-#endif
-
+static void ctx_drawlist_compact (CtxDrawlist *drawlist);
 static void
-ctx_drawlist_compact (CtxDrawlist *drawlist)
+ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
 {
-#if CTX_BITPACK_PACKER
-  int last_history;
-  last_history = ctx_last_history (drawlist);
+#if CTX_DRAWLIST_STATIC
+  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
+    {
+      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
+      drawlist->entries = &sbuf[0];
+      drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
+    {
+      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
+      drawlist->entries = &sbuf[0];
+      drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else
+    {
+      static CtxEntry sbuf[CTX_MAX_JOURNAL_SIZE];
+      drawlist->entries = &sbuf[0];
+      drawlist->size = CTX_MAX_JOURNAL_SIZE;
+      ctx_drawlist_compact (drawlist);
+    }
 #else
-  if (drawlist) {};
-#endif
-#if CTX_BITPACK_PACKER
-  ctx_drawlist_bitpack (drawlist, last_history);
+  int new_size = desired_size;
+  int min_size = CTX_MIN_JOURNAL_SIZE;
+  int max_size = CTX_MAX_JOURNAL_SIZE;
+  if ((drawlist->flags & CTX_DRAWLIST_EDGE_LIST))
+    {
+      min_size = CTX_MIN_EDGE_LIST_SIZE;
+      max_size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
+    {
+      min_size = CTX_MIN_EDGE_LIST_SIZE;
+      max_size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else
+    {
+      ctx_drawlist_compact (drawlist);
+    }
+
+  if (new_size < drawlist->size)
+    { return; }
+  if (drawlist->size == max_size)
+    { return; }
+  if (new_size < min_size)
+    { new_size = min_size; }
+  if (new_size < drawlist->count)
+    { new_size = drawlist->count + 4; }
+  if (new_size >= max_size)
+    { new_size = max_size; }
+  if (new_size != drawlist->size)
+    {
+      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, drawlist->flags, new_size, 
drawlist->size);
+  if (drawlist->entries)
+    {
+      //printf ("grow %p to %d from %d\n", drawlist, new_size, drawlist->size);
+      CtxEntry *ne =  (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
+      memcpy (ne, drawlist->entries, drawlist->size * sizeof (CtxEntry) );
+      free (drawlist->entries);
+      drawlist->entries = ne;
+      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, sizeof (CtxEntry) * new_size);
+    }
+  else
+    {
+      //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size);
+      drawlist->entries = (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
+    }
+  drawlist->size = new_size;
+    }
+  //fprintf (stderr, "drawlist %p is %d\n", drawlist, drawlist->size);
 #endif
 }
 
+static int
+ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
+{
+  int max_size = CTX_MAX_JOURNAL_SIZE;
+  int ret = drawlist->count;
+  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
+    {
+      max_size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
+    {
+      max_size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
+    {
+      return ret;
+    }
+  if (ret + 1024 >= drawlist->size - 40)
+    {
+      int new_ = CTX_MAX (drawlist->size * 2, ret + 1024);
+      ctx_drawlist_resize (drawlist, new_);
+    }
 
-#ifndef __CTX_TRANSFORM
-#define __CTX_TRANSFORM
+  if (drawlist->count >= max_size - 20)
+    {
+      return 0;
+    }
+  drawlist->entries[drawlist->count] = *entry;
+  ret = drawlist->count;
+  drawlist->count++;
+  return ret;
+}
 
-static void
-_ctx_user_to_device (CtxState *state, float *x, float *y)
+int
+ctx_add_single (Ctx *ctx, void *entry)
 {
-  ctx_matrix_apply_transform (&state->gstate.transform, x, y);
+  return ctx_drawlist_add_single (&ctx->drawlist, (CtxEntry *) entry);
 }
 
-static void
-_ctx_user_to_device_distance (CtxState *state, float *x, float *y)
+int
+ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry)
 {
-  const CtxMatrix *m = &state->gstate.transform;
-  ctx_matrix_apply_transform (m, x, y);
-  *x -= m->m[2][0];
-  *y -= m->m[2][1];
+  int length = ctx_conts_for_entry (entry) + 1;
+  int ret = 0;
+  for (int i = 0; i < length; i ++)
+    {
+      ret = ctx_drawlist_add_single (drawlist, &entry[i]);
+    }
+  return ret;
 }
 
-void ctx_user_to_device          (Ctx *ctx, float *x, float *y)
+#if 0
+int
+ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry)
 {
-  _ctx_user_to_device (&ctx->state, x, y);
+  int length = ctx_conts_for_entry (entry) + 1;
+  int tmp_pos = ctx_drawlist_add_entry (drawlist, entry);
+  for (int i = 0; i < length; i++)
+  {
+    for (int j = pos + i + 1; j < tmp_pos; j++)
+      drawlist->entries[j] = entry[j-1];
+    drawlist->entries[pos + i] = entry[i];
+  }
+  return pos;
 }
-void ctx_user_to_device_distance (Ctx *ctx, float *x, float *y)
+#endif
+int
+ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry)
 {
-  _ctx_user_to_device_distance (&ctx->state, x, y);
+  int length = ctx_conts_for_entry (entry) + 1;
+  int tmp_pos = ctx_drawlist_add_entry (drawlist, entry);
+#if 1
+  for (int i = 0; i < length; i++)
+  {
+    for (int j = tmp_pos; j > pos + i; j--)
+      drawlist->entries[j] = drawlist->entries[j-1];
+    drawlist->entries[pos + i] = entry[i];
+  }
+  return pos;
+#endif
+  return tmp_pos;
 }
 
-static void
-ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f)
+int ctx_append_drawlist (Ctx *ctx, void *data, int length)
 {
-  matrix->m[0][0] = a;
-  matrix->m[0][1] = b;
-  matrix->m[1][0] = c;
-  matrix->m[1][1] = d;
-  matrix->m[2][0] = e;
-  matrix->m[2][1] = f;
+  CtxEntry *entries = (CtxEntry *) data;
+  if (length % sizeof (CtxEntry) )
+    {
+      ctx_log("drawlist not multiple of 9\n");
+      return -1;
+    }
+  for (unsigned int i = 0; i < length / sizeof (CtxEntry); i++)
+    {
+      ctx_drawlist_add_single (&ctx->drawlist, &entries[i]);
+    }
+  return 0;
 }
 
-void
-ctx_matrix_identity (CtxMatrix *matrix)
+int ctx_set_drawlist (Ctx *ctx, void *data, int length)
 {
-  matrix->m[0][0] = 1.0f;
-  matrix->m[0][1] = 0.0f;
-  matrix->m[1][0] = 0.0f;
-  matrix->m[1][1] = 1.0f;
-  matrix->m[2][0] = 0.0f;
-  matrix->m[2][1] = 0.0f;
+  CtxDrawlist *drawlist = &ctx->drawlist;
+  ctx->drawlist.count = 0;
+  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
+    {
+      return -1;
+    }
+  if (length % 9) return -1;
+  ctx_drawlist_resize (drawlist, length/9);
+  memcpy (drawlist->entries, data, length);
+  drawlist->count = length / 9;
+  return length;
 }
 
-void
-ctx_matrix_multiply (CtxMatrix       *result,
-                     const CtxMatrix *t,
-                     const CtxMatrix *s)
+int ctx_get_drawlist_count (Ctx *ctx)
 {
-  CtxMatrix r;
-  r.m[0][0] = t->m[0][0] * s->m[0][0] + t->m[0][1] * s->m[1][0];
-  r.m[0][1] = t->m[0][0] * s->m[0][1] + t->m[0][1] * s->m[1][1];
-  r.m[1][0] = t->m[1][0] * s->m[0][0] + t->m[1][1] * s->m[1][0];
-  r.m[1][1] = t->m[1][0] * s->m[0][1] + t->m[1][1] * s->m[1][1];
-  r.m[2][0] = t->m[2][0] * s->m[0][0] + t->m[2][1] * s->m[1][0] + s->m[2][0];
-  r.m[2][1] = t->m[2][0] * s->m[0][1] + t->m[2][1] * s->m[1][1] + s->m[2][1];
-  *result = r;
+  return ctx->drawlist.count;
 }
 
-
-void
-ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
+const CtxEntry *ctx_get_drawlist (Ctx *ctx)
 {
-  CtxMatrix transform;
-  transform.m[0][0] = 1.0f;
-  transform.m[0][1] = 0.0f;
-  transform.m[1][0] = 0.0f;
-  transform.m[1][1] = 1.0f;
-  transform.m[2][0] = x;
-  transform.m[2][1] = y;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  return ctx->drawlist.entries;
 }
 
-void
-ctx_matrix_scale (CtxMatrix *matrix, float x, float y)
+int
+ctx_add_data (Ctx *ctx, void *data, int length)
 {
-  CtxMatrix transform;
-  transform.m[0][0] = x;
-  transform.m[0][1] = 0.0f;
-  transform.m[1][0] = 0.0f;
-  transform.m[1][1] = y;
-  transform.m[2][0] = 0.0f;
-  transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  if (length % sizeof (CtxEntry) )
+    {
+      //ctx_log("err\n");
+      return -1;
+    }
+  /* some more input verification might be in order.. like
+   * verify that it is well-formed up to length?
+   *
+   * also - it would be very useful to stop processing
+   * upon flush - and do drawlist resizing.
+   */
+  return ctx_drawlist_add_entry (&ctx->drawlist, (CtxEntry *) data);
 }
 
-void
-ctx_matrix_rotate (CtxMatrix *matrix, float angle)
+int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2])
 {
-  CtxMatrix transform;
-  float val_sin = ctx_sinf (angle);
-  float val_cos = ctx_cosf (angle);
-  transform.m[0][0] =  val_cos;
-  transform.m[0][1] = val_sin;
-  transform.m[1][0] = -val_sin;
-  transform.m[1][1] = val_cos;
-  transform.m[2][0] =     0.0f;
-  transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  CtxEntry entry = {code, {{0},}};
+  entry.data.u32[0] = u32[0];
+  entry.data.u32[1] = u32[1];
+  return ctx_drawlist_add_single (drawlist, &entry);
 }
 
-#if 0
-static void
-ctx_matrix_skew_x (CtxMatrix *matrix, float angle)
+int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
 {
-  CtxMatrix transform;
-  float val_tan = ctx_tanf (angle);
-  transform.m[0][0] =    1.0f;
-  transform.m[0][1] = 0.0f;
-  transform.m[1][0] = val_tan;
-  transform.m[1][1] = 1.0f;
-  transform.m[2][0] =    0.0f;
-  transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  CtxEntry entry = {CTX_DATA, {{0},}};
+  entry.data.u32[0] = 0;
+  entry.data.u32[1] = 0;
+  int ret = ctx_drawlist_add_single (drawlist, &entry);
+  if (!data) { return -1; }
+  int length_in_blocks;
+  if (length <= 0) { length = strlen ( (char *) data) + 1; }
+  length_in_blocks = length / sizeof (CtxEntry);
+  length_in_blocks += (length % sizeof (CtxEntry) ) ?1:0;
+  if (drawlist->count + length_in_blocks + 4 > drawlist->size)
+    { ctx_drawlist_resize (drawlist, drawlist->count * 1.2 + length_in_blocks + 32); }
+  if (drawlist->count >= drawlist->size)
+    { return -1; }
+  drawlist->count += length_in_blocks;
+  drawlist->entries[ret].data.u32[0] = length;
+  drawlist->entries[ret].data.u32[1] = length_in_blocks;
+  memcpy (&drawlist->entries[ret+1], data, length);
+  {
+    //int reverse = ctx_drawlist_add (drawlist, CTX_DATA_REV);
+    CtxEntry entry = {CTX_DATA_REV, {{0},}};
+    entry.data.u32[0] = length;
+    entry.data.u32[1] = length_in_blocks;
+    ctx_drawlist_add_single (drawlist, &entry);
+    /* this reverse marker exist to enable more efficient
+       front to back traversal, can be ignored in other
+       direction, is this needed after string setters as well?
+     */
+  }
+  return ret;
 }
 
-static void
-ctx_matrix_skew_y (CtxMatrix *matrix, float angle)
+static CtxEntry
+ctx_void (CtxCode code)
 {
-  CtxMatrix transform;
-  float val_tan = ctx_tanf (angle);
-  transform.m[0][0] =    1.0f;
-  transform.m[0][1] = val_tan;
-  transform.m[1][0] =    0.0f;
-  transform.m[1][1] = 1.0f;
-  transform.m[2][0] =    0.0f;
-  transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  CtxEntry command;
+  command.code = code;
+  command.data.u32[0] = 0;
+  command.data.u32[1] = 0;
+  return command;
 }
-#endif
 
-
-void
-ctx_identity (Ctx *ctx)
+static CtxEntry
+ctx_f (CtxCode code, float x, float y)
 {
-  CTX_PROCESS_VOID (CTX_IDENTITY);
+  CtxEntry command = ctx_void (code);
+  command.data.f[0] = x;
+  command.data.f[1] = y;
+  return command;
 }
 
-void
-ctx_apply_transform (Ctx *ctx, float a, float b,  // hscale, hskew
-                     float c, float d,  // vskew,  vscale
-                     float e, float f)  // htran,  vtran
+static CtxEntry
+ctx_u32 (CtxCode code, uint32_t x, uint32_t y)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_APPLY_TRANSFORM, a, b),
-    ctx_f (CTX_CONT,            c, d),
-    ctx_f (CTX_CONT,            e, f)
-  };
-  ctx_process (ctx, command);
+  CtxEntry command = ctx_void (code);
+  command.data.u32[0] = x;
+  command.data.u32[1] = y;
+  return command;
 }
 
-void
-ctx_get_transform  (Ctx *ctx, float *a, float *b,
-                    float *c, float *d,
-                    float *e, float *f)
+#if 0
+static CtxEntry
+ctx_s32 (CtxCode code, int32_t x, int32_t y)
 {
-  if (a) { *a = ctx->state.gstate.transform.m[0][0]; }
-  if (b) { *b = ctx->state.gstate.transform.m[0][1]; }
-  if (c) { *c = ctx->state.gstate.transform.m[1][0]; }
-  if (d) { *d = ctx->state.gstate.transform.m[1][1]; }
-  if (e) { *e = ctx->state.gstate.transform.m[2][0]; }
-  if (f) { *f = ctx->state.gstate.transform.m[2][1]; }
+  CtxEntry command = ctx_void (code);
+  command.data.s32[0] = x;
+  command.data.s32[1] = y;
+  return command;
 }
+#endif
 
-void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix)
+CtxEntry
+ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1)
 {
-  ctx_apply_transform (ctx,
-                       matrix->m[0][0], matrix->m[0][1],
-                       matrix->m[1][0], matrix->m[1][1],
-                       matrix->m[2][0], matrix->m[2][1]);
+  CtxEntry command = ctx_void (code);
+  command.data.s16[0] = x0;
+  command.data.s16[1] = y0;
+  command.data.s16[2] = x1;
+  command.data.s16[3] = y1;
+  return command;
 }
 
-void ctx_get_matrix (Ctx *ctx, CtxMatrix *matrix)
+static CtxEntry
+ctx_u8 (CtxCode code,
+        uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+        uint8_t e, uint8_t f, uint8_t g, uint8_t h)
 {
-  *matrix = ctx->state.gstate.transform;
+  CtxEntry command = ctx_void (code);
+  command.data.u8[0] = a;
+  command.data.u8[1] = b;
+  command.data.u8[2] = c;
+  command.data.u8[3] = d;
+  command.data.u8[4] = e;
+  command.data.u8[5] = f;
+  command.data.u8[6] = g;
+  command.data.u8[7] = h;
+  return command;
 }
 
-void ctx_set_matrix (Ctx *ctx, CtxMatrix *matrix)
-{
-  ctx_identity (ctx);
-  ctx_apply_matrix (ctx, matrix);
-}
+#define CTX_PROCESS_VOID(cmd) do {\
+  CtxEntry command = ctx_void (cmd); \
+  ctx_process (ctx, &command);}while(0) \
 
-void ctx_rotate (Ctx *ctx, float x)
-{
-  if (x == 0.0f)
-    return;
-  CTX_PROCESS_F1 (CTX_ROTATE, x);
-  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
-    { ctx->drawlist.count--; }
-}
+#define CTX_PROCESS_F(cmd, x, y) do {\
+  CtxEntry command = ctx_f(cmd, x, y);\
+  ctx_process (ctx, &command);}while(0)
 
-void ctx_scale (Ctx *ctx, float x, float y)
-{
-  if (x == 1.0f && y == 1.0f)
-    return;
-  CTX_PROCESS_F (CTX_SCALE, x, y);
-  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
-    { ctx->drawlist.count--; }
-}
+#define CTX_PROCESS_F1(cmd, x) do {\
+  CtxEntry command = ctx_f(cmd, x, 0);\
+  ctx_process (ctx, &command);}while(0)
 
-void ctx_translate (Ctx *ctx, float x, float y)
-{
-  if (x == 0.0f && y == 0.0f)
-    return;
-  CTX_PROCESS_F (CTX_TRANSLATE, x, y);
-  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
-    { ctx->drawlist.count--; }
-}
-
-void
-ctx_matrix_invert (CtxMatrix *m)
-{
-  CtxMatrix t = *m;
-  float invdet, det = m->m[0][0] * m->m[1][1] -
-                      m->m[1][0] * m->m[0][1];
-  if (det > -0.0000001f && det < 0.0000001f)
-    {
-      m->m[0][0] = m->m[0][1] =
-                     m->m[1][0] = m->m[1][1] =
-                                    m->m[2][0] = m->m[2][1] = 0.0;
-      return;
-    }
-  invdet = 1.0f / det;
-  m->m[0][0] = t.m[1][1] * invdet;
-  m->m[1][0] = -t.m[1][0] * invdet;
-  m->m[2][0] = (t.m[1][0] * t.m[2][1] - t.m[1][1] * t.m[2][0]) * invdet;
-  m->m[0][1] = -t.m[0][1] * invdet;
-  m->m[1][1] = t.m[0][0] * invdet;
-  m->m[2][1] = (t.m[0][1] * t.m[2][0] - t.m[0][0] * t.m[2][1]) * invdet ;
-}
-
-void
-ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
-{
-  float x_in = *x;
-  float y_in = *y;
-  *x = ( (x_in * m->m[0][0]) + (y_in * m->m[1][0]) + m->m[2][0]);
-  *y = ( (y_in * m->m[1][1]) + (x_in * m->m[0][1]) + m->m[2][1]);
-}
-
-
-#endif
-#ifndef __CTX_COLOR
-#define __CTX_COLOR
-
-int ctx_color_model_get_components (CtxColorModel model)
-{
-  switch (model)
-    {
-      case CTX_GRAY:
-        return 1;
-      case CTX_GRAYA:
-      case CTX_GRAYA_A:
-        return 1;
-      case CTX_RGB:
-      case CTX_LAB:
-      case CTX_LCH:
-      case CTX_DRGB:
-        return 3;
-      case CTX_CMYK:
-      case CTX_DCMYK:
-      case CTX_LABA:
-      case CTX_LCHA:
-      case CTX_RGBA:
-      case CTX_DRGBA:
-      case CTX_RGBA_A:
-      case CTX_RGBA_A_DEVICE:
-        return 4;
-      case CTX_DCMYKA:
-      case CTX_CMYKA:
-      case CTX_CMYKA_A:
-      case CTX_DCMYKA_A:
-        return 5;
-    }
-  return 0;
-}
+#define CTX_PROCESS_U32(cmd, x, y) do {\
+  CtxEntry command = ctx_u32(cmd, x, y);\
+  ctx_process (ctx, &command);}while(0)
 
-#if 0
-inline static float ctx_u8_to_float (uint8_t val_u8)
-{
-  float val_f = val_u8 / 255.0;
-  return val_f;
-}
-#else
-float ctx_u8_float[256];
-#endif
+#define CTX_PROCESS_U8(cmd, x) do {\
+  CtxEntry command = ctx_u8(cmd, x,0,0,0,0,0,0,0);\
+  ctx_process (ctx, &command);}while(0)
 
-CtxColor *ctx_color_new ()
-{
-  CtxColor *color = (CtxColor*)ctx_calloc (sizeof (CtxColor), 1);
-  return color;
-}
 
-int ctx_color_is_transparent (CtxColor *color)
+static void
+ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len)
 {
-  return color->alpha <= 0.001f;
+  CtxEntry commands[1 + 2 + (len+1+1)/9];
+  ctx_memset (commands, 0, sizeof (commands) );
+  commands[0] = ctx_u32 (code, arg0, arg1);
+  commands[1].code = CTX_DATA;
+  commands[1].data.u32[0] = len;
+  commands[1].data.u32[1] = (len+1+1)/9 + 1;
+  memcpy( (char *) &commands[2].data.u8[0], string, len);
+  ( (char *) (&commands[2].data.u8[0]) ) [len]=0;
+  ctx_process (ctx, commands);
 }
 
-
-void ctx_color_free (CtxColor *color)
+static void
+ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1)
 {
-  free (color);
+  ctx_process_cmd_str_with_len (ctx, code, string, arg0, arg1, strlen (string));
 }
 
-static void ctx_color_set_RGBA8 (CtxState *state, CtxColor *color, uint8_t r, uint8_t g, uint8_t b, uint8_t 
a)
+static void
+ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg0, float arg1)
 {
-  color->original = color->valid = CTX_VALID_RGBA_U8;
-  color->rgba[0] = r;
-  color->rgba[1] = g;
-  color->rgba[2] = b;
-  color->rgba[3] = a;
-#if CTX_ENABLE_CM
-  color->space = state->gstate.device_space;
-#endif
+  uint32_t iarg0;
+  uint32_t iarg1;
+  memcpy (&iarg0, &arg0, sizeof (iarg0));
+  memcpy (&iarg1, &arg1, sizeof (iarg1));
+  ctx_process_cmd_str_with_len (ctx, code, string, iarg0, iarg1, strlen (string));
 }
 
-#if 0
-static void ctx_color_set_RGBA8_ (CtxColor *color, const uint8_t *in)
+#if CTX_BITPACK_PACKER
+static int
+ctx_last_history (CtxDrawlist *drawlist)
 {
-  ctx_color_set_RGBA8 (color, in[0], in[1], in[2], in[3]);
+  int last_history = 0;
+  int i = 0;
+  while (i < drawlist->count)
+    {
+      CtxEntry *entry = &drawlist->entries[i];
+      i += (ctx_conts_for_entry (entry) + 1);
+    }
+  return last_history;
 }
 #endif
 
-static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, float alpha)
-{
-  color->original = color->valid = CTX_VALID_GRAYA;
-  color->l = gray;
-  color->alpha = alpha;
-}
-#if 0
-static void ctx_color_set_graya_ (CtxColor *color, const float *in)
-{
-  return ctx_color_set_graya (color, in[0], in[1]);
-}
-#endif
+#if CTX_BITPACK_PACKER
 
-void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a)
+static float
+find_max_dev (CtxEntry *entry, int nentrys)
 {
-#if CTX_ENABLE_CM
-  color->original = color->valid = CTX_VALID_RGBA;
-  color->red      = r;
-  color->green    = g;
-  color->blue     = b;
-  color->space    = state->gstate.rgb_space;
-#else
-  color->original     = color->valid = CTX_VALID_RGBA_DEVICE;
-  color->device_red   = r;
-  color->device_green = g;
-  color->device_blue  = b;
-#endif
-  color->alpha        = a;
+  float max_dev = 0.0;
+  for (int c = 0; c < nentrys; c++)
+    {
+      for (int d = 0; d < 2; d++)
+        {
+          if (entry[c].data.f[d] > max_dev)
+            { max_dev = entry[c].data.f[d]; }
+          if (entry[c].data.f[d] < -max_dev)
+            { max_dev = -entry[c].data.f[d]; }
+        }
+    }
+  return max_dev;
 }
 
-static void ctx_color_set_drgba (CtxState *state, CtxColor *color, float r, float g, float b, float a)
+static void
+pack_s8_args (CtxEntry *entry, int npairs)
 {
-#if CTX_ENABLE_CM
-  color->original     = color->valid = CTX_VALID_RGBA_DEVICE;
-  color->device_red   = r;
-  color->device_green = g;
-  color->device_blue  = b;
-  color->alpha        = a;
-  color->space        = state->gstate.device_space;
-#else
-  ctx_color_set_rgba (state, color, r, g, b, a);
-#endif
+  for (int c = 0; c < npairs; c++)
+    for (int d = 0; d < 2; d++)
+      { entry[0].data.s8[c*2+d]=entry[c].data.f[d] * CTX_SUBDIV; }
 }
 
-#if 0
-static void ctx_color_set_rgba_ (CtxState *state, CtxColor *color, const float *in)
+static void
+pack_s16_args (CtxEntry *entry, int npairs)
 {
-  ctx_color_set_rgba (color, in[0], in[1], in[2], in[3]);
+  for (int c = 0; c < npairs; c++)
+    for (int d = 0; d < 2; d++)
+      { entry[0].data.s16[c*2+d]=entry[c].data.f[d] * CTX_SUBDIV; }
 }
 #endif
 
-/* the baseline conversions we have whether CMYK support is enabled or not,
- * providing an effort at right rendering
- */
-static void ctx_cmyk_to_rgb (float c, float m, float y, float k, float *r, float *g, float *b)
-{
-  *r = (1.0f-c) * (1.0f-k);
-  *g = (1.0f-m) * (1.0f-k);
-  *b = (1.0f-y) * (1.0f-k);
-}
-
-// XXX needs state
-void ctx_rgb_to_cmyk (float r, float g, float b,
-              float *c_out, float *m_out, float *y_out, float *k_out)
+#if CTX_BITPACK_PACKER
+static void
+ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos)
 {
-  float c = 1.0f - r;
-  float m = 1.0f - g;
-  float y = 1.0f - b;
-  float k = ctx_minf (c, ctx_minf (y, m) );
-  if (k < 1.0f)
-    {
-      c = (c - k) / (1.0f - k);
-      m = (m - k) / (1.0f - k);
-      y = (y - k) / (1.0f - k);
-    }
-  else
+  CtxIterator iterator;
+  if ( (drawlist->flags & CTX_TRANSFORMATION_BITPACK) == 0)
+    { return; }
+  ctx_iterator_init (&iterator, drawlist, start_pos, CTX_ITERATOR_FLAT);
+  iterator.end_pos = drawlist->count - 5;
+  CtxCommand *command = NULL;
+  while ( (command = ctx_iterator_next (&iterator) ) )
     {
-      c = m = y = 0.0f;
+      CtxEntry *entry = &command->entry;
+      /* things smaller than this have probably been scaled down
+         beyond recognition, bailing for both better packing and less rasterization work
+       */
+      if (command[0].code == CTX_REL_CURVE_TO)
+        {
+          float max_dev = find_max_dev (entry, 3);
+          if (max_dev < 1.0)
+            {
+              entry[0].code = CTX_REL_LINE_TO;
+              entry[0].data.f[0] = entry[2].data.f[0];
+              entry[0].data.f[1] = entry[2].data.f[1];
+              entry[1].code = CTX_NOP;
+              entry[2].code = CTX_NOP;
+            }
+        }
     }
-  *c_out = c;
-  *m_out = m;
-  *y_out = y;
-  *k_out = k;
 }
+#endif
 
-#if CTX_ENABLE_CMYK
-static void ctx_color_set_cmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float 
a)
-{
-  color->original = color->valid = CTX_VALID_CMYKA;
-  color->cyan     = c;
-  color->magenta  = m;
-  color->yellow   = y;
-  color->key      = k;
-  color->alpha    = a;
-#if CTX_ENABLE_CM
-  color->space    = state->gstate.cmyk_space;
-#endif
-}
-
-static void ctx_color_set_dcmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, 
float a)
-{
-  color->original       = color->valid = CTX_VALID_DCMYKA;
-  color->device_cyan    = c;
-  color->device_magenta = m;
-  color->device_yellow  = y;
-  color->device_key     = k;
-  color->alpha          = a;
-#if CTX_ENABLE_CM
-  color->space = state->gstate.device_space;
-#endif
-}
-
-#endif
-
-#if CTX_ENABLE_CM
-
-static void ctx_rgb_user_to_device (CtxState *state, float rin, float gin, float bin,
-                                    float *rout, float *gout, float *bout)
-{
-#if CTX_BABL
-#if 0
-  fprintf (stderr, "-[%p %p\n",
-    state->gstate.fish_rgbaf_user_to_device,
-    state->gstate.fish_rgbaf_device_to_user);
-#endif
-  if (state->gstate.fish_rgbaf_user_to_device)
-  {
-    float rgbaf[4]={rin,gin,bin,1.0};
-    float rgbafo[4];
-    babl_process (state->gstate.fish_rgbaf_user_to_device,
-                  rgbaf, rgbafo, 1);
-
-    *rout = rgbafo[0];
-    *gout = rgbafo[1];
-    *bout = rgbafo[2];
-    return;
-  }
-#endif
-  *rout = rin;
-  *gout = gin;
-  *bout = bin;
-}
-
-static void ctx_rgb_device_to_user (CtxState *state, float rin, float gin, float bin,
-                                    float *rout, float *gout, float *bout)
-{
-#if CTX_BABL
-#if 0
-  fprintf (stderr, "=[%p %p\n",
-    state->gstate.fish_rgbaf_user_to_device,
-    state->gstate.fish_rgbaf_device_to_user);
-#endif
-  if (state->gstate.fish_rgbaf_device_to_user)
-  {
-    float rgbaf[4]={rin,gin,bin,1.0};
-    float rgbafo[4];
-    babl_process (state->gstate.fish_rgbaf_device_to_user,
-                  rgbaf, rgbafo, 1);
-
-    *rout = rgbafo[0];
-    *gout = rgbafo[1];
-    *bout = rgbafo[2];
-    return;
-  }
-#endif
-  *rout = rin;
-  *gout = gin;
-  *bout = bin;
-}
-#endif
-
-static void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out)
+#if CTX_BITPACK_PACKER
+static void
+ctx_drawlist_bitpack (CtxDrawlist *drawlist, int start_pos)
 {
-  if (! (color->valid & CTX_VALID_RGBA_DEVICE) )
+#if CTX_BITPACK
+  int i = 0;
+  if ( (drawlist->flags & CTX_TRANSFORMATION_BITPACK) == 0)
+    { return; }
+  ctx_drawlist_remove_tiny_curves (drawlist, drawlist->bitpack_pos);
+  i = drawlist->bitpack_pos;
+  if (start_pos > i)
+    { i = start_pos; }
+  while (i < drawlist->count - 4) /* the -4 is to avoid looking past
+                                    initialized data we're not ready
+                                    to bitpack yet*/
     {
-#if CTX_ENABLE_CM
-      if (color->valid & CTX_VALID_RGBA)
+      CtxEntry *entry = &drawlist->entries[i];
+      if (entry[0].code == CTX_SET_RGBA_U8 &&
+          entry[1].code == CTX_MOVE_TO &&
+          entry[2].code == CTX_REL_LINE_TO &&
+          entry[3].code == CTX_REL_LINE_TO &&
+          entry[4].code == CTX_REL_LINE_TO &&
+          entry[5].code == CTX_REL_LINE_TO &&
+          entry[6].code == CTX_FILL &&
+          ctx_fabsf (entry[2].data.f[0] - 1.0f) < 0.02f &&
+          ctx_fabsf (entry[3].data.f[1] - 1.0f) < 0.02f)
         {
-          ctx_rgb_user_to_device (state, color->red, color->green, color->blue,
-                                  & (color->device_red), & (color->device_green), & (color->device_blue) );
+          entry[0].code = CTX_SET_PIXEL;
+          entry[0].data.u16[2] = entry[1].data.f[0];
+          entry[0].data.u16[3] = entry[1].data.f[1];
+          entry[1].code = CTX_NOP;
+          entry[2].code = CTX_NOP;
+          entry[3].code = CTX_NOP;
+          entry[4].code = CTX_NOP;
+          entry[5].code = CTX_NOP;
+          entry[6].code = CTX_NOP;
         }
-      else
-#endif
-        if (color->valid & CTX_VALID_RGBA_U8)
-          {
-            float red = ctx_u8_to_float (color->rgba[0]);
-            float green = ctx_u8_to_float (color->rgba[1]);
-            float blue = ctx_u8_to_float (color->rgba[2]);
-#if CTX_ENABLE_CM
-            ctx_rgb_user_to_device (state, red, green, blue,
-                                  & (color->device_red), & (color->device_green), & (color->device_blue) );
-#else
-            color->device_red = red;
-            color->device_green = green;
-            color->device_blue = blue;
-#endif
-            color->alpha        = ctx_u8_to_float (color->rgba[3]);
-          }
-#if CTX_ENABLE_CMYK
-        else if (color->valid & CTX_VALID_CMYKA)
-          {
-            ctx_cmyk_to_rgb (color->cyan, color->magenta, color->yellow, color->key,
-                             &color->device_red,
-                             &color->device_green,
-                             &color->device_blue);
-          }
-#endif
-        else if (color->valid & CTX_VALID_GRAYA)
-          {
-            color->device_red   =
-              color->device_green =
-                color->device_blue  = color->l;
-          }
-      color->valid |= CTX_VALID_RGBA_DEVICE;
-    }
-  out[0] = color->device_red;
-  out[1] = color->device_green;
-  out[2] = color->device_blue;
-  out[3] = color->alpha;
-}
-
-void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
-{
-#if CTX_ENABLE_CM
-  if (! (color->valid & CTX_VALID_RGBA) )
-    {
-      ctx_color_get_drgba (state, color, out);
-      if (color->valid & CTX_VALID_RGBA_DEVICE)
+#if 1
+      else if (entry[0].code == CTX_REL_LINE_TO)
         {
-          ctx_rgb_device_to_user (state, color->device_red, color->device_green, color->device_blue,
-                                  & (color->red), & (color->green), & (color->blue) );
+          if (entry[1].code == CTX_REL_LINE_TO &&
+              entry[2].code == CTX_REL_LINE_TO &&
+              entry[3].code == CTX_REL_LINE_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_LINE_TO_X4;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else if (entry[1].code == CTX_REL_CURVE_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_LINE_TO_REL_CURVE_TO;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else if (entry[1].code == CTX_REL_LINE_TO &&
+                   entry[2].code == CTX_REL_LINE_TO &&
+                   entry[3].code == CTX_REL_LINE_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_LINE_TO_X4;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else if (entry[1].code == CTX_REL_MOVE_TO)
+            {
+              float max_dev = find_max_dev (entry, 2);
+              if (max_dev < 31000 / CTX_SUBDIV)
+                {
+                  pack_s16_args (entry, 2);
+                  entry[0].code = CTX_REL_LINE_TO_REL_MOVE_TO;
+                  entry[1].code = CTX_NOP;
+                }
+            }
+          else if (entry[1].code == CTX_REL_LINE_TO)
+            {
+              float max_dev = find_max_dev (entry, 2);
+              if (max_dev < 31000 / CTX_SUBDIV)
+                {
+                  pack_s16_args (entry, 2);
+                  entry[0].code = CTX_REL_LINE_TO_X2;
+                  entry[1].code = CTX_NOP;
+                }
+            }
         }
-      color->valid |= CTX_VALID_RGBA;
-    }
-  out[0] = color->red;
-  out[1] = color->green;
-  out[2] = color->blue;
-  out[3] = color->alpha;
-#else
-  ctx_color_get_drgba (state, color, out);
 #endif
-}
-
-
-float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb)
-{
-        // XXX todo replace with correct according to primaries
-  return CTX_CSS_RGB_TO_LUMINANCE(rgb);
-}
-uint8_t ctx_u8_color_rgb_to_gray (CtxState *state, const uint8_t *rgb)
-{
-        // XXX todo replace with correct according to primaries
-  return CTX_CSS_RGB_TO_LUMINANCE(rgb);
-}
-
-void ctx_color_get_graya (CtxState *state, CtxColor *color, float *out)
-{
-  if (! (color->valid & CTX_VALID_GRAYA) )
-    {
-      float rgba[4];
-      ctx_color_get_drgba (state, color, rgba);
-      color->l = ctx_float_color_rgb_to_gray (state, rgba);
-      color->valid |= CTX_VALID_GRAYA;
-    }
-  out[0] = color->l;
-  out[1] = color->alpha;
-}
-
-#if CTX_ENABLE_CMYK
-void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out)
-{
-  if (! (color->valid & CTX_VALID_CMYKA) )
-    {
-      if (color->valid & CTX_VALID_GRAYA)
+#if 1
+      else if (entry[0].code == CTX_REL_CURVE_TO)
         {
-          color->cyan = color->magenta = color->yellow = 0.0;
-          color->key = color->l;
+          if (entry[3].code == CTX_REL_LINE_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_CURVE_TO_REL_LINE_TO;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else if (entry[3].code == CTX_REL_MOVE_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_CURVE_TO_REL_MOVE_TO;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else
+            {
+              float max_dev = find_max_dev (entry, 3);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 3);
+                  ctx_arg_s8 (6) =
+                    ctx_arg_s8 (7) = 0;
+                  entry[0].code = CTX_REL_CURVE_TO_REL_LINE_TO;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                }
+            }
         }
-      else
+#endif
+#if 1
+      else if (entry[0].code == CTX_REL_QUAD_TO)
         {
-          float rgba[4];
-          ctx_color_get_rgba (state, color, rgba);
-          ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2],
-                           &color->cyan, &color->magenta, &color->yellow, &color->key);
-          color->alpha = rgba[3];
+          if (entry[2].code == CTX_REL_QUAD_TO)
+            {
+              float max_dev = find_max_dev (entry, 4);
+              if (max_dev < 114 / CTX_SUBDIV)
+                {
+                  pack_s8_args (entry, 4);
+                  entry[0].code = CTX_REL_QUAD_TO_REL_QUAD_TO;
+                  entry[1].code = CTX_NOP;
+                  entry[2].code = CTX_NOP;
+                  entry[3].code = CTX_NOP;
+                }
+            }
+          else
+            {
+              float max_dev = find_max_dev (entry, 2);
+              if (max_dev < 3100 / CTX_SUBDIV)
+                {
+                  pack_s16_args (entry, 2);
+                  entry[0].code = CTX_REL_QUAD_TO_S16;
+                  entry[1].code = CTX_NOP;
+                }
+            }
         }
-      color->valid |= CTX_VALID_CMYKA;
+#endif
+#if 1
+      else if (entry[0].code == CTX_FILL &&
+               entry[1].code == CTX_MOVE_TO)
+        {
+          entry[0] = entry[1];
+          entry[0].code = CTX_FILL_MOVE_TO;
+          entry[1].code = CTX_NOP;
+        }
+#endif
+#if 1
+      else if (entry[0].code == CTX_MOVE_TO &&
+               entry[1].code == CTX_MOVE_TO &&
+               entry[2].code == CTX_MOVE_TO)
+        {
+          entry[0]      = entry[2];
+          entry[0].code = CTX_MOVE_TO;
+          entry[1].code = CTX_NOP;
+          entry[2].code = CTX_NOP;
+        }
+#endif
+#if 1
+      else if ( (entry[0].code == CTX_MOVE_TO &&
+                 entry[1].code == CTX_MOVE_TO) ||
+                (entry[0].code == CTX_REL_MOVE_TO &&
+                 entry[1].code == CTX_MOVE_TO) )
+        {
+          entry[0]      = entry[1];
+          entry[0].code = CTX_MOVE_TO;
+          entry[1].code = CTX_NOP;
+        }
+#endif
+      i += (ctx_conts_for_entry (entry) + 1);
     }
-  out[0] = color->cyan;
-  out[1] = color->magenta;
-  out[2] = color->yellow;
-  out[3] = color->key;
-  out[4] = color->alpha;
-}
-
-#if 0
-static void ctx_color_get_cmyka_u8 (CtxState *state, CtxColor *color, uint8_t *out)
-{
-  if (! (color->valid & CTX_VALID_CMYKA_U8) )
+  int source = drawlist->bitpack_pos;
+  int target = drawlist->bitpack_pos;
+  int removed = 0;
+  /* remove nops that have been inserted as part of shortenings
+   */
+  while (source < drawlist->count)
     {
-      float cmyka[5];
-      ctx_color_get_cmyka (color, cmyka);
-      for (int i = 0; i < 5; i ++)
-        { color->cmyka[i] = ctx_float_to_u8 (cmyka[i]); }
-      color->valid |= CTX_VALID_CMYKA_U8;
+      CtxEntry *sentry = &drawlist->entries[source];
+      CtxEntry *tentry = &drawlist->entries[target];
+      while (sentry->code == CTX_NOP && source < drawlist->count)
+        {
+          source++;
+          sentry = &drawlist->entries[source];
+          removed++;
+        }
+      if (sentry != tentry)
+        { *tentry = *sentry; }
+      source ++;
+      target ++;
     }
-  out[0] = color->cmyka[0];
-  out[1] = color->cmyka[1];
-  out[2] = color->cmyka[2];
-  out[3] = color->cmyka[3];
-}
+  drawlist->count -= removed;
+  drawlist->bitpack_pos = drawlist->count;
 #endif
+}
+
 #endif
 
-void
-ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
+static void
+ctx_drawlist_compact (CtxDrawlist *drawlist)
 {
-  if (! (color->valid & CTX_VALID_RGBA_U8) )
-    {
-      float rgba[4];
-      ctx_color_get_drgba (state, color, rgba);
-      for (int i = 0; i < 4; i ++)
-        { color->rgba[i] = ctx_float_to_u8 (rgba[i]); }
-      color->valid |= CTX_VALID_RGBA_U8;
-    }
-  out[0] = color->rgba[0];
-  out[1] = color->rgba[1];
-  out[2] = color->rgba[2];
-  out[3] = color->rgba[3];
+#if CTX_BITPACK_PACKER
+  int last_history;
+  last_history = ctx_last_history (drawlist);
+#else
+  if (drawlist) {};
+#endif
+#if CTX_BITPACK_PACKER
+  ctx_drawlist_bitpack (drawlist, last_history);
+#endif
 }
 
-void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out)
+uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry)
 {
-  if (! (color->valid & CTX_VALID_GRAYA_U8) )
-    {
-      float graya[2];
-      ctx_color_get_graya (state, color, graya);
-      color->l_u8 = ctx_float_to_u8 (graya[0]);
-      color->rgba[3] = ctx_float_to_u8 (graya[1]);
-      color->valid |= CTX_VALID_GRAYA_U8;
-    }
-  out[0] = color->l_u8;
-  out[1] = color->rgba[3];
+  return &entry[2 + 1 + 1 + ctx_conts_for_entry (&entry[2])].data.u8[0];
 }
+#ifndef __CTX_TRANSFORM
+#define __CTX_TRANSFORM
 
-
-void
-ctx_get_rgba (Ctx *ctx, float *rgba)
+static void
+_ctx_user_to_device (CtxState *state, float *x, float *y)
 {
-  ctx_color_get_rgba (& (ctx->state), &ctx->state.gstate.source.color, rgba);
+  ctx_matrix_apply_transform (&state->gstate.transform, x, y);
 }
 
-void
-ctx_get_drgba (Ctx *ctx, float *rgba)
+static void
+_ctx_user_to_device_distance (CtxState *state, float *x, float *y)
 {
-  ctx_color_get_drgba (& (ctx->state), &ctx->state.gstate.source.color, rgba);
+  const CtxMatrix *m = &state->gstate.transform;
+  ctx_matrix_apply_transform (m, x, y);
+  *x -= m->m[2][0];
+  *y -= m->m[2][1];
 }
 
-int ctx_in_fill (Ctx *ctx, float x, float y)
+void ctx_user_to_device          (Ctx *ctx, float *x, float *y)
 {
-  float x1, y1, x2, y2;
-  ctx_path_extents (ctx, &x1, &y1, &x2, &y2);
-
-  if (x1 <= x && x <= x2 && // XXX - just bounding box for now
-      y1 <= y && y <= y2)   //
-    return 1;
-  return 0;
+  _ctx_user_to_device (&ctx->state, x, y);
 }
-
-
-#if CTX_ENABLE_CMYK
-void
-ctx_get_cmyka (Ctx *ctx, float *cmyka)
+void ctx_user_to_device_distance (Ctx *ctx, float *x, float *y)
 {
-  ctx_color_get_cmyka (& (ctx->state), &ctx->state.gstate.source.color, cmyka);
+  _ctx_user_to_device_distance (&ctx->state, x, y);
 }
-#endif
-void
-ctx_get_graya (Ctx *ctx, float *ya)
+
+static void
+ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f)
 {
-  ctx_color_get_graya (& (ctx->state), &ctx->state.gstate.source.color, ya);
+  matrix->m[0][0] = a;
+  matrix->m[0][1] = b;
+  matrix->m[1][0] = c;
+  matrix->m[1][1] = d;
+  matrix->m[2][0] = e;
+  matrix->m[2][1] = f;
 }
 
-void ctx_drgba (Ctx *ctx, float r, float g, float b, float a)
+void
+ctx_matrix_identity (CtxMatrix *matrix)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_DRGBA, r),
-    ctx_f (CTX_CONT, g, b),
-    ctx_f (CTX_CONT, a, 0)
-  };
-  ctx_process (ctx, command);
+  matrix->m[0][0] = 1.0f;
+  matrix->m[0][1] = 0.0f;
+  matrix->m[1][0] = 0.0f;
+  matrix->m[1][1] = 1.0f;
+  matrix->m[2][0] = 0.0f;
+  matrix->m[2][1] = 0.0f;
 }
 
-void ctx_rgba (Ctx *ctx, float r, float g, float b, float a)
+void
+ctx_matrix_multiply (CtxMatrix       *result,
+                     const CtxMatrix *t,
+                     const CtxMatrix *s)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, r),
-    ctx_f (CTX_CONT, g, b),
-    ctx_f (CTX_CONT, a, 0)
-  };
-  float rgba[4];
-  ctx_color_get_rgba (&ctx->state, &ctx->state.gstate.source.color, rgba);
-  if (rgba[0] == r && rgba[1] == g && rgba[2] == b && rgba[3] == a)
-     return;
-  ctx_process (ctx, command);
+  CtxMatrix r;
+  r.m[0][0] = t->m[0][0] * s->m[0][0] + t->m[0][1] * s->m[1][0];
+  r.m[0][1] = t->m[0][0] * s->m[0][1] + t->m[0][1] * s->m[1][1];
+  r.m[1][0] = t->m[1][0] * s->m[0][0] + t->m[1][1] * s->m[1][0];
+  r.m[1][1] = t->m[1][0] * s->m[0][1] + t->m[1][1] * s->m[1][1];
+  r.m[2][0] = t->m[2][0] * s->m[0][0] + t->m[2][1] * s->m[1][0] + s->m[2][0];
+  r.m[2][1] = t->m[2][0] * s->m[0][1] + t->m[2][1] * s->m[1][1] + s->m[2][1];
+  *result = r;
 }
 
-void ctx_rgb (Ctx *ctx, float   r, float   g, float   b)
+
+void
+ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
 {
-  ctx_rgba (ctx, r, g, b, 1.0f);
+  CtxMatrix transform;
+  transform.m[0][0] = 1.0f;
+  transform.m[0][1] = 0.0f;
+  transform.m[1][0] = 0.0f;
+  transform.m[1][1] = 1.0f;
+  transform.m[2][0] = x;
+  transform.m[2][1] = y;
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
-void ctx_gray (Ctx *ctx, float gray)
+void
+ctx_matrix_scale (CtxMatrix *matrix, float x, float y)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_GRAY, gray),
-    ctx_f (CTX_CONT, 1.0f, 0.0f),
-    ctx_f (CTX_CONT, 0.0f, 0.0f)
-  };
-  ctx_process (ctx, command);
+  CtxMatrix transform;
+  transform.m[0][0] = x;
+  transform.m[0][1] = 0.0f;
+  transform.m[1][0] = 0.0f;
+  transform.m[1][1] = y;
+  transform.m[2][0] = 0.0f;
+  transform.m[2][1] = 0.0f;
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
-#if CTX_ENABLE_CMYK
-void ctx_cmyk (Ctx *ctx, float c, float m, float y, float k)
+void
+ctx_matrix_rotate (CtxMatrix *matrix, float angle)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_CMYKA, c),
-    ctx_f (CTX_CONT, m, y),
-    ctx_f (CTX_CONT, k, 1.0f)
-  };
-  ctx_process (ctx, command);
+  CtxMatrix transform;
+  float val_sin = ctx_sinf (angle);
+  float val_cos = ctx_cosf (angle);
+  transform.m[0][0] =  val_cos;
+  transform.m[0][1] = val_sin;
+  transform.m[1][0] = -val_sin;
+  transform.m[1][1] = val_cos;
+  transform.m[2][0] =     0.0f;
+  transform.m[2][1] = 0.0f;
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
-void ctx_cmyka      (Ctx *ctx, float c, float m, float y, float k, float a)
+#if 0
+static void
+ctx_matrix_skew_x (CtxMatrix *matrix, float angle)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_CMYKA, c),
-    ctx_f (CTX_CONT, m, y),
-    ctx_f (CTX_CONT, k, a)
-  };
-  ctx_process (ctx, command);
+  CtxMatrix transform;
+  float val_tan = ctx_tanf (angle);
+  transform.m[0][0] =    1.0f;
+  transform.m[0][1] = 0.0f;
+  transform.m[1][0] = val_tan;
+  transform.m[1][1] = 1.0f;
+  transform.m[2][0] =    0.0f;
+  transform.m[2][1] = 0.0f;
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
-void ctx_dcmyk (Ctx *ctx, float c, float m, float y, float k)
+static void
+ctx_matrix_skew_y (CtxMatrix *matrix, float angle)
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_COLOR, CTX_DCMYKA, c),
-    ctx_f (CTX_CONT, m, y),
-    ctx_f (CTX_CONT, k, 1.0f)
-  };
-  ctx_process (ctx, command);
+  CtxMatrix transform;
+  float val_tan = ctx_tanf (angle);
+  transform.m[0][0] =    1.0f;
+  transform.m[0][1] = val_tan;
+  transform.m[1][0] =    0.0f;
+  transform.m[1][1] = 1.0f;
+  transform.m[2][0] =    0.0f;
+  transform.m[2][1] = 0.0f;
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
+#endif
 
-void ctx_dcmyka (Ctx *ctx, float c, float m, float y, float k, float a)
+
+void
+ctx_identity (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_IDENTITY);
+}
+
+void
+ctx_apply_transform (Ctx *ctx, float a, float b,  // hscale, hskew
+                     float c, float d,  // vskew,  vscale
+                     float e, float f)  // htran,  vtran
 {
   CtxEntry command[3]=
   {
-    ctx_f (CTX_COLOR, CTX_DCMYKA, c),
-    ctx_f (CTX_CONT, m, y),
-    ctx_f (CTX_CONT, k, a)
+    ctx_f (CTX_APPLY_TRANSFORM, a, b),
+    ctx_f (CTX_CONT,            c, d),
+    ctx_f (CTX_CONT,            e, f)
   };
   ctx_process (ctx, command);
 }
 
-#endif
-
-/* XXX: missing CSS1:
- *
- *   EM { color: rgb(110%, 0%, 0%) }  // clipped to 100% 
- *
- *
- *   :first-letter
- *   :first-list
- *   :link :visited :active
- *
- */
+void
+ctx_get_transform  (Ctx *ctx, float *a, float *b,
+                    float *c, float *d,
+                    float *e, float *f)
+{
+  if (a) { *a = ctx->state.gstate.transform.m[0][0]; }
+  if (b) { *b = ctx->state.gstate.transform.m[0][1]; }
+  if (c) { *c = ctx->state.gstate.transform.m[1][0]; }
+  if (d) { *d = ctx->state.gstate.transform.m[1][1]; }
+  if (e) { *e = ctx->state.gstate.transform.m[2][0]; }
+  if (f) { *f = ctx->state.gstate.transform.m[2][1]; }
+}
 
-typedef struct ColorDef {
-  uint32_t name;
-  float r;
-  float g;
-  float b;
-  float a;
-} ColorDef;
+void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix)
+{
+  ctx_apply_transform (ctx,
+                       matrix->m[0][0], matrix->m[0][1],
+                       matrix->m[1][0], matrix->m[1][1],
+                       matrix->m[2][0], matrix->m[2][1]);
+}
 
-#define CTX_silver     CTX_STRH('s','i','l','v','e','r',0,0,0,0,0,0,0,0)
-#define CTX_fuchsia    CTX_STRH('f','u','c','h','s','i','a',0,0,0,0,0,0,0)
-#define CTX_gray       CTX_STRH('g','r','a','y',0,0,0,0,0,0,0,0,0,0)
-#define CTX_yellow     CTX_STRH('y','e','l','l','o','w',0,0,0,0,0,0,0,0)
-#define CTX_white      CTX_STRH('w','h','i','t','e',0,0,0,0,0,0,0,0,0)
-#define CTX_maroon     CTX_STRH('m','a','r','o','o','n',0,0,0,0,0,0,0,0)
-#define CTX_magenta    CTX_STRH('m','a','g','e','n','t','a',0,0,0,0,0,0,0)
-#define CTX_blue       CTX_STRH('b','l','u','e',0,0,0,0,0,0,0,0,0,0)
-#define CTX_green      CTX_STRH('g','r','e','e','n',0,0,0,0,0,0,0,0,0)
-#define CTX_red        CTX_STRH('r','e','d',0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_purple     CTX_STRH('p','u','r','p','l','e',0,0,0,0,0,0,0,0)
-#define CTX_olive      CTX_STRH('o','l','i','v','e',0,0,0,0,0,0,0,0,0)
-#define CTX_teal        CTX_STRH('t','e','a','l',0,0,0,0,0,0,0,0,0,0)
-#define CTX_black      CTX_STRH('b','l','a','c','k',0,0,0,0,0,0,0,0,0)
-#define CTX_cyan       CTX_STRH('c','y','a','n',0,0,0,0,0,0,0,0,0,0)
-#define CTX_navy       CTX_STRH('n','a','v','y',0,0,0,0,0,0,0,0,0,0)
-#define CTX_lime       CTX_STRH('l','i','m','e',0,0,0,0,0,0,0,0,0,0)
-#define CTX_aqua       CTX_STRH('a','q','u','a',0,0,0,0,0,0,0,0,0,0)
-#define CTX_transparent CTX_STRH('t','r','a','n','s','p','a','r','e','n','t',0,0,0)
+void ctx_get_matrix (Ctx *ctx, CtxMatrix *matrix)
+{
+  *matrix = ctx->state.gstate.transform;
+}
 
-static ColorDef colors[]={
-  {CTX_black,    0, 0, 0, 1},
-  {CTX_red,      1, 0, 0, 1},
-  {CTX_green,    0, 1, 0, 1},
-  {CTX_yellow,   1, 1, 0, 1},
-  {CTX_blue,     0, 0, 1, 1},
-  {CTX_fuchsia,  1, 0, 1, 1},
-  {CTX_cyan,     0, 1, 1, 1},
-  {CTX_white,    1, 1, 1, 1},
-  {CTX_silver,   0.75294, 0.75294, 0.75294, 1},
-  {CTX_gray,     0.50196, 0.50196, 0.50196, 1},
-  {CTX_magenta,  0.50196, 0, 0.50196, 1},
-  {CTX_maroon,   0.50196, 0, 0, 1},
-  {CTX_purple,   0.50196, 0, 0.50196, 1},
-  {CTX_green,    0, 0.50196, 0, 1},
-  {CTX_lime,     0, 1, 0, 1},
-  {CTX_olive,    0.50196, 0.50196, 0, 1},
-  {CTX_navy,     0, 0,      0.50196, 1},
-  {CTX_teal,     0, 0.50196, 0.50196, 1},
-  {CTX_aqua,     0, 1, 1, 1},
-  {CTX_transparent, 0, 0, 0, 0},
-  {CTX_none,     0, 0, 0, 0},
-};
+void ctx_set_matrix (Ctx *ctx, CtxMatrix *matrix)
+{
+  ctx_identity (ctx);
+  ctx_apply_matrix (ctx, matrix);
+}
 
-static int xdigit_value(const char xdigit)
+void ctx_rotate (Ctx *ctx, float x)
 {
-  if (xdigit >= '0' && xdigit <= '9')
-   return xdigit - '0';
-  switch (xdigit)
-  {
-    case 'A':case 'a': return 10;
-    case 'B':case 'b': return 11;
-    case 'C':case 'c': return 12;
-    case 'D':case 'd': return 13;
-    case 'E':case 'e': return 14;
-    case 'F':case 'f': return 15;
-  }
-  return 0;
+  if (x == 0.0f)
+    return;
+  CTX_PROCESS_F1 (CTX_ROTATE, x);
+  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
+    { ctx->drawlist.count--; }
 }
 
-static int
-ctx_color_parse_rgb (CtxState *ctxstate, CtxColor *color, const char *color_string)
+void ctx_scale (Ctx *ctx, float x, float y)
 {
-  float dcolor[4] = {0,0,0,1};
-  while (*color_string && *color_string != '(')
-    color_string++;
-  if (*color_string) color_string++;
+  if (x == 1.0f && y == 1.0f)
+    return;
+  CTX_PROCESS_F (CTX_SCALE, x, y);
+  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
+    { ctx->drawlist.count--; }
+}
 
-  {
-    int n_floats = 0;
-    char *p =    (char*)color_string;
-    char *prev = (char*)NULL;
-    for (; p && n_floats < 4 && p != prev && *p; )
-    {
-      float val;
-      prev = p;
-      val = _ctx_parse_float (p, &p);
-      if (p != prev)
-      {
-        if (n_floats < 3)
-          dcolor[n_floats++] = val/255.0;
-        else
-          dcolor[n_floats++] = val;
-
-        while (*p == ' ' || *p == ',')
-        {
-          p++;
-          prev++;
-        }
-      }
-    }
-  }
-  ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
-  return 0;
-}
-
-static int ctx_isxdigit (uint8_t ch)
+void ctx_translate (Ctx *ctx, float x, float y)
 {
-  if (ch >= '0' && ch <= '9') return 1;
-  if (ch >= 'a' && ch <= 'f') return 1;
-  if (ch >= 'A' && ch <= 'F') return 1;
-  return 0;
+  if (x == 0.0f && y == 0.0f)
+    return;
+  CTX_PROCESS_F (CTX_TRANSLATE, x, y);
+  if (ctx->transformation & CTX_TRANSFORMATION_SCREEN_SPACE)
+    { ctx->drawlist.count--; }
 }
 
-static int
-mrg_color_parse_hex (CtxState *ctxstate, CtxColor *color, const char *color_string)
+void
+ctx_matrix_invert (CtxMatrix *m)
 {
-  float dcolor[4]={0,0,0,1};
-  int string_length = strlen (color_string);
-  int i;
-  dcolor[3] = 1.0;
-
-  if (string_length == 7 ||  /* #rrggbb   */
-      string_length == 9)    /* #rrggbbaa */
-    {
-      int num_iterations = (string_length - 1) / 2;
-  
-      for (i = 0; i < num_iterations; ++i)
-        {
-          if (ctx_isxdigit (color_string[2 * i + 1]) &&
-              ctx_isxdigit (color_string[2 * i + 2]))
-            {
-              dcolor[i] = (xdigit_value (color_string[2 * i + 1]) << 4 |
-                           xdigit_value (color_string[2 * i + 2])) / 255.f;
-            }
-          else
-            {
-              return 0;
-            }
-        }
-      /* Successful #rrggbb(aa) parsing! */
-      ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
-      return 1;
-    }
-  else if (string_length == 4 ||  /* #rgb  */
-           string_length == 5)    /* #rgba */
+  CtxMatrix t = *m;
+  float invdet, det = m->m[0][0] * m->m[1][1] -
+                      m->m[1][0] * m->m[0][1];
+  if (det > -0.0000001f && det < 0.0000001f)
     {
-      int num_iterations = string_length - 1;
-      for (i = 0; i < num_iterations; ++i)
-        {
-          if (ctx_isxdigit (color_string[i + 1]))
-            {
-              dcolor[i] = (xdigit_value (color_string[i + 1]) << 4 |
-                           xdigit_value (color_string[i + 1])) / 255.f;
-            }
-          else
-            {
-              return 0;
-            }
-        }
-      ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
-      /* Successful #rgb(a) parsing! */
-      return 0;
+      m->m[0][0] = m->m[0][1] =
+                     m->m[1][0] = m->m[1][1] =
+                                    m->m[2][0] = m->m[2][1] = 0.0;
+      return;
     }
-  /* String was of unsupported length. */
-  return 1;
+  invdet = 1.0f / det;
+  m->m[0][0] = t.m[1][1] * invdet;
+  m->m[1][0] = -t.m[1][0] * invdet;
+  m->m[2][0] = (t.m[1][0] * t.m[2][1] - t.m[1][1] * t.m[2][0]) * invdet;
+  m->m[0][1] = -t.m[0][1] * invdet;
+  m->m[1][1] = t.m[0][0] * invdet;
+  m->m[2][1] = (t.m[0][1] * t.m[2][0] - t.m[0][0] * t.m[2][1]) * invdet ;
 }
 
-#define CTX_currentColor       CTX_STRH('c','u','r','r','e','n','t','C','o','l','o','r',0,0)
-
-int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string)
+void
+ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
 {
-  int i;
-  uint32_t hash = ctx_strhash (string, 0);
-//  ctx_color_set_rgba (&(ctx->state), color, 0.4,0.1,0.9,1.0);
-//  return 0;
-    //rgba[0], rgba[1], rgba[2], rgba[3]);
+  float x_in = *x;
+  float y_in = *y;
+  *x = ( (x_in * m->m[0][0]) + (y_in * m->m[1][0]) + m->m[2][0]);
+  *y = ( (y_in * m->m[1][1]) + (x_in * m->m[0][1]) + m->m[2][1]);
+}
 
-  if (hash == CTX_currentColor)
-  {
-    float rgba[4];
-    CtxColor ccolor;
-    ctx_get_color (ctx, CTX_color, &ccolor);
-    ctx_color_get_rgba (&(ctx->state), &ccolor, rgba);
-    ctx_color_set_rgba (&(ctx->state), color, rgba[0], rgba[1], rgba[2], rgba[3]);
-    return 0;
-  }
 
-  for (i = (sizeof(colors)/sizeof(colors[0]))-1; i>=0; i--)
-  {
-    if (hash == colors[i].name)
+#endif
+#ifndef __CTX_COLOR
+#define __CTX_COLOR
+
+int ctx_color_model_get_components (CtxColorModel model)
+{
+  switch (model)
     {
-      ctx_color_set_rgba (&(ctx->state), color,
-       colors[i].r, colors[i].g, colors[i].b, colors[i].a);
-      return 0;
+      case CTX_GRAY:
+        return 1;
+      case CTX_GRAYA:
+      case CTX_GRAYA_A:
+        return 1;
+      case CTX_RGB:
+      case CTX_LAB:
+      case CTX_LCH:
+      case CTX_DRGB:
+        return 3;
+      case CTX_CMYK:
+      case CTX_DCMYK:
+      case CTX_LABA:
+      case CTX_LCHA:
+      case CTX_RGBA:
+      case CTX_DRGBA:
+      case CTX_RGBA_A:
+      case CTX_RGBA_A_DEVICE:
+        return 4;
+      case CTX_DCMYKA:
+      case CTX_CMYKA:
+      case CTX_CMYKA_A:
+      case CTX_DCMYKA_A:
+        return 5;
     }
-  }
-
-  if (string[0] == '#')
-    mrg_color_parse_hex (&(ctx->state), color, string);
-  else if (string[0] == 'r' &&
-      string[1] == 'g' &&
-      string[2] == 'b'
-      )
-    ctx_color_parse_rgb (&(ctx->state), color, string);
-
   return 0;
 }
 
-int ctx_color (Ctx *ctx, const char *string)
+#if 0
+inline static float ctx_u8_to_float (uint8_t val_u8)
 {
-  CtxColor color = {0,};
-  ctx_color_set_from_string (ctx, &color, string);
-  float rgba[4];
-  ctx_color_get_rgba (&(ctx->state), &color, rgba);
-  ctx_rgba (ctx, rgba[0],rgba[1],rgba[2],rgba[3]);
-  return 0;
+  float val_f = val_u8 / 255.0;
+  return val_f;
 }
-
-void
-ctx_rgba8 (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
-#if 0
-  CtxEntry command = ctx_u8 (CTX_SET_RGBA_U8, r, g, b, a, 0, 0, 0, 0);
-
-  uint8_t rgba[4];
-  ctx_color_get_rgba8 (&ctx->state, &ctx->state.gstate.source.color, rgba);
-  if (rgba[0] == r && rgba[1] == g && rgba[2] == b && rgba[3] == a)
-     return;
-
-  ctx_process (ctx, &command);
 #else
-  ctx_rgba (ctx, r/255.0f, g/255.0f, b/255.0f, a/255.0f);
+float ctx_u8_float[256];
 #endif
-}
 
-#endif 
+CtxColor *ctx_color_new ()
+{
+  CtxColor *color = (CtxColor*)ctx_calloc (sizeof (CtxColor), 1);
+  return color;
+}
 
-#if CTX_BABL
-void ctx_rasterizer_colorspace_babl (CtxState      *state,
-                                     CtxColorSpace  space_slot,
-                                     const Babl    *space)
+int ctx_color_is_transparent (CtxColor *color)
 {
-  switch (space_slot)
-  {
-    case CTX_COLOR_SPACE_DEVICE_RGB:
-      state->gstate.device_space = space;
-      break;
-    case CTX_COLOR_SPACE_DEVICE_CMYK:
-      state->gstate.device_space = space;
-      break;
-    case CTX_COLOR_SPACE_USER_RGB:
-      state->gstate.rgb_space = space;
-      break;
-    case CTX_COLOR_SPACE_USER_CMYK:
-      state->gstate.cmyk_space = space;
-      break;
-  }
+  return color->alpha <= 0.001f;
+}
 
 
-  if (!state->gstate.device_space) 
-       state->gstate.device_space = babl_space ("sRGB");
-  if (!state->gstate.rgb_space) 
-       state->gstate.rgb_space = babl_space ("sRGB");
+void ctx_color_free (CtxColor *color)
+{
+  free (color);
+}
 
-  //fprintf (stderr, "%s\n", babl_get_name (state->gstate.device_space));
+static void ctx_color_set_RGBA8 (CtxState *state, CtxColor *color, uint8_t r, uint8_t g, uint8_t b, uint8_t 
a)
+{
+  color->original = color->valid = CTX_VALID_RGBA_U8;
+  color->rgba[0] = r;
+  color->rgba[1] = g;
+  color->rgba[2] = b;
+  color->rgba[3] = a;
+#if CTX_ENABLE_CM
+  color->space = state->gstate.device_space;
+#endif
+}
 
-  state->gstate.fish_rgbaf_device_to_user = babl_fish (
-       babl_format_with_space ("R'G'B'A float", state->gstate.device_space),
-       babl_format_with_space ("R'G'B'A float", state->gstate.rgb_space));
-  state->gstate.fish_rgbaf_user_to_device = babl_fish (
-       babl_format_with_space ("R'G'B'A float", state->gstate.rgb_space),
-       babl_format_with_space ("R'G'B'A float", state->gstate.device_space));
+#if 0
+static void ctx_color_set_RGBA8_ (CtxColor *color, const uint8_t *in)
+{
+  ctx_color_set_RGBA8 (color, in[0], in[1], in[2], in[3]);
 }
 #endif
 
-void ctx_rasterizer_colorspace_icc (CtxState      *state,
-                                    CtxColorSpace  space_slot,
-                                    char          *icc_data,
-                                    int            icc_length)
+static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, float alpha)
 {
-#if CTX_BABL
-   const char *error = NULL;
-   const Babl *space = NULL;
-   if (icc_data == NULL) space = babl_space ("sRGB");
-   if (!space && !strcmp (icc_data, "sRGB"))       space = babl_space ("sRGB");
-   if (!space && !strcmp (icc_data, "ACEScg"))     space = babl_space ("ACEScg");
-   if (!space && !strcmp (icc_data, "Adobish"))    space = babl_space ("Adobish");
-   if (!space && !strcmp (icc_data, "Apple"))      space = babl_space ("Apple");
-   if (!space && !strcmp (icc_data, "Rec2020"))    space = babl_space ("Rec2020");
-   if (!space && !strcmp (icc_data, "ACES2065-1")) space = babl_space ("ACES2065-1");
-
-   if (!space)
-   {
-     space = babl_space_from_icc (icc_data, icc_length, BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error);
-   }
-   if (space)
-   {
-     ctx_rasterizer_colorspace_babl (state, space_slot, space);
-   }
-#endif
+  color->original = color->valid = CTX_VALID_GRAYA;
+  color->l = gray;
+  color->alpha = alpha;
 }
-
-void ctx_colorspace (Ctx           *ctx,
-                     CtxColorSpace  space_slot,
-                     unsigned char *data,
-                     int            data_length)
+#if 0
+static void ctx_color_set_graya_ (CtxColor *color, const float *in)
 {
-  if (data)
-  {
-    ctx_process_cmd_str_with_len (ctx, CTX_COLOR_SPACE, (char*)data, space_slot, 0, data_length);
-  }
-  else
-  {
-    ctx_process_cmd_str_with_len (ctx, CTX_COLOR_SPACE, "sRGB", space_slot, 0, 4);
-  }
+  return ctx_color_set_graya (color, in[0], in[1]);
 }
+#endif
 
-//  deviceRGB .. settable when creating an RGB image surface..
-//               queryable when running in terminal - is it really needed?
-//               though it is also settable.. 
-//
-//  userRGB - settable at any time, stored in save|restore 
-
-float ctx_state_get (CtxState *state, uint32_t hash)
+void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a)
 {
-  for (int i = state->gstate.keydb_pos-1; i>=0; i--)
-    {
-      if (state->keydb[i].key == hash)
-        { return state->keydb[i].value; }
-    }
-  return -0.0;
+#if CTX_ENABLE_CM
+  color->original = color->valid = CTX_VALID_RGBA;
+  color->red      = r;
+  color->green    = g;
+  color->blue     = b;
+  color->space    = state->gstate.rgb_space;
+#else
+  color->original     = color->valid = CTX_VALID_RGBA_DEVICE;
+  color->device_red   = r;
+  color->device_green = g;
+  color->device_blue  = b;
+#endif
+  color->alpha        = a;
 }
 
-void ctx_state_set (CtxState *state, uint32_t key, float value)
+static void ctx_color_set_drgba (CtxState *state, CtxColor *color, float r, float g, float b, float a)
 {
-  if (key != CTX_new_state)
-    {
-      if (ctx_state_get (state, key) == value)
-        { return; }
-      for (int i = state->gstate.keydb_pos-1;
-           state->keydb[i].key != CTX_new_state && i >=0;
-           i--)
-        {
-          if (state->keydb[i].key == key)
-            {
-              state->keydb[i].value = value;
-              return;
-            }
-        }
-    }
-  if (state->gstate.keydb_pos >= CTX_MAX_KEYDB)
-    { return; }
-  state->keydb[state->gstate.keydb_pos].key = key;
-  state->keydb[state->gstate.keydb_pos].value = value;
-  state->gstate.keydb_pos++;
+#if CTX_ENABLE_CM
+  color->original     = color->valid = CTX_VALID_RGBA_DEVICE;
+  color->device_red   = r;
+  color->device_green = g;
+  color->device_blue  = b;
+  color->alpha        = a;
+  color->space        = state->gstate.device_space;
+#else
+  ctx_color_set_rgba (state, color, r, g, b, a);
+#endif
 }
 
-
-#define CTX_KEYDB_STRING_START (-90000.0)
-#define CTX_KEYDB_STRING_END   (CTX_KEYDB_STRING_START + CTX_STRINGPOOL_SIZE)
-
-static int ctx_float_is_string (float val)
+#if 0
+static void ctx_color_set_rgba_ (CtxState *state, CtxColor *color, const float *in)
 {
-  return val >= CTX_KEYDB_STRING_START && val <= CTX_KEYDB_STRING_END;
+  ctx_color_set_rgba (color, in[0], in[1], in[2], in[3]);
 }
+#endif
 
-static int ctx_float_to_string_index (float val)
+/* the baseline conversions we have whether CMYK support is enabled or not,
+ * providing an effort at right rendering
+ */
+static void ctx_cmyk_to_rgb (float c, float m, float y, float k, float *r, float *g, float *b)
 {
-  int idx = -1;
-  if (ctx_float_is_string (val))
-  {
-    idx = val - CTX_KEYDB_STRING_START;
-  }
-  return idx;
+  *r = (1.0f-c) * (1.0f-k);
+  *g = (1.0f-m) * (1.0f-k);
+  *b = (1.0f-y) * (1.0f-k);
 }
 
-static float ctx_string_index_to_float (int index)
+void ctx_rgb_to_cmyk (float r, float g, float b,
+                      float *c_out, float *m_out, float *y_out, float *k_out)
 {
-  return CTX_KEYDB_STRING_START + index;
+  float c = 1.0f - r;
+  float m = 1.0f - g;
+  float y = 1.0f - b;
+  float k = ctx_minf (c, ctx_minf (y, m) );
+  if (k < 1.0f)
+    {
+      c = (c - k) / (1.0f - k);
+      m = (m - k) / (1.0f - k);
+      y = (y - k) / (1.0f - k);
+    }
+  else
+    {
+      c = m = y = 0.0f;
+    }
+  *c_out = c;
+  *m_out = m;
+  *y_out = y;
+  *k_out = k;
 }
 
-void *ctx_state_get_blob (CtxState *state, uint32_t key)
+#if CTX_ENABLE_CMYK
+static void ctx_color_set_cmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float 
a)
 {
-  float stored = ctx_state_get (state, key);
-  int idx = ctx_float_to_string_index (stored);
-  if (idx >= 0)
-  {
-     // can we know length?
-     return &state->stringpool[idx];
-  }
-
-  // format number as string?
-  return NULL;
+  color->original = color->valid = CTX_VALID_CMYKA;
+  color->cyan     = c;
+  color->magenta  = m;
+  color->yellow   = y;
+  color->key      = k;
+  color->alpha    = a;
+#if CTX_ENABLE_CM
+  color->space    = state->gstate.cmyk_space;
+#endif
 }
 
-const char *ctx_state_get_string (CtxState *state, uint32_t key)
+static void ctx_color_set_dcmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, 
float a)
 {
-  const char *ret = (char*)ctx_state_get_blob (state, key);
-  if (ret && ret[0] == 127)
-    return NULL;
-  return ret;
+  color->original       = color->valid = CTX_VALID_DCMYKA;
+  color->device_cyan    = c;
+  color->device_magenta = m;
+  color->device_yellow  = y;
+  color->device_key     = k;
+  color->alpha          = a;
+#if CTX_ENABLE_CM
+  color->space = state->gstate.device_space;
+#endif
 }
 
+#endif
 
-static void ctx_state_set_blob (CtxState *state, uint32_t key, uint8_t *data, int len)
-{
-  int idx = state->gstate.stringpool_pos;
+#if CTX_ENABLE_CM
 
-  if (idx + len > CTX_STRINGPOOL_SIZE)
-  {
-    ctx_log ("blowing varpool size [%c..]\n", data[0]);
-    //fprintf (stderr, "blowing varpool size [%c%c%c..]\n", data[0],data[1], data[1]?data[2]:0);
+static void ctx_rgb_user_to_device (CtxState *state, float rin, float gin, float bin,
+                                    float *rout, float *gout, float *bout)
+{
+#if CTX_BABL
 #if 0
-    for (int i = 0; i< CTX_STRINGPOOL_SIZE; i++)
-    {
-       if (i==0) fprintf (stderr, "\n%i ", i);
-       else      fprintf (stderr, "%c", state->stringpool[i]);
-    }
+  fprintf (stderr, "-[%p %p\n",
+    state->gstate.fish_rgbaf_user_to_device,
+    state->gstate.fish_rgbaf_device_to_user);
 #endif
+  if (state->gstate.fish_rgbaf_user_to_device)
+  {
+    float rgbaf[4]={rin,gin,bin,1.0};
+    float rgbafo[4];
+    babl_process (state->gstate.fish_rgbaf_user_to_device,
+                  rgbaf, rgbafo, 1);
+
+    *rout = rgbafo[0];
+    *gout = rgbafo[1];
+    *bout = rgbafo[2];
     return;
   }
-
-  memcpy (&state->stringpool[idx], data, len);
-  state->gstate.stringpool_pos+=len;
-  state->stringpool[state->gstate.stringpool_pos++]=0;
-  ctx_state_set (state, key, ctx_string_index_to_float (idx));
+#endif
+  *rout = rin;
+  *gout = gin;
+  *bout = bin;
 }
 
-static void ctx_state_set_string (CtxState *state, uint32_t key, const char *string)
+static void ctx_rgb_device_to_user (CtxState *state, float rin, float gin, float bin,
+                                    float *rout, float *gout, float *bout)
 {
-  float old_val = ctx_state_get (state, key);
-  int   old_idx = ctx_float_to_string_index (old_val);
-
-  if (old_idx >= 0)
+#if CTX_BABL
+#if 0
+  fprintf (stderr, "=[%p %p\n",
+    state->gstate.fish_rgbaf_user_to_device,
+    state->gstate.fish_rgbaf_device_to_user);
+#endif
+  if (state->gstate.fish_rgbaf_device_to_user)
   {
-    const char *old_string = ctx_state_get_string (state, key);
-    if (old_string && !strcmp (old_string, string))
-      return;
-  }
+    float rgbaf[4]={rin,gin,bin,1.0};
+    float rgbafo[4];
+    babl_process (state->gstate.fish_rgbaf_device_to_user,
+                  rgbaf, rgbafo, 1);
 
-  if (ctx_str_is_number (string))
-  {
-    ctx_state_set (state, key, strtod (string, NULL));
+    *rout = rgbafo[0];
+    *gout = rgbafo[1];
+    *bout = rgbafo[2];
     return;
   }
-  // should do same with color
- 
-  // XXX should special case when the string modified is at the
-  //     end of the stringpool.
-  //
-  //     for clips the behavior is howevre ideal, since
-  //     we can have more than one clip per save/restore level
-  ctx_state_set_blob (state, key, (uint8_t*)string, strlen(string));
+#endif
+  *rout = rin;
+  *gout = gin;
+  *bout = bin;
 }
+#endif
 
-static int ctx_state_get_color (CtxState *state, uint32_t key, CtxColor *color)
+static void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out)
 {
-  CtxColor *stored = (CtxColor*)ctx_state_get_blob (state, key);
-  if (stored)
-  {
-    if (stored->magic == 127)
+  if (! (color->valid & CTX_VALID_RGBA_DEVICE) )
     {
-      *color = *stored;
-      return 0;
+#if CTX_ENABLE_CM
+      if (color->valid & CTX_VALID_RGBA)
+        {
+          ctx_rgb_user_to_device (state, color->red, color->green, color->blue,
+                                  & (color->device_red), & (color->device_green), & (color->device_blue) );
+        }
+      else
+#endif
+        if (color->valid & CTX_VALID_RGBA_U8)
+          {
+            float red = ctx_u8_to_float (color->rgba[0]);
+            float green = ctx_u8_to_float (color->rgba[1]);
+            float blue = ctx_u8_to_float (color->rgba[2]);
+#if CTX_ENABLE_CM
+            ctx_rgb_user_to_device (state, red, green, blue,
+                                  & (color->device_red), & (color->device_green), & (color->device_blue) );
+#else
+            color->device_red = red;
+            color->device_green = green;
+            color->device_blue = blue;
+#endif
+            color->alpha        = ctx_u8_to_float (color->rgba[3]);
+          }
+#if CTX_ENABLE_CMYK
+        else if (color->valid & CTX_VALID_CMYKA)
+          {
+            ctx_cmyk_to_rgb (color->cyan, color->magenta, color->yellow, color->key,
+                             &color->device_red,
+                             &color->device_green,
+                             &color->device_blue);
+          }
+#endif
+        else if (color->valid & CTX_VALID_GRAYA)
+          {
+            color->device_red   =
+              color->device_green =
+                color->device_blue  = color->l;
+          }
+      color->valid |= CTX_VALID_RGBA_DEVICE;
     }
-  }
-  return -1;
+  out[0] = color->device_red;
+  out[1] = color->device_green;
+  out[2] = color->device_blue;
+  out[3] = color->alpha;
 }
 
-static void ctx_state_set_color (CtxState *state, uint32_t key, CtxColor *color)
+void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
 {
-  CtxColor mod_color;
-  CtxColor old_color;
-  mod_color = *color;
-  mod_color.magic = 127;
-  if (ctx_state_get_color (state, key, &old_color)==0)
-  {
-    if (!memcmp (&mod_color, &old_color, sizeof (mod_color)))
-      return;
-  }
-  ctx_state_set_blob (state, key, (uint8_t*)&mod_color, sizeof (CtxColor));
+#if CTX_ENABLE_CM
+  if (! (color->valid & CTX_VALID_RGBA) )
+    {
+      ctx_color_get_drgba (state, color, out);
+      if (color->valid & CTX_VALID_RGBA_DEVICE)
+        {
+          ctx_rgb_device_to_user (state, color->device_red, color->device_green, color->device_blue,
+                                  & (color->red), & (color->green), & (color->blue) );
+        }
+      color->valid |= CTX_VALID_RGBA;
+    }
+  out[0] = color->red;
+  out[1] = color->green;
+  out[2] = color->blue;
+  out[3] = color->alpha;
+#else
+  ctx_color_get_drgba (state, color, out);
+#endif
 }
 
-const char *ctx_get_string (Ctx *ctx, uint32_t hash)
+
+float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb)
 {
-  return ctx_state_get_string (&ctx->state, hash);
+        // XXX todo replace with correct according to primaries
+  return CTX_CSS_RGB_TO_LUMINANCE(rgb);
 }
-float ctx_get_float (Ctx *ctx, uint32_t hash)
+uint8_t ctx_u8_color_rgb_to_gray (CtxState *state, const uint8_t *rgb)
 {
-  return ctx_state_get (&ctx->state, hash);
+        // XXX todo replace with correct according to primaries
+  return CTX_CSS_RGB_TO_LUMINANCE(rgb);
 }
-int ctx_get_int (Ctx *ctx, uint32_t hash)
+
+void ctx_color_get_graya (CtxState *state, CtxColor *color, float *out)
 {
-  return ctx_state_get (&ctx->state, hash);
+  if (! (color->valid & CTX_VALID_GRAYA) )
+    {
+      float rgba[4];
+      ctx_color_get_drgba (state, color, rgba);
+      color->l = ctx_float_color_rgb_to_gray (state, rgba);
+      color->valid |= CTX_VALID_GRAYA;
+    }
+  out[0] = color->l;
+  out[1] = color->alpha;
 }
-void ctx_set_float (Ctx *ctx, uint32_t hash, float value)
+
+#if CTX_ENABLE_CMYK
+void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out)
 {
-  ctx_state_set (&ctx->state, hash, value);
+  if (! (color->valid & CTX_VALID_CMYKA) )
+    {
+      if (color->valid & CTX_VALID_GRAYA)
+        {
+          color->cyan = color->magenta = color->yellow = 0.0;
+          color->key = color->l;
+        }
+      else
+        {
+          float rgba[4];
+          ctx_color_get_rgba (state, color, rgba);
+          ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2],
+                           &color->cyan, &color->magenta, &color->yellow, &color->key);
+          color->alpha = rgba[3];
+        }
+      color->valid |= CTX_VALID_CMYKA;
+    }
+  out[0] = color->cyan;
+  out[1] = color->magenta;
+  out[2] = color->yellow;
+  out[3] = color->key;
+  out[4] = color->alpha;
 }
-void ctx_set_string (Ctx *ctx, uint32_t hash, const char *value)
+
+#if 0
+static void ctx_color_get_cmyka_u8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
-  ctx_state_set_string (&ctx->state, hash, value);
+  if (! (color->valid & CTX_VALID_CMYKA_U8) )
+    {
+      float cmyka[5];
+      ctx_color_get_cmyka (color, cmyka);
+      for (int i = 0; i < 5; i ++)
+        { color->cmyka[i] = ctx_float_to_u8 (cmyka[i]); }
+      color->valid |= CTX_VALID_CMYKA_U8;
+    }
+  out[0] = color->cmyka[0];
+  out[1] = color->cmyka[1];
+  out[2] = color->cmyka[2];
+  out[3] = color->cmyka[3];
 }
-void ctx_set_color (Ctx *ctx, uint32_t hash, CtxColor *color)
+#endif
+#endif
+
+void
+ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
-  ctx_state_set_color (&ctx->state, hash, color);
+  if (! (color->valid & CTX_VALID_RGBA_U8) )
+    {
+      float rgba[4];
+      ctx_color_get_drgba (state, color, rgba);
+      for (int i = 0; i < 4; i ++)
+        { color->rgba[i] = ctx_float_to_u8 (rgba[i]); }
+      color->valid |= CTX_VALID_RGBA_U8;
+    }
+  out[0] = color->rgba[0];
+  out[1] = color->rgba[1];
+  out[2] = color->rgba[2];
+  out[3] = color->rgba[3];
 }
-int  ctx_get_color (Ctx *ctx, uint32_t hash, CtxColor *color)
+
+void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
-  return ctx_state_get_color (&ctx->state, hash, color);
+  if (! (color->valid & CTX_VALID_GRAYA_U8) )
+    {
+      float graya[2];
+      ctx_color_get_graya (state, color, graya);
+      color->l_u8 = ctx_float_to_u8 (graya[0]);
+      color->rgba[3] = ctx_float_to_u8 (graya[1]);
+      color->valid |= CTX_VALID_GRAYA_U8;
+    }
+  out[0] = color->l_u8;
+  out[1] = color->rgba[3];
 }
-int ctx_is_set (Ctx *ctx, uint32_t hash)
+
+#if 0
+void
+ctx_get_rgba (Ctx *ctx, float *rgba)
 {
-  return ctx_get_float (ctx, hash) != -0.0f;
+  ctx_color_get_rgba (& (ctx->state), &ctx->state.gstate.source.color, rgba);
 }
-int ctx_is_set_now (Ctx *ctx, uint32_t hash)
+
+void
+ctx_get_drgba (Ctx *ctx, float *rgba)
 {
-  return ctx_is_set (ctx, hash);
+  ctx_color_get_drgba (& (ctx->state), &ctx->state.gstate.source.color, rgba);
 }
-#if CTX_RASTERIZER
-
-void ctx_compositor_setup_default (CtxRasterizer *rasterizer);
+#endif
 
-inline static void
-ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
-                               uint8_t * __restrict__ dst,
-                               int            x,
-                               uint8_t * __restrict__ coverage,
-                               int            count)
+int ctx_in_fill (Ctx *ctx, float x, float y)
 {
-  if (rasterizer->format->apply_coverage)
-    rasterizer->format->apply_coverage(rasterizer, dst, rasterizer->color, x, coverage, count);
-  else
-    rasterizer->comp_op (rasterizer, dst, rasterizer->color, x, coverage, count);
+  float x1, y1, x2, y2;
+  ctx_path_extents (ctx, &x1, &y1, &x2, &y2);
+
+  if (x1 <= x && x <= x2 && // XXX - just bounding box for now
+      y1 <= y && y <= y2)   //
+    return 1;
+  return 0;
 }
 
-static void
-ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
+
+#if CTX_ENABLE_CMYK
+#if 0
+void
+ctx_get_cmyka (Ctx *ctx, float *cmyka)
 {
-  CtxGradient *gradient = &rasterizer->state->gradient;
-  CtxGradientStop *stop = &gradient->stops[gradient->n_stops];
-  stop->pos = pos;
-  ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]);
-  if (gradient->n_stops < 15) //we'll keep overwriting the last when out of stops
-    { gradient->n_stops++; }
+  ctx_color_get_cmyka (& (ctx->state), &ctx->state.gstate.source.color, cmyka);
 }
-
-static int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
+#endif
+#endif
+#if 0
+void
+ctx_get_graya (Ctx *ctx, float *ya)
 {
-  CtxEntry entry = {CTX_EDGE, {{0},}};
-  if (y1 < rasterizer->scan_min)
-    { rasterizer->scan_min = y1; }
-  if (y1 > rasterizer->scan_max)
-    { rasterizer->scan_max = y1; }
+  ctx_color_get_graya (& (ctx->state), &ctx->state.gstate.source.color, ya);
+}
+#endif
 
-  if (x1 < rasterizer->col_min)
-    { rasterizer->col_min = x1; }
-  if (x1 > rasterizer->col_max)
-    { rasterizer->col_max = x1; }
+void ctx_color_raw (Ctx *ctx, CtxColorModel model, float *components, int stroke)
+{
+  if (model == CTX_RGB || model == CTX_RGBA)
+  {
+    float rgba[4];
+  // XXX it should be possible to disable this, to get a more accurate record
+  // when it is intentional
+    float a = 1.0f;
+    if (model == CTX_RGBA) a = components[3];
+  if (stroke)
+    ctx_color_get_rgba (&ctx->state, &ctx->state.gstate.source_stroke.color, rgba);
+  else
+    ctx_color_get_rgba (&ctx->state, &ctx->state.gstate.source_fill.color, rgba);
+  if (rgba[0] == components[0] && rgba[1] == components[1] && rgba[2] == components[2] && rgba[3] == a)
+     return;
+  }
 
-  entry.data.s16[2]=x1;
-  entry.data.s16[3]=y1;
-  return ctx_drawlist_add_single (&rasterizer->edge_list, &entry);
+  CtxEntry command[3]= {
+  ctx_f (CTX_COLOR, model + stroke * 512, 0)
+  };
+  switch (model)
+  {
+    case CTX_RGBA:
+    case CTX_RGBA_A:
+    case CTX_RGBA_A_DEVICE:
+    case CTX_DRGBA:
+    case CTX_LABA:
+    case CTX_LCHA:
+      command[2].data.f[0]=components[3];
+      /*FALLTHROUGH*/
+    case CTX_RGB:
+    case CTX_LAB:
+    case CTX_LCH:
+    case CTX_DRGB:
+      command[0].data.f[1]=components[0];
+      command[1].data.f[0]=components[1];
+      command[1].data.f[1]=components[2];
+      break;
+    case CTX_DCMYKA:
+    case CTX_CMYKA:
+    case CTX_DCMYKA_A:
+    case CTX_CMYKA_A:
+      command[2].data.f[1]=components[4];
+      /*FALLTHROUGH*/
+    case CTX_CMYK:
+    case CTX_DCMYK:
+      command[0].data.f[1]=components[0];
+      command[1].data.f[0]=components[1];
+      command[1].data.f[1]=components[2];
+      command[2].data.f[0]=components[3];
+      break;
+    case CTX_GRAYA:
+    case CTX_GRAYA_A:
+      command[1].data.f[0]=components[1];
+      /*FALLTHROUGH*/
+    case CTX_GRAY:
+      command[0].data.f[1]=components[0];
+      break;
+  }
+  ctx_process (ctx, command);
 }
 
-#if 0
-#define CTX_SHAPE_CACHE_PRIME1   7853
-#define CTX_SHAPE_CACHE_PRIME2   4129
-#define CTX_SHAPE_CACHE_PRIME3   3371
-#define CTX_SHAPE_CACHE_PRIME4   4221
-#else
-#define CTX_SHAPE_CACHE_PRIME1   283
-#define CTX_SHAPE_CACHE_PRIME2   599
-#define CTX_SHAPE_CACHE_PRIME3   101
-#define CTX_SHAPE_CACHE_PRIME4   661
-#endif
-
-float ctx_shape_cache_rate = 0.0;
-#if CTX_SHAPE_CACHE
-
-//static CtxShapeCache ctx_cache = {{NULL,}, 0};
-
-static long hits = 0;
-static long misses = 0;
-
-
-/* this returns the buffer to use for rendering, it always
-   succeeds..
- */
-static CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int height)
+void ctx_rgba (Ctx *ctx, float r, float g, float b, float a)
 {
-  /* use both some high and some low bits  */
-  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
-  int i;
-  {
-    static int i = 0;
-    i++;
-    if (i>1000)
-      {
-        ctx_shape_cache_rate = hits * 100.0  / (hits+misses);
-        i = 0;
-        hits = 0;
-        misses = 0;
-      }
-  }
-// XXX : this 1 one is needed  to silence a false positive:
-// ==90718== Invalid write of size 1
-// ==90718==    at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786)
-// ==90718==    by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907)
-//
-  int size = sizeof (CtxShapeEntry) + width * height + 1;
-
-  i = entry_no;
-  if (rasterizer->shape_cache.entries[i])
-    {
-      CtxShapeEntry *entry = rasterizer->shape_cache.entries[i];
-      int old_size = sizeof (CtxShapeEntry) + width + height + 1;
-      if (entry->hash == hash &&
-          entry->width == width &&
-          entry->height == height)
-        {
-          if (entry->uses < 1<<30)
-            { entry->uses++; }
-          hits ++;
-          return entry;
-        }
-
-      if (old_size >= size)
-      {
-      }
-      else
-      {
-        rasterizer->shape_cache.entries[i] = NULL;
-        rasterizer->shape_cache.size -= entry->width * entry->height;
-        rasterizer->shape_cache.size -= sizeof (CtxShapeEntry);
-        free (entry);
-        rasterizer->shape_cache.entries[i] =(CtxShapeEntry *) calloc (size, 1);
-      }
-    }
-  else
-    {
-        rasterizer->shape_cache.entries[i] =(CtxShapeEntry *) calloc (size, 1);
-    }
+  float components[4]={r,g,b,a};
+  ctx_color_raw (ctx, CTX_RGBA, components, 0);
+}
 
-  misses ++;
-  
-  rasterizer->shape_cache.size += size;
-  rasterizer->shape_cache.entries[i]->hash=hash;
-  rasterizer->shape_cache.entries[i]->width=width;
-  rasterizer->shape_cache.entries[i]->height=height;
-  rasterizer->shape_cache.entries[i]->uses = 0;
-  return rasterizer->shape_cache.entries[i];
+void ctx_rgba_stroke (Ctx *ctx, float r, float g, float b, float a)
+{
+  float components[4]={r,g,b,a};
+  ctx_color_raw (ctx, CTX_RGBA, components, 1);
 }
 
-#endif
+void ctx_rgb (Ctx *ctx, float   r, float   g, float   b)
+{
+  ctx_rgba (ctx, r, g, b, 1.0f);
+}
 
-static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
+void ctx_rgb_stroke (Ctx *ctx, float   r, float   g, float   b)
 {
-  int16_t x = 0;
-  int16_t y = 0;
+  ctx_rgba_stroke (ctx, r, g, b, 1.0f);
+}
 
-  CtxEntry *entry = &rasterizer->edge_list.entries[0];
-  int ox = entry->data.s16[2];
-  int oy = entry->data.s16[3];
-  uint32_t hash = rasterizer->edge_list.count;
-  hash = ox;//(ox % CTX_SUBDIV);
-  hash *= CTX_SHAPE_CACHE_PRIME1;
-  hash += oy; //(oy % CTX_RASTERIZER_AA);
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
-    {
-      CtxEntry *entry = &rasterizer->edge_list.entries[i];
-      x = entry->data.s16[2];
-      y = entry->data.s16[3];
-      int dx = x-ox;
-      int dy = y-oy;
-      ox = x;
-      oy = y;
-      hash *= CTX_SHAPE_CACHE_PRIME3;
-      hash += dx;
-      hash *= CTX_SHAPE_CACHE_PRIME4;
-      hash += dy;
-    }
-  return hash;
+void ctx_gray_stroke   (Ctx *ctx, float gray)
+{
+  ctx_color_raw (ctx, CTX_GRAY, &gray, 1);
+}
+void ctx_gray (Ctx *ctx, float gray)
+{
+  ctx_color_raw (ctx, CTX_GRAY, &gray, 0);
 }
 
-static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
+void ctx_drgba_stroke (Ctx *ctx, float r, float g, float b, float a)
 {
-  int16_t x = 0;
-  int16_t y = 0;
-  if (rasterizer->edge_list.count == 0)
-     return 0;
-#if CTX_SHAPE_CACHE
-  int aa = rasterizer->aa;
-  CtxEntry *entry = &rasterizer->edge_list.entries[0];
-  int ox = entry->data.s16[2];
-  int oy = entry->data.s16[3];
-  uint32_t hash = rasterizer->edge_list.count;
-  hash = (ox % CTX_SUBDIV);
-  hash *= CTX_SHAPE_CACHE_PRIME1;
-  hash += (oy % aa);
-#endif
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
-    {
-      CtxEntry *entry = &rasterizer->edge_list.entries[i];
-      if (entry->code == CTX_NEW_EDGE)
-        {
-          entry->code = CTX_EDGE;
-#if CTX_SHAPE_CACHE
-          hash *= CTX_SHAPE_CACHE_PRIME2;
-#endif
-        }
-      else
-        {
-          entry->data.s16[0] = x;
-          entry->data.s16[1] = y;
-        }
-      x = entry->data.s16[2];
-      y = entry->data.s16[3];
-#if CTX_SHAPE_CACHE
-      int dx = x-ox;
-      int dy = y-oy;
-      ox = x;
-      oy = y;
-      hash *= CTX_SHAPE_CACHE_PRIME3;
-      hash += dx;
-      hash *= CTX_SHAPE_CACHE_PRIME4;
-      hash += dy;
-#endif
-      if (entry->data.s16[3] < entry->data.s16[1])
-        {
-          *entry = ctx_s16 (CTX_EDGE_FLIPPED,
-                            entry->data.s16[2], entry->data.s16[3],
-                            entry->data.s16[0], entry->data.s16[1]);
-        }
-    }
-#if CTX_SHAPE_CACHE
-  return hash;
-#else
-  return 0;
-#endif
+  float components[4]={r,g,b,a};
+  ctx_color_raw (ctx, CTX_DRGBA, components, 1);
+}
+void ctx_drgba (Ctx *ctx, float r, float g, float b, float a)
+{
+  float components[4]={r,g,b,a};
+  ctx_color_raw (ctx, CTX_DRGBA, components, 0);
 }
 
-static void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y);
+#if CTX_ENABLE_CMYK
 
-static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
+void ctx_cmyka_stroke (Ctx *ctx, float c, float m, float y, float k, float a)
 {
-  if (rasterizer->has_shape && rasterizer->has_prev)
-    {
-      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
-      rasterizer->has_prev = 0;
-    }
+  float components[5]={c,m,y,k,a};
+  ctx_color_raw (ctx, CTX_CMYKA, components, 1);
+}
+void ctx_cmyka (Ctx *ctx, float c, float m, float y, float k, float a)
+{
+  float components[5]={c,m,y,k,a};
+  ctx_color_raw (ctx, CTX_CMYKA, components, 0);
+}
+void ctx_cmyk_stroke   (Ctx *ctx, float c, float m, float y, float k)
+{
+  float components[4]={c,m,y,k};
+  ctx_color_raw (ctx, CTX_CMYK, components, 1);
+}
+void ctx_cmyk (Ctx *ctx, float c, float m, float y, float k)
+{
+  float components[4]={c,m,y,k};
+  ctx_color_raw (ctx, CTX_CMYK, components, 0);
 }
 
-static void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
+static void ctx_dcmyk_raw (Ctx *ctx, float c, float m, float y, float k, int stroke)
 {
-  float tx = x; float ty = y;
-  int aa = rasterizer->aa;
-  rasterizer->x        = x;
-  rasterizer->y        = y;
-  rasterizer->first_x  = x;
-  rasterizer->first_y  = y;
-  rasterizer->has_prev = -1;
-  if (rasterizer->uses_transforms)
-    {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    }
+  float components[5]={c,m,y,k,1.0f};
+  ctx_color_raw (ctx, CTX_DCMYKA, components, stroke);
+}
 
-  tx = (tx - rasterizer->blit_x) * CTX_SUBDIV;
-  ty = ty * aa;
+static void ctx_dcmyka_raw (Ctx *ctx, float c, float m, float y, float k, float a, int stroke)
+{
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_COLOR, CTX_DCMYKA + 512 * stroke, c),
+    ctx_f (CTX_CONT, m, y),
+    ctx_f (CTX_CONT, k, a)
+  };
+  ctx_process (ctx, command);
+}
 
-  if (ty < rasterizer->scan_min)
-    { rasterizer->scan_min = ty; }
-  if (ty > rasterizer->scan_max)
-    { rasterizer->scan_max = ty; }
-  if (tx < rasterizer->col_min)
-    { rasterizer->col_min = tx; }
-  if (tx > rasterizer->col_max)
-    { rasterizer->col_max = tx; }
+void ctx_dcmyk_stroke   (Ctx *ctx, float c, float m, float y, float k)
+{
+  float components[5]={c,m,y,k,1.0f};
+  ctx_color_raw (ctx, CTX_DCMYK, components, 1);
+}
+void ctx_dcmyk (Ctx *ctx, float c, float m, float y, float k)
+{
+  float components[5]={c,m,y,k,1.0f};
+  ctx_color_raw (ctx, CTX_DCMYK, components, 0);
 }
 
-static void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y)
+void ctx_dcmyka_stroke   (Ctx *ctx, float c, float m, float y, float k, float a)
 {
-  float tx = x;
-  float ty = y;
-  float ox = rasterizer->x;
-  float oy = rasterizer->y;
-  if (rasterizer->uses_transforms)
-    {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    }
-  tx -= rasterizer->blit_x;
-#define MIN_Y -1000
-#define MAX_Y 1400
+  float components[5]={c,m,y,k,a};
+  ctx_color_raw (ctx, CTX_DCMYKA, components, 1);
+}
+void ctx_dcmyka (Ctx *ctx, float c, float m, float y, float k, float a)
+{
+  float components[5]={c,m,y,k,a};
+  ctx_color_raw (ctx, CTX_DCMYKA, components, 0);
+}
 
+#endif
 
-  if (ty < MIN_Y) ty = MIN_Y;
-  if (ty > MAX_Y) ty = MAX_Y;
-  ctx_rasterizer_add_point (rasterizer, tx * CTX_SUBDIV, ty * rasterizer->aa);
-  if (rasterizer->has_prev<=0)
-    {
-      if (rasterizer->uses_transforms)
-      {
-        // storing transformed would save some processing for a tiny
-        // amount of runtime RAM XXX
-        _ctx_user_to_device (rasterizer->state, &ox, &oy);
-      }
-      ox -= rasterizer->blit_x;
-
-  if (oy < MIN_Y) oy = MIN_Y;
-  if (oy > MAX_Y) oy = MAX_Y;
+/* XXX: missing CSS1:
+ *
+ *   EM { color: rgb(110%, 0%, 0%) }  // clipped to 100% 
+ *
+ *
+ *   :first-letter
+ *   :first-list
+ *   :link :visited :active
+ *
+ */
 
-      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[0] = ox * CTX_SUBDIV;
-      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[1] = oy * rasterizer->aa;
-      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].code = CTX_NEW_EDGE;
-      rasterizer->has_prev = 1;
-    }
-  rasterizer->has_shape = 1;
-  rasterizer->y         = y;
-  rasterizer->x         = x;
-}
+typedef struct ColorDef {
+  uint64_t name;
+  float r;
+  float g;
+  float b;
+  float a;
+} ColorDef;
 
+#if 0
+#define CTX_silver     CTX_STRH('s','i','l','v','e','r',0,0,0,0,0,0,0,0)
+#define CTX_fuchsia    CTX_STRH('f','u','c','h','s','i','a',0,0,0,0,0,0,0)
+#define CTX_gray       CTX_STRH('g','r','a','y',0,0,0,0,0,0,0,0,0,0)
+#define CTX_yellow     CTX_STRH('y','e','l','l','o','w',0,0,0,0,0,0,0,0)
+#define CTX_white      CTX_STRH('w','h','i','t','e',0,0,0,0,0,0,0,0,0)
+#define CTX_maroon     CTX_STRH('m','a','r','o','o','n',0,0,0,0,0,0,0,0)
+#define CTX_magenta    CTX_STRH('m','a','g','e','n','t','a',0,0,0,0,0,0,0)
+#define CTX_blue       CTX_STRH('b','l','u','e',0,0,0,0,0,0,0,0,0,0)
+#define CTX_green      CTX_STRH('g','r','e','e','n',0,0,0,0,0,0,0,0,0)
+#define CTX_red        CTX_STRH('r','e','d',0,0,0,0,0,0,0,0,0,0,0)
+#define CTX_purple     CTX_STRH('p','u','r','p','l','e',0,0,0,0,0,0,0,0)
+#define CTX_olive      CTX_STRH('o','l','i','v','e',0,0,0,0,0,0,0,0,0)
+#define CTX_teal        CTX_STRH('t','e','a','l',0,0,0,0,0,0,0,0,0,0)
+#define CTX_black      CTX_STRH('b','l','a','c','k',0,0,0,0,0,0,0,0,0)
+#define CTX_cyan       CTX_STRH('c','y','a','n',0,0,0,0,0,0,0,0,0,0)
+#define CTX_navy       CTX_STRH('n','a','v','y',0,0,0,0,0,0,0,0,0,0)
+#define CTX_lime       CTX_STRH('l','i','m','e',0,0,0,0,0,0,0,0,0,0)
+#define CTX_aqua       CTX_STRH('a','q','u','a',0,0,0,0,0,0,0,0,0,0)
+#define CTX_transparent CTX_STRH('t','r','a','n','s','p','a','r','e','n','t',0,0,0)
+#endif
 
-CTX_INLINE static float
-ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
-{
-  float ab   = ctx_lerpf (x0, x1, dt);
-  float bc   = ctx_lerpf (x1, x2, dt);
-  float cd   = ctx_lerpf (x2, x3, dt);
-  float abbc = ctx_lerpf (ab, bc, dt);
-  float bccd = ctx_lerpf (bc, cd, dt);
-  return ctx_lerpf (abbc, bccd, dt);
-}
+static ColorDef colors[]={
+  {CTX_black,    0, 0, 0, 1},
+  {CTX_red,      1, 0, 0, 1},
+  {CTX_green,    0, 1, 0, 1},
+  {CTX_yellow,   1, 1, 0, 1},
+  {CTX_blue,     0, 0, 1, 1},
+  {CTX_fuchsia,  1, 0, 1, 1},
+  {CTX_cyan,     0, 1, 1, 1},
+  {CTX_white,    1, 1, 1, 1},
+  {CTX_silver,   0.75294, 0.75294, 0.75294, 1},
+  {CTX_gray,     0.50196, 0.50196, 0.50196, 1},
+  {CTX_magenta,  0.50196, 0, 0.50196, 1},
+  {CTX_maroon,   0.50196, 0, 0, 1},
+  {CTX_purple,   0.50196, 0, 0.50196, 1},
+  {CTX_green,    0, 0.50196, 0, 1},
+  {CTX_lime,     0, 1, 0, 1},
+  {CTX_olive,    0.50196, 0.50196, 0, 1},
+  {CTX_navy,     0, 0,      0.50196, 1},
+  {CTX_teal,     0, 0.50196, 0.50196, 1},
+  {CTX_aqua,     0, 1, 1, 1},
+  {CTX_transparent, 0, 0, 0, 0},
+  {CTX_none,     0, 0, 0, 0},
+};
 
-inline static void
-ctx_bezier_sample (float x0, float y0,
-                   float x1, float y1,
-                   float x2, float y2,
-                   float x3, float y3,
-                   float dt, float *x, float *y)
+static int xdigit_value(const char xdigit)
 {
-  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
-  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
+  if (xdigit >= '0' && xdigit <= '9')
+   return xdigit - '0';
+  switch (xdigit)
+  {
+    case 'A':case 'a': return 10;
+    case 'B':case 'b': return 11;
+    case 'C':case 'c': return 12;
+    case 'D':case 'd': return 13;
+    case 'E':case 'e': return 14;
+    case 'F':case 'f': return 15;
+  }
+  return 0;
 }
 
-static void
-ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer,
-                              float ox, float oy,
-                              float x0, float y0,
-                              float x1, float y1,
-                              float x2, float y2,
-                              float sx, float sy,
-                              float ex, float ey,
-                              float s,
-                              float e,
-                              int   iteration,
-                              float tolerance)
+static int
+ctx_color_parse_rgb (CtxState *ctxstate, CtxColor *color, const char *color_string)
 {
-  if (iteration > 8)
-    { return; }
-  float t = (s + e) * 0.5f;
-  float x, y, lx, ly, dx, dy;
-  ctx_bezier_sample (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y);
-  if (iteration)
-    {
-      lx = ctx_lerpf (sx, ex, t);
-      ly = ctx_lerpf (sy, ey, t);
-      dx = lx - x;
-      dy = ly - y;
-      if ( (dx*dx+dy*dy) < tolerance)
-        /* bailing - because for the mid-point straight line difference is
-           tiny */
-        { return; }
-      dx = sx - ex;
-      dy = ey - ey;
-      if ( (dx*dx+dy*dy) < tolerance)
-        /* bailing on tiny segments */
-        { return; }
-    }
-  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
-                                sx, sy, x, y, s, t, iteration + 1,
-                                tolerance);
-  ctx_rasterizer_line_to (rasterizer, x, y);
-  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
-                                x, y, ex, ey, t, e, iteration + 1,
-                                tolerance);
-}
+  float dcolor[4] = {0,0,0,1};
+  while (*color_string && *color_string != '(')
+    color_string++;
+  if (*color_string) color_string++;
 
-static void
-ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
-                         float x0, float y0,
-                         float x1, float y1,
-                         float x2, float y2)
-{
-  float tolerance =
-    ctx_pow2 (rasterizer->state->gstate.transform.m[0][0]) +
-    ctx_pow2 (rasterizer->state->gstate.transform.m[1][1]);
-  float ox = rasterizer->x;
-  float oy = rasterizer->y;
-  ox = rasterizer->state->x;
-  oy = rasterizer->state->y;
-  tolerance = 1.0f/tolerance * 2;
-#if 1 // skipping this to preserve hash integrity
-  if (tolerance == 1.0f || 1)
   {
-  float maxx = ctx_maxf (x1,x2);
-  maxx = ctx_maxf (maxx, ox);
-  maxx = ctx_maxf (maxx, x0);
-  float maxy = ctx_maxf (y1,y2);
-  maxy = ctx_maxf (maxy, oy);
-  maxy = ctx_maxf (maxy, y0);
-  float minx = ctx_minf (x1,x2);
-  minx = ctx_minf (minx, ox);
-  minx = ctx_minf (minx, x0);
-  float miny = ctx_minf (y1,y2);
-  miny = ctx_minf (miny, oy);
-  miny = ctx_minf (miny, y0);
-  
-  _ctx_user_to_device (rasterizer->state, &minx, &miny);
-  _ctx_user_to_device (rasterizer->state, &maxx, &maxy);
-
-    if(
-        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
-        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
-        (maxx < rasterizer->blit_x) ||
-        (maxy < rasterizer->blit_y) )
-    {
-    }
-    else
+    int n_floats = 0;
+    char *p =    (char*)color_string;
+    char *prev = (char*)NULL;
+    for (; p && n_floats < 4 && p != prev && *p; )
     {
-      ctx_rasterizer_bezier_divide (rasterizer,
-                                    ox, oy, x0, y0,
-                                    x1, y1, x2, y2,
-                                    ox, oy, x2, y2,
-                                    0.0f, 1.0f, 0.0f, tolerance);
+      float val;
+      prev = p;
+      val = _ctx_parse_float (p, &p);
+      if (p != prev)
+      {
+        if (n_floats < 3)
+          dcolor[n_floats++] = val/255.0;
+        else
+          dcolor[n_floats++] = val;
+
+        while (*p == ' ' || *p == ',')
+        {
+          p++;
+          prev++;
+        }
+      }
     }
   }
-  else
-#endif
-    {
-      ctx_rasterizer_bezier_divide (rasterizer,
-                                    ox, oy, x0, y0,
-                                    x1, y1, x2, y2,
-                                    ox, oy, x2, y2,
-                                    0.0f, 1.0f, 0.0f, tolerance);
-    }
-  ctx_rasterizer_line_to (rasterizer, x2, y2);
+  ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
+  return 0;
 }
 
-static void
-ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
+static int ctx_isxdigit (uint8_t ch)
 {
-  if (x == 0.f && y == 0.f)
-    { return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_move_to (rasterizer, x, y);
+  if (ch >= '0' && ch <= '9') return 1;
+  if (ch >= 'a' && ch <= 'f') return 1;
+  if (ch >= 'A' && ch <= 'F') return 1;
+  return 0;
 }
 
-static void
-ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
+static int
+mrg_color_parse_hex (CtxState *ctxstate, CtxColor *color, const char *color_string)
 {
-  if (x== 0.f && y==0.f)
-    { return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_line_to (rasterizer, x, y);
-}
+  float dcolor[4]={0,0,0,1};
+  int string_length = strlen (color_string);
+  int i;
+  dcolor[3] = 1.0;
 
-static void
-ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer,
-                             float x0, float y0, float x1, float y1, float x2, float y2)
-{
-  x0 += rasterizer->x;
-  y0 += rasterizer->y;
-  x1 += rasterizer->x;
-  y1 += rasterizer->y;
-  x2 += rasterizer->x;
-  y2 += rasterizer->y;
-  ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2);
-}
-
-CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp)
-{
-  const CtxEntry *a = (const CtxEntry *) ap;
-  const CtxEntry *b = (const CtxEntry *) bp;
-  int ycompare = a->data.s16[1] - b->data.s16[1];
-  if (ycompare)
-    { return ycompare; }
-  int xcompare = a->data.s16[0] - b->data.s16[0];
-  return xcompare;
-}
-
-CTX_INLINE static int ctx_edge_qsort_partition (CtxEntry *A, int low, int high)
-{
-  CtxEntry pivot = A[ (high+low) /2];
-  int i = low;
-  int j = high;
-  while (i <= j)
+  if (string_length == 7 ||  /* #rrggbb   */
+      string_length == 9)    /* #rrggbbaa */
     {
-      while (ctx_compare_edges (&A[i], &pivot) <0) { i ++; }
-      while (ctx_compare_edges (&pivot, &A[j]) <0) { j --; }
-      if (i <= j)
+      int num_iterations = (string_length - 1) / 2;
+  
+      for (i = 0; i < num_iterations; ++i)
         {
-          CtxEntry tmp = A[i];
-          A[i] = A[j];
-          A[j] = tmp;
-          i++;
-          j--;
+          if (ctx_isxdigit (color_string[2 * i + 1]) &&
+              ctx_isxdigit (color_string[2 * i + 2]))
+            {
+              dcolor[i] = (xdigit_value (color_string[2 * i + 1]) << 4 |
+                           xdigit_value (color_string[2 * i + 2])) / 255.f;
+            }
+          else
+            {
+              return 0;
+            }
         }
+      /* Successful #rrggbb(aa) parsing! */
+      ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
+      return 1;
     }
-  return i;
+  else if (string_length == 4 ||  /* #rgb  */
+           string_length == 5)    /* #rgba */
+    {
+      int num_iterations = string_length - 1;
+      for (i = 0; i < num_iterations; ++i)
+        {
+          if (ctx_isxdigit (color_string[i + 1]))
+            {
+              dcolor[i] = (xdigit_value (color_string[i + 1]) << 4 |
+                           xdigit_value (color_string[i + 1])) / 255.f;
+            }
+          else
+            {
+              return 0;
+            }
+        }
+      ctx_color_set_rgba (ctxstate, color, dcolor[0], dcolor[1],dcolor[2],dcolor[3]);
+      /* Successful #rgb(a) parsing! */
+      return 0;
+    }
+  /* String was of unsupported length. */
+  return 1;
 }
 
-static void ctx_edge_qsort (CtxEntry *entries, int low, int high)
+//#define CTX_currentColor     CTX_STRH('c','u','r','r','e','n','t','C','o','l','o','r',0,0)
+
+int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string)
 {
+  int i;
+  uint64_t hash = ctx_strhash (string, 0);
+//  ctx_color_set_rgba (&(ctx->state), color, 0.4,0.1,0.9,1.0);
+//  return 0;
+    //rgba[0], rgba[1], rgba[2], rgba[3]);
+
+  if (hash == CTX_currentColor)
   {
-    int p = ctx_edge_qsort_partition (entries, low, high);
-    if (low < p -1 )
-      { ctx_edge_qsort (entries, low, p - 1); }
-    if (low < high)
-      { ctx_edge_qsort (entries, p, high); }
+    float rgba[4];
+    CtxColor ccolor;
+    ctx_get_color (ctx, CTX_color, &ccolor);
+    ctx_color_get_rgba (&(ctx->state), &ccolor, rgba);
+    ctx_color_set_rgba (&(ctx->state), color, rgba[0], rgba[1], rgba[2], rgba[3]);
+    return 0;
   }
-}
 
-static void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
-{
-  if (rasterizer->edge_list.count > 1)
+  for (i = (sizeof(colors)/sizeof(colors[0]))-1; i>=0; i--)
+  {
+    if (hash == colors[i].name)
     {
-      ctx_edge_qsort (& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
+      ctx_color_set_rgba (&(ctx->state), color,
+       colors[i].r, colors[i].g, colors[i].b, colors[i].a);
+      return 0;
     }
+  }
+
+  if (string[0] == '#')
+    mrg_color_parse_hex (&(ctx->state), color, string);
+  else if (string[0] == 'r' &&
+      string[1] == 'g' &&
+      string[2] == 'b'
+      )
+    ctx_color_parse_rgb (&(ctx->state), color, string);
+
+  return 0;
 }
 
-static void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
+int ctx_color_fill (Ctx *ctx, const char *string)
 {
-#if CTX_RASTERIZER_FORCE_AA==0
-  rasterizer->ending_edges = 0;
-#endif
-  for (int i = 0; i < rasterizer->active_edges; i++)
-    {
-      int edge_end =rasterizer->edge_list.entries[rasterizer->edges[i].index].data.s16[3];
-      if (edge_end < rasterizer->scanline)
-        {
-          int dx_dy = rasterizer->edges[i].dx;
-          if (abs(dx_dy)> CTX_RASTERIZER_AA_SLOPE_LIMIT)
-            { rasterizer->needs_aa --; }
-          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
-          rasterizer->active_edges--;
-          i--;
-        }
-#if CTX_RASTERIZER_FORCE_AA==0
-      else if (edge_end < rasterizer->scanline + rasterizer->aa)
-        rasterizer->ending_edges = 1;
-#endif
-    }
+  CtxColor color = {0,};
+  ctx_color_set_from_string (ctx, &color, string);
+  float rgba[4];
+  ctx_color_get_rgba (&(ctx->state), &color, rgba);
+  ctx_color_raw (ctx, CTX_RGBA, rgba, 0);
+  return 0;
 }
 
-static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
+int ctx_color_stroke (Ctx *ctx, const char *string)
 {
-  for (int i = 0; i < rasterizer->active_edges; i++)
-    {
-      rasterizer->edges[i].x += rasterizer->edges[i].dx * count;
-    }
-#if CTX_RASTERIZER_FORCE_AA==0
-  for (int i = 0; i < rasterizer->pending_edges; i++)
-    {
-      rasterizer->edges[CTX_MAX_EDGES-1-i].x += rasterizer->edges[CTX_MAX_EDGES-1-i].dx * count;
-    }
-#endif
+  CtxColor color = {0,};
+  ctx_color_set_from_string (ctx, &color, string);
+  float rgba[4];
+  ctx_color_get_rgba (&(ctx->state), &color, rgba);
+  ctx_color_raw (ctx, CTX_RGBA, rgba, 1);
+  return 0;
 }
 
-/* feeds up to rasterizer->scanline,
-   keeps a pending buffer of edges - that encompass
-   the full incoming scanline,
-   feed until the start of the scanline and check for need for aa
-   in all of pending + active edges, then
-   again feed_edges until middle of scanline if doing non-AA
-   or directly render when doing AA
-*/
-inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer)
+void
+ctx_rgba8 (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
-  int miny;
-  CtxEntry *entries = rasterizer->edge_list.entries;
-#if CTX_RASTERIZER_FORCE_AA==0
-  for (int i = 0; i < rasterizer->pending_edges; i++)
-    {
-      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i].index].data.s16[1] <= rasterizer->scanline)
-        {
-          if (rasterizer->active_edges < CTX_MAX_EDGES-2)
-            {
-              int no = rasterizer->active_edges;
-              rasterizer->active_edges++;
-              rasterizer->edges[no] = rasterizer->edges[CTX_MAX_EDGES-1-i];
-              rasterizer->edges[CTX_MAX_EDGES-1-i] =
-                rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges + 1];
-              rasterizer->pending_edges--;
-              i--;
-            }
-        }
-    }
-#endif
-  while (rasterizer->edge_pos < rasterizer->edge_list.count &&
-         (miny=entries[rasterizer->edge_pos].data.s16[1]) <= rasterizer->scanline 
-#if CTX_RASTERIZER_FORCE_AA==0
-         + rasterizer->aa
-#endif
-         
-         )
-    {
-      if (rasterizer->active_edges < CTX_MAX_EDGES-2)
-        {
-          int dy = (entries[rasterizer->edge_pos].data.s16[3] -
-                    miny);
-          if (dy) /* skipping horizontal edges */
-            {
-              int yd = rasterizer->scanline - miny;
-              int no = rasterizer->active_edges;
-              rasterizer->active_edges++;
-              rasterizer->edges[no].index = rasterizer->edge_pos;
-              int index = rasterizer->edges[no].index;
-              int x0 = entries[index].data.s16[0];
-              int x1 = entries[index].data.s16[2];
-              rasterizer->edges[no].x = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER;
-              int dx_dy;
-              //  if (dy)
-              dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
-              //  else
-              //  dx_dy = 0;
-              rasterizer->edges[no].dx = dx_dy;
-              rasterizer->edges[no].x += (yd * dx_dy);
-              // XXX : even better minx and maxx can
-              //       be derived using y0 and y1 for scaling dx_dy
-              //       when ydelta to these are smaller than
-              //       ydelta to scanline
 #if 0
-              if (dx_dy < 0)
-                {
-                  rasterizer->edges[no].minx =
-                    rasterizer->edges[no].x + dx_dy/2;
-                  rasterizer->edges[no].maxx =
-                    rasterizer->edges[no].x - dx_dy/2;
-                }
-              else
-                {
-                  rasterizer->edges[no].minx =
-                    rasterizer->edges[no].x - dx_dy/2;
-                  rasterizer->edges[no].maxx =
-                    rasterizer->edges[no].x + dx_dy/2;
-                }
+  CtxEntry command = ctx_u8 (CTX_SET_RGBA_U8, r, g, b, a, 0, 0, 0, 0);
+
+  uint8_t rgba[4];
+  ctx_color_get_rgba8 (&ctx->state, &ctx->state.gstate.source.color, rgba);
+  if (rgba[0] == r && rgba[1] == g && rgba[2] == b && rgba[3] == a)
+     return;
+
+  ctx_process (ctx, &command);
+#else
+  ctx_rgba (ctx, r/255.0f, g/255.0f, b/255.0f, a/255.0f);
 #endif
-#if CTX_RASTERIZER_FORCE_AA==0
-              if (abs(dx_dy)> CTX_RASTERIZER_AA_SLOPE_LIMIT)
-                { rasterizer->needs_aa ++; }
+}
 
-              if ((miny > rasterizer->scanline) )
-                {
-                  /* it is a pending edge - we add it to the end of the array
-                     and keep a different count for items stored here, like
-                     a heap and stack growing against each other
-                  */
-                  if (rasterizer->pending_edges < CTX_MAX_PENDING-1)
-                  {
-                    rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
-                    rasterizer->edges[no];
-                    rasterizer->pending_edges++;
-                    rasterizer->active_edges--;
-                  }
-                }
-#endif
-            }
-        }
-      rasterizer->edge_pos++;
-    }
-}
-
-CTX_INLINE static int ctx_compare_edges2 (const void *ap, const void *bp)
-{
-  const CtxEdge *a = (const CtxEdge *) ap;
-  const CtxEdge *b = (const CtxEdge *) bp;
-  return a->x - b->x;
-}
-
-CTX_INLINE static int ctx_edge2_qsort_partition (CtxEdge *A, int low, int high)
+void ctx_rgba8_stroke (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
-  CtxEdge pivot = A[ (high+low) /2];
-  int i = low;
-  int j = high;
-  while (i <= j)
-    {
-      while (ctx_compare_edges2 (&A[i], &pivot) <0) { i ++; }
-      while (ctx_compare_edges2 (&pivot, &A[j]) <0) { j --; }
-      if (i <= j)
-        {
-          CtxEdge tmp = A[i];
-          A[i] = A[j];
-          A[j] = tmp;
-          i++;
-          j--;
-        }
-    }
-  return i;
+  ctx_rgba_stroke (ctx, r/255.0f, g/255.0f, b/255.0f, a/255.0f);
 }
 
-static void ctx_edge2_qsort (CtxEdge *entries, int low, int high)
-{
-  {
-    int p = ctx_edge2_qsort_partition (entries, low, high);
-    if (low < p -1 )
-      { ctx_edge2_qsort (entries, low, p - 1); }
-    if (low < high)
-      { ctx_edge2_qsort (entries, p, high); }
-  }
-}
 
+#endif 
 
-static void ctx_rasterizer_sort_active_edges (CtxRasterizer *rasterizer)
+#if CTX_BABL
+void ctx_rasterizer_colorspace_babl (CtxState      *state,
+                                     CtxColorSpace  space_slot,
+                                     const Babl    *space)
 {
-  CtxEdge *edges = rasterizer->edges;
-  /* we use sort networks for the very frequent cases of few active edges
-   * the built in qsort is fast, but sort networks are even faster
-   */
-  switch (rasterizer->active_edges)
+  switch (space_slot)
   {
-    case 0:
-    case 1: break;
-#if CTX_BLOATY_FAST_PATHS
-    case 2:
-#define COMPARE(a,b) \
-      if (ctx_compare_edges2 (&edges[a], &edges[b])>0)\
-      {\
-        CtxEdge tmp = edges[a];\
-        edges[a] = edges[b];\
-        edges[b] = tmp;\
-      }
-      COMPARE(0,1);
-      break;
-    case 3:
-      COMPARE(0,1); COMPARE(0,2); COMPARE(1,2);
+    case CTX_COLOR_SPACE_DEVICE_RGB:
+      state->gstate.device_space = space;
       break;
-    case 4:
-      COMPARE(0,1); COMPARE(2,3); COMPARE(0,2); COMPARE(1,3); COMPARE(1,2);
+    case CTX_COLOR_SPACE_DEVICE_CMYK:
+      state->gstate.device_space = space;
       break;
-    case 5:
-      COMPARE(1,2); COMPARE(0,2); COMPARE(0,1); COMPARE(3,4); COMPARE(0,3);
-      COMPARE(1,4); COMPARE(2,4); COMPARE(1,3); COMPARE(2,3);
+    case CTX_COLOR_SPACE_USER_RGB:
+      state->gstate.rgb_space = space;
       break;
-    case 6:
-      COMPARE(1,2); COMPARE(0,2); COMPARE(0,1); COMPARE(4,5);
-      COMPARE(3,5); COMPARE(3,4); COMPARE(0,3); COMPARE(1,4);
-      COMPARE(2,5); COMPARE(2,4); COMPARE(1,3); COMPARE(2,3);
+    case CTX_COLOR_SPACE_USER_CMYK:
+      state->gstate.cmyk_space = space;
       break;
-#endif
-    default:
-      ctx_edge2_qsort (&edges[0], 0, rasterizer->active_edges-1);
+    case CTX_COLOR_SPACE_TEXTURE:
+      state->gstate.texture_space = space;
       break;
   }
+
+  const Babl *srgb = babl_space ("sRGB");
+  if (!state->gstate.texture_space) 
+       state->gstate.texture_space = srgb;
+  if (!state->gstate.device_space) 
+       state->gstate.device_space = srgb;
+  if (!state->gstate.rgb_space) 
+       state->gstate.rgb_space = srgb;
+
+  //fprintf (stderr, "%s\n", babl_get_name (state->gstate.device_space));
+
+  state->gstate.fish_rgbaf_device_to_user = babl_fish (
+       babl_format_with_space ("R'G'B'A float", state->gstate.device_space),
+       babl_format_with_space ("R'G'B'A float", state->gstate.rgb_space));
+  state->gstate.fish_rgbaf_user_to_device = babl_fish (
+       babl_format_with_space ("R'G'B'A float", state->gstate.rgb_space),
+       babl_format_with_space ("R'G'B'A float", state->gstate.device_space));
+  state->gstate.fish_rgbaf_texture_to_device = babl_fish (
+       babl_format_with_space ("R'G'B'A float", state->gstate.texture_space),
+       babl_format_with_space ("R'G'B'A float", state->gstate.device_space));
 }
+#endif
 
-inline static void
-ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
-                                  int            minx,
-                                  int            maxx,
-                                  uint8_t       *coverage,
-                                  int            winding,
-                                  int            aa_factor)
+void ctx_rasterizer_colorspace_icc (CtxState      *state,
+                                    CtxColorSpace  space_slot,
+                                    char          *icc_data,
+                                    int            icc_length)
 {
-  CtxEntry *entries = rasterizer->edge_list.entries;;
-  CtxEdge  *edges = rasterizer->edges;
-  int scanline     = rasterizer->scanline;
-  int active_edges = rasterizer->active_edges;
-  int parity = 0;
-  int fraction = 255/aa_factor;
-  coverage -= minx;
-#define CTX_EDGE(no)      entries[edges[no].index]
-#define CTX_EDGE_YMIN(no) CTX_EDGE(no).data.s16[1]
-#define CTX_EDGE_X(no)     (rasterizer->edges[no].x)
-  for (int t = 0; t < active_edges -1;)
-    {
-      int ymin = CTX_EDGE_YMIN (t);
-      int next_t = t + 1;
-      if (scanline != ymin)
-        {
-          if (winding)
-            { parity += ( (CTX_EDGE (t).code == CTX_EDGE_FLIPPED) ?1:-1); }
-          else
-            { parity = 1 - parity; }
-        }
-      if (parity)
-        {
-          int x0 = CTX_EDGE_X (t)      / CTX_SUBDIV ;
-          int x1 = CTX_EDGE_X (next_t) / CTX_SUBDIV ;
-          int first = x0 / CTX_RASTERIZER_EDGE_MULTIPLIER;
-          int last  = x1 / CTX_RASTERIZER_EDGE_MULTIPLIER;
-
-          int graystart = 255 - ( (x0 * 256/CTX_RASTERIZER_EDGE_MULTIPLIER) & 0xff);
-          int grayend   = (x1 * 256/CTX_RASTERIZER_EDGE_MULTIPLIER) & 0xff;
+#if CTX_BABL
+   const char *error = NULL;
+   const Babl *space = NULL;
 
-          if (first < minx)
-            { first = minx;
-              graystart=255;
-            }
-          if (last > maxx)
-            { last = maxx;
-              grayend=255;
-            }
-          if (first == last)
-          {
-            coverage[first] += (graystart-(255-grayend))/ aa_factor;
-          }
-          else if (first < last)
-          {
-                  /*
-            if (aa_factor == 1)
-            {
-              coverage[first] += graystart;
-              for (int x = first + 1; x < last; x++)
-                coverage[x] = 255;
-              coverage[last] = grayend;
-            }
-            else
-            */
-            {
-              coverage[first] += graystart/ aa_factor;
-              for (int x = first + 1; x < last; x++)
-                coverage[x] += fraction;
-              coverage[last]  += grayend/ aa_factor;
-            }
-          }
-        }
-      t = next_t;
-    }
+   if (icc_data == NULL) space = babl_space ("sRGB");
+   else if (icc_length < 16)
+   {
+      char tmp[24];
+      int i;
+      for (i = 0; i < icc_length; i++)
+        tmp[i]= (icc_data[i]>='A' && icc_data[i]<='Z')?icc_data[i]+('a'-'A'):icc_data[i];
+      tmp[icc_length]=0;
+      if (!strcmp (tmp, "srgb"))            space = babl_space ("sRGB");
+      else if (!strcmp (tmp, "scrgb"))      space = babl_space ("scRGB");
+      else if (!strcmp (tmp, "acescg"))     space = babl_space ("ACEScg");
+      else if (!strcmp (tmp, "adobe"))      space = babl_space ("Adobe");
+      else if (!strcmp (tmp, "apple"))      space = babl_space ("Apple");
+      else if (!strcmp (tmp, "rec2020"))    space = babl_space ("Rec2020");
+      else if (!strcmp (tmp, "aces2065-1")) space = babl_space ("ACES2065-1");
+   }
 
-#if CTX_ENABLE_SHADOW_BLUR
-  if (rasterizer->in_shadow)
-  {
-    float radius = rasterizer->state->gstate.shadow_blur;
-    int dim = 2 * radius + 1;
-    if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-      dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-    {
-      uint16_t temp[maxx-minx+1];
-      memset (temp, 0, sizeof (temp));
-      for (int x = dim/2; x < maxx-minx + 1 - dim/2; x ++)
-        for (int u = 0; u < dim; u ++)
-        {
-            temp[x] += coverage[minx+x+u-dim/2] * rasterizer->kernel[u] * 256;
-        }
-      for (int x = 0; x < maxx-minx + 1; x ++)
-        coverage[minx+x] = temp[x] >> 8;
-    }
-  }
+   if (!space)
+   {
+     space = babl_space_from_icc (icc_data, icc_length, BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error);
+   }
+   if (space)
+   {
+     ctx_rasterizer_colorspace_babl (state, space_slot, space);
+   }
 #endif
+}
 
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
+void ctx_colorspace (Ctx           *ctx,
+                     CtxColorSpace  space_slot,
+                     unsigned char *data,
+                     int            data_length)
+{
+  if (data)
   {
-    /* perhaps not working right for clear? */
-    int y = scanline / rasterizer->aa;
-    uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y];
-    // XXX SIMD candidate
-    for (int x = minx; x <= maxx; x ++)
-    {
-#if CTX_1BIT_CLIP
-        coverage[x] = (coverage[x] * ((clip_line[x/8]&(1<<(x%8)))?255:0))/255;
-#else
-        coverage[x] = (coverage[x] * clip_line[x])/255;
-#endif
-    }
+    if (data_length <= 0) data_length = (int)strlen ((char*)data);
+    ctx_process_cmd_str_with_len (ctx, CTX_COLOR_SPACE, (char*)data, space_slot, 0, data_length);
   }
-  if (rasterizer->aa == 1)
+  else
   {
-    for (int x = minx; x <= maxx; x ++)
-     coverage[x] = coverage[x] > 127?255:0;
+    ctx_process_cmd_str_with_len (ctx, CTX_COLOR_SPACE, "sRGB", space_slot, 0, 4);
   }
-#endif
 }
 
-#undef CTX_EDGE_Y0
-#undef CTX_EDGE
-
-static void
-ctx_rasterizer_reset (CtxRasterizer *rasterizer)
+void ctx_gradient_add_stop_u8
+(Ctx *ctx, float pos, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
-#if CTX_RASTERIZER_FORCE_AA==0
-  rasterizer->pending_edges   = 0;
-#endif
-  rasterizer->active_edges    = 0;
-  rasterizer->has_shape       = 0;
-  rasterizer->has_prev        = 0;
-  rasterizer->edge_list.count = 0; // ready for new edges
-  rasterizer->edge_pos        = 0;
-  rasterizer->needs_aa        = 0;
-  rasterizer->scanline        = 0;
-  if (!rasterizer->preserve)
-  {
-    rasterizer->scan_min      = 5000;
-    rasterizer->scan_max      = -5000;
-    rasterizer->col_min       = 5000;
-    rasterizer->col_max       = -5000;
-  }
-  //rasterizer->comp_op       = NULL;
+  CtxEntry entry = ctx_f (CTX_GRADIENT_STOP, pos, 0);
+  entry.data.u8[4+0] = r;
+  entry.data.u8[4+1] = g;
+  entry.data.u8[4+2] = b;
+  entry.data.u8[4+3] = a;
+  ctx_process (ctx, &entry);
 }
 
-static void
-ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, int winding
-#if CTX_SHAPE_CACHE
-                                ,CtxShapeEntry *shape
-#endif
-                               )
+void ctx_gradient_add_stop
+(Ctx *ctx, float pos, float r, float g, float b, float a)
 {
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-  int aa = rasterizer->aa;
-
-  int scan_start = rasterizer->blit_y * aa;
-  int scan_end   = scan_start + rasterizer->blit_height * aa;
-  int blit_width = rasterizer->blit_width;
-  int blit_max_x = rasterizer->blit_x + blit_width;
-  int minx       = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x;
-  int maxx       = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - rasterizer->blit_x;
+  int ir = r * 255;
+  int ig = g * 255;
+  int ib = b * 255;
+  int ia = a * 255;
+  ir = CTX_CLAMP (ir, 0,255);
+  ig = CTX_CLAMP (ig, 0,255);
+  ib = CTX_CLAMP (ib, 0,255);
+  ia = CTX_CLAMP (ia, 0,255);
+  ctx_gradient_add_stop_u8 (ctx, pos, ir, ig, ib, ia);
+}
 
-#if 1
-  if (
-#if CTX_SHAPE_CACHE
-    !shape &&
-#endif
-    maxx > blit_max_x - 1)
-    { maxx = blit_max_x - 1; }
-#endif
-#if 1
-  if (rasterizer->state->gstate.clip_min_x>
-      minx)
-    { minx = rasterizer->state->gstate.clip_min_x; }
-  if (rasterizer->state->gstate.clip_max_x <
-      maxx)
-    { maxx = rasterizer->state->gstate.clip_max_x; }
-#endif
-  if (minx < 0)
-    { minx = 0; }
-  if (minx >= maxx)
-    {
-      ctx_rasterizer_reset (rasterizer);
-      return;
-    }
-#if CTX_SHAPE_CACHE
-  uint8_t _coverage[shape?2:maxx-minx+1];
-#else
-  uint8_t _coverage[maxx-minx+1];
-#endif
-  uint8_t *coverage = &_coverage[0];
+void ctx_gradient_add_stop_string
+(Ctx *ctx, float pos, const char *string)
+{
+  CtxColor color = {0,};
+  ctx_color_set_from_string (ctx, &color, string);
+  float rgba[4];
+  ctx_color_get_rgba (&(ctx->state), &color, rgba);
+  ctx_gradient_add_stop (ctx, pos, rgba[0], rgba[1], rgba[2], rgba[3]);
+}
 
-  ctx_compositor_setup_default (rasterizer);
+//  deviceRGB .. settable when creating an RGB image surface..
+//               queryable when running in terminal - is it really needed?
+//               though it is settable ; and functional for changing this state at runtime..
+//
+//  userRGB - settable at any time, stored in save|restore 
+//  texture - set as the space of data on subsequent 
 
-#if CTX_SHAPE_CACHE
-  if (shape)
-    {
-      coverage = &shape->data[0];
-    }
-#endif
-  ctx_assert (coverage);
-  rasterizer->scan_min -= (rasterizer->scan_min % aa);
-#if CTX_SHAPE_CACHE
-  if (shape)
-    {
-      scan_start = rasterizer->scan_min;
-      scan_end   = rasterizer->scan_max;
-    }
-  else
-#endif
+float ctx_state_get (CtxState *state, uint64_t hash)
+{
+  for (int i = state->gstate.keydb_pos-1; i>=0; i--)
     {
-      if (rasterizer->scan_min > scan_start)
-        {
-          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / aa);
-          scan_start = rasterizer->scan_min;
-        }
-      if (rasterizer->scan_max < scan_end)
-        { scan_end = rasterizer->scan_max; }
-    }
-  if (rasterizer->state->gstate.clip_min_y * aa > scan_start )
-    { 
-       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * aa -scan_start) / aa);
-       scan_start = rasterizer->state->gstate.clip_min_y * aa; 
+      if (state->keydb[i].key == hash)
+        { return state->keydb[i].value; }
     }
-  if (rasterizer->state->gstate.clip_max_y * aa < scan_end)
-    { scan_end = rasterizer->state->gstate.clip_max_y * aa; }
-  if (scan_start > scan_end ||
-      (scan_start > (rasterizer->blit_y + rasterizer->blit_height) * aa) ||
-      (scan_end < (rasterizer->blit_y) * aa))
-  { 
-    /* not affecting this rasterizers scanlines */
-    ctx_rasterizer_reset (rasterizer);
-    return;
-  }
-  ctx_rasterizer_sort_edges (rasterizer);
-  if (maxx>minx)
-  {
-#if CTX_RASTERIZER_FORCE_AA==0
-    int halfstep2 = aa/2;
-    int halfstep  = aa/2 + 1;
-#endif
-    rasterizer->needs_aa = 0;
-    rasterizer->scanline = scan_start-aa*200;
-    ctx_rasterizer_feed_edges (rasterizer);
-    ctx_rasterizer_discard_edges (rasterizer);
-    ctx_rasterizer_increment_edges (rasterizer, aa * 200);
-    rasterizer->scanline = scan_start;
-    ctx_rasterizer_feed_edges (rasterizer);
-    ctx_rasterizer_discard_edges (rasterizer);
+  return -0.0;
+}
 
-  for (rasterizer->scanline = scan_start; rasterizer->scanline <= scan_end;)
+void ctx_state_set (CtxState *state, uint64_t key, float value)
+{
+  if (key != CTX_new_state)
     {
-      ctx_memset (coverage, 0,
-#if CTX_SHAPE_CACHE
-                  shape?shape->width:
-#endif
-                  sizeof (_coverage) );
-#if CTX_RASTERIZER_FORCE_AA==1
-      rasterizer->needs_aa = 1;
-#endif
-
-#if CTX_RASTERIZER_FORCE_AA==0
-      if (rasterizer->needs_aa
-        || rasterizer->pending_edges
-        || rasterizer->ending_edges
-        || rasterizer->force_aa
-        || aa == 1
-          )
-#endif
-        {
-          for (int i = 0; i < rasterizer->aa; i++)
-            {
-              ctx_rasterizer_sort_active_edges (rasterizer);
-              ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, aa);
-              rasterizer->scanline ++;
-              ctx_rasterizer_increment_edges (rasterizer, 1);
-              ctx_rasterizer_feed_edges (rasterizer);
-  ctx_rasterizer_discard_edges (rasterizer);
-            }
-        }
-#if CTX_RASTERIZER_FORCE_AA==0
-      else
-        {
-          ctx_rasterizer_increment_edges (rasterizer, halfstep);
-          ctx_rasterizer_sort_active_edges (rasterizer);
-          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, 1);
-          ctx_rasterizer_increment_edges (rasterizer, halfstep2);
-          rasterizer->scanline += rasterizer->aa;
-          ctx_rasterizer_feed_edges (rasterizer);
-  ctx_rasterizer_discard_edges (rasterizer);
-        }
-#endif
+      if (ctx_state_get (state, key) == value)
+        { return; }
+      for (int i = state->gstate.keydb_pos-1;
+           state->keydb[i].key != CTX_new_state && i >=0;
+           i--)
         {
-#if CTX_SHAPE_CACHE
-          if (shape == NULL)
-#endif
+          if (state->keydb[i].key == key)
             {
-#if 0
-              if (aa==1)
-              {
-                for (int x = 0; x < maxx-minx; x++)
-                  coverage
-              }
-#endif
-              ctx_rasterizer_apply_coverage (rasterizer,
-                                             &dst[(minx * rasterizer->format->bpp) /8],
-                                             minx,
-                                             coverage, maxx-minx + 1);
+              state->keydb[i].value = value;
+              return;
             }
         }
-#if CTX_SHAPE_CACHE
-      if (shape)
-        {
-          coverage += shape->width;
-        }
-#endif
-      dst += rasterizer->blit_stride;
     }
-  }
+  if (state->gstate.keydb_pos >= CTX_MAX_KEYDB)
+    { return; }
+  state->keydb[state->gstate.keydb_pos].key = key;
+  state->keydb[state->gstate.keydb_pos].value = value;
+  state->gstate.keydb_pos++;
+}
 
-  if (rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OUT ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_IN ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_IN ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_COPY ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_ATOP ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_CLEAR)
+
+#define CTX_KEYDB_STRING_START (-90000.0)
+#define CTX_KEYDB_STRING_END   (CTX_KEYDB_STRING_START + CTX_STRINGPOOL_SIZE)
+
+static int ctx_float_is_string (float val)
+{
+  return val >= CTX_KEYDB_STRING_START && val <= CTX_KEYDB_STRING_END;
+}
+
+static int ctx_float_to_string_index (float val)
+{
+  int idx = -1;
+  if (ctx_float_is_string (val))
   {
-     /* fill in the rest of the blitrect when compositing mode permits it */
-     uint8_t nocoverage[rasterizer->blit_width];
-     //int gscan_start = rasterizer->state->gstate.clip_min_y * aa;
-     int gscan_start = rasterizer->state->gstate.clip_min_y * aa;
-     int gscan_end = rasterizer->state->gstate.clip_max_y * aa;
-     memset (nocoverage, 0, sizeof(nocoverage));
-     int startx   = rasterizer->state->gstate.clip_min_x;
-     int endx     = rasterizer->state->gstate.clip_max_x;
-     int clipw    = endx-startx + 1;
-     uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+    idx = val - CTX_KEYDB_STRING_START;
+  }
+  return idx;
+}
 
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / aa);
-     for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, clipw);
-       rasterizer->scanline += aa;
-       dst += rasterizer->blit_stride;
-     }
-     if (minx < startx)
-     {
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / aa);
-     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, minx-startx);
-       dst += rasterizer->blit_stride;
-     }
-     }
-     if (endx > maxx)
-     {
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / aa);
-     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (maxx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, endx-maxx);
-
-       rasterizer->scanline += aa;
-       dst += rasterizer->blit_stride;
-     }
-     }
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / aa);
-     // XXX valgrind/asan this
-     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/aa < gscan_end-1;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, clipw-1);
+static float ctx_string_index_to_float (int index)
+{
+  return CTX_KEYDB_STRING_START + index;
+}
 
-       rasterizer->scanline += aa;
-       dst += rasterizer->blit_stride;
-     }
+void *ctx_state_get_blob (CtxState *state, uint64_t key)
+{
+  float stored = ctx_state_get (state, key);
+  int idx = ctx_float_to_string_index (stored);
+  if (idx >= 0)
+  {
+     // can we know length?
+     return &state->stringpool[idx];
   }
-  ctx_rasterizer_reset (rasterizer);
+
+  // format number as string?
+  return NULL;
+}
+
+const char *ctx_state_get_string (CtxState *state, uint64_t key)
+{
+  const char *ret = (char*)ctx_state_get_blob (state, key);
+  if (ret && ret[0] == 127)
+    return NULL;
+  return ret;
 }
 
 
-static int
-ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
-                          int          x0,
-                          int          y0,
-                          int          x1,
-                          int          y1)
+static void ctx_state_set_blob (CtxState *state, uint64_t key, uint8_t *data, int len)
 {
-  int aa = rasterizer->aa;
-  if (x0>x1) { // && y0>y1) { 
-     int tmp = x1;
-     x1 = x0;
-     x0 = tmp;
-     tmp = y1;
-     y1 = y0;
-     y0 = tmp;
-  }
-  if (x1 % CTX_SUBDIV ||
-      x0 % CTX_SUBDIV ||
-      y1 % aa ||
-      y0 % aa)
-    { return 0; }
-  x1 /= CTX_SUBDIV;
-  x0 /= CTX_SUBDIV;
-  y1 /= aa;
-  y0 /= aa;
-  uint8_t coverage[x1-x0 + 1];
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-  ctx_compositor_setup_default (rasterizer);
-  ctx_memset (coverage, 0xff, sizeof (coverage) );
-  if (x0 < rasterizer->blit_x)
-    { x0 = rasterizer->blit_x; }
-  if (y0 < rasterizer->blit_y)
-    { y0 = rasterizer->blit_y; }
-  if (y1 > rasterizer->blit_y + rasterizer->blit_height)
-    { y1 = rasterizer->blit_y + rasterizer->blit_height; }
-  if (x1 > rasterizer->blit_x + rasterizer->blit_width)
-    { x1 = rasterizer->blit_x + rasterizer->blit_width; }
-  dst += (y0 - rasterizer->blit_y) * rasterizer->blit_stride;
-  int width = x1 - x0;
-  if (width > 0)
+  int idx = state->gstate.stringpool_pos;
+
+  if (idx + len > CTX_STRINGPOOL_SIZE)
+  {
+    ctx_log ("blowing varpool size [%c..]\n", data[0]);
+    //fprintf (stderr, "blowing varpool size [%c%c%c..]\n", data[0],data[1], data[1]?data[2]:0);
+#if 0
+    for (int i = 0; i< CTX_STRINGPOOL_SIZE; i++)
     {
-      rasterizer->scanline = y0 * aa;
-      for (int y = y0; y < y1; y++)
-        {
-          rasterizer->scanline += aa;
-          ctx_rasterizer_apply_coverage (rasterizer,
-                                         &dst[ (x0) * rasterizer->format->bpp/8],
-                                         x0,
-                                         coverage, width);
-          dst += rasterizer->blit_stride;
-        }
+       if (i==0) fprintf (stderr, "\n%i ", i);
+       else      fprintf (stderr, "%c", state->stringpool[i]);
     }
-  return 1;
+#endif
+    return;
+  }
+
+  memcpy (&state->stringpool[idx], data, len);
+  state->gstate.stringpool_pos+=len;
+  state->stringpool[state->gstate.stringpool_pos++]=0;
+  ctx_state_set (state, key, ctx_string_index_to_float (idx));
 }
 
-inline static int
-ctx_is_transparent (CtxRasterizer *rasterizer)
+static void ctx_state_set_string (CtxState *state, uint64_t key, const char *string)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  if (gstate->global_alpha_u8 == 0)
-    return 1;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  float old_val = ctx_state_get (state, key);
+  int   old_idx = ctx_float_to_string_index (old_val);
+
+  if (old_idx >= 0)
   {
-    uint8_t ga[2];
-    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source.color, ga);
-    if (ga[1] == 0)
-      return 1;
+    const char *old_string = ctx_state_get_string (state, key);
+    if (old_string && !strcmp (old_string, string))
+      return;
   }
-  return 0;
+
+  if (ctx_str_is_number (string))
+  {
+    ctx_state_set (state, key, strtod (string, NULL));
+    return;
+  }
+  // should do same with color
+ 
+  // XXX should special case when the string modified is at the
+  //     end of the stringpool.
+  //
+  //     for clips the behavior is howevre ideal, since
+  //     we can have more than one clip per save/restore level
+  ctx_state_set_blob (state, key, (uint8_t*)string, strlen(string));
 }
 
-static void
-ctx_rasterizer_fill (CtxRasterizer *rasterizer)
+static int ctx_state_get_color (CtxState *state, uint64_t key, CtxColor *color)
 {
-  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
-  int aa = rasterizer->aa;
-
-  CtxEntry temp[count]; /* copy of already built up path's poly line
-                          XXX - by building a large enough path
-                          the stack can be smashed!
-                         */
-  if (rasterizer->preserve)
-    { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); }
-
-#if CTX_ENABLE_SHADOW_BLUR
-  if (rasterizer->in_shadow)
+  CtxColor *stored = (CtxColor*)ctx_state_get_blob (state, key);
+  if (stored)
   {
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
+    if (stored->magic == 127)
     {
-      CtxEntry *entry = &rasterizer->edge_list.entries[i];
-      entry->data.s16[2] += rasterizer->shadow_x * CTX_SUBDIV;
-      entry->data.s16[3] += rasterizer->shadow_y * aa;
+      *color = *stored;
+      return 0;
     }
-    rasterizer->scan_min += rasterizer->shadow_y * aa;
-    rasterizer->scan_max += rasterizer->shadow_y * aa;
-    rasterizer->col_min  += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
-    rasterizer->col_max  += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
   }
-#endif
+  return -1;
+}
 
-  if (ctx_is_transparent (rasterizer) ||
-      rasterizer->scan_min / aa > rasterizer->blit_y + rasterizer->blit_height ||
-      rasterizer->scan_max / aa < rasterizer->blit_y ||
-      rasterizer->col_min / CTX_SUBDIV > rasterizer->blit_x + rasterizer->blit_width ||
-      rasterizer->col_max / CTX_SUBDIV < rasterizer->blit_x)
-    {
-      ctx_rasterizer_reset (rasterizer);
-    }
-  else
+static void ctx_state_set_color (CtxState *state, uint64_t key, CtxColor *color)
+{
+  CtxColor mod_color;
+  CtxColor old_color;
+  mod_color = *color;
+  mod_color.magic = 127;
+  if (ctx_state_get_color (state, key, &old_color)==0)
   {
-    ctx_compositor_setup_default (rasterizer);
-
-    rasterizer->state->min_x =
-      ctx_mini (rasterizer->state->min_x, rasterizer->col_min / CTX_SUBDIV);
-    rasterizer->state->max_x =
-      ctx_maxi (rasterizer->state->max_x, rasterizer->col_max / CTX_SUBDIV);
-    rasterizer->state->min_y =
-      ctx_mini (rasterizer->state->min_y, rasterizer->scan_min / aa);
-    rasterizer->state->max_y =
-      ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / aa);
-    if (rasterizer->edge_list.count == 6)
-      {
-        CtxEntry *entry0 = &rasterizer->edge_list.entries[0];
-        CtxEntry *entry1 = &rasterizer->edge_list.entries[1];
-        CtxEntry *entry2 = &rasterizer->edge_list.entries[2];
-        CtxEntry *entry3 = &rasterizer->edge_list.entries[3];
-        if (!rasterizer->state->gstate.clipped &&
-             (entry0->data.s16[2] == entry1->data.s16[2]) &&
-             (entry0->data.s16[3] == entry3->data.s16[3]) &&
-             (entry1->data.s16[3] == entry2->data.s16[3]) &&
-             (entry2->data.s16[2] == entry3->data.s16[2]) &&
-             ((entry3->data.s16[2] & (CTX_SUBDIV-1)) == 0)  &&
-             ((entry3->data.s16[3] & (aa-1)) == 0)
-#if CTX_ENABLE_SHADOW_BLUR
-             && !rasterizer->in_shadow
-#endif
-           )
-        if (ctx_rasterizer_fill_rect (rasterizer,
-                                      entry3->data.s16[2],
-                                      entry3->data.s16[3],
-                                      entry1->data.s16[2],
-                                      entry1->data.s16[3]) )
-          {
-            ctx_rasterizer_reset (rasterizer);
-            goto done;
-          }
-      }
-    ctx_rasterizer_finish_shape (rasterizer);
-
-    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
+    if (!memcmp (&mod_color, &old_color, sizeof (mod_color)))
+      return;
+  }
+  ctx_state_set_blob (state, key, (uint8_t*)&mod_color, sizeof (CtxColor));
+}
 
-#if CTX_SHAPE_CACHE
-    int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1;
-    int height = (rasterizer->scan_max + (aa-1) ) / aa - rasterizer->scan_min / aa + 1;
-    if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1
-        && width < CTX_SHAPE_CACHE_MAX_DIM
-        && height < CTX_SHAPE_CACHE_MAX_DIM 
-        && !rasterizer->state->gstate.clipped // XXX  - this causes any clipping, also rectangular -
-                                              //        to disable caching
-#if CTX_ENABLE_SHADOW_BLUR
-        && !rasterizer->in_shadow
-#endif
-        )
-      {
-        int scan_min = rasterizer->scan_min;
-        int col_min = rasterizer->col_min;
-        scan_min -= (scan_min % aa);
-        int y0 = scan_min / aa;
-        int y1 = y0 + height;
-        int x0 = col_min / CTX_SUBDIV;
-        int ymin = y0;
-        int x1 = x0 + width;
-        int clip_x_min = rasterizer->blit_x;
-        int clip_x_max = rasterizer->blit_x + rasterizer->blit_width - 1;
-        int clip_y_min = rasterizer->blit_y;
-        int clip_y_max = rasterizer->blit_y + rasterizer->blit_height - 1;
-
-        int dont_cache = 0;
-        if (x1 >= clip_x_max)
-          { x1 = clip_x_max;
-            dont_cache = 1;
-          }
-        int xo = 0;
-        if (x0 < clip_x_min)
-          {
-            xo = clip_x_min - x0;
-            x0 = clip_x_min;
-            dont_cache = 1;
-          }
-        if (y0 < clip_y_min || y1 >= clip_y_max)
-          dont_cache = 1;
-        if (dont_cache)
-        {
-          ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
-#if CTX_SHAPE_CACHE
-                                        , NULL
-#endif
-                                       );
-        }
-        else
-        {
-
-        rasterizer->scanline = scan_min;
-        CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); 
-
-        if (shape->uses == 0)
-          {
-            ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape);
-          }
-        rasterizer->scanline = scan_min;
-
-        int ewidth = x1 - x0;
-        if (ewidth>0)
-          for (int y = y0; y < y1; y++)
-            {
-              if ( (y >= clip_y_min) && (y <= clip_y_max) )
-                {
-                  ctx_rasterizer_apply_coverage (rasterizer,
-                                                 ( (uint8_t *) rasterizer->buf) + (y-rasterizer->blit_y) * 
rasterizer->blit_stride + (int) (x0) * rasterizer->format->bpp/8,
-                                                 x0,
-                                                 &shape->data[shape->width * (int) (y-ymin) + xo],
-                                                 ewidth );
-                }
-               rasterizer->scanline += aa;
-            }
-        if (shape->uses != 0)
-          {
-            ctx_rasterizer_reset (rasterizer);
-          }
-        }
-      }
-    else
-#endif
-    ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
-#if CTX_SHAPE_CACHE
-                                    , NULL
-#endif
-                                   );
-  }
-  done:
-  if (rasterizer->preserve)
-    {
-      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
-    }
-#if CTX_ENABLE_SHADOW_BLUR
-  if (rasterizer->in_shadow)
-  {
-    rasterizer->scan_min -= rasterizer->shadow_y * aa;
-    rasterizer->scan_max -= rasterizer->shadow_y * aa;
-    rasterizer->col_min  -= (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
-    rasterizer->col_max  -= (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
-  }
-#endif
-  rasterizer->preserve = 0;
+const char *ctx_get_string (Ctx *ctx, uint64_t hash)
+{
+  return ctx_state_get_string (&ctx->state, hash);
 }
-
-#if 0
-static void
-ctx_rasterizer_triangle (CtxRasterizer *rasterizer,
-                         int x0, int y0,
-                         int x1, int y1,
-                         int x2, int y2,
-                         int r0, int g0, int b0, int a0,
-                         int r1, int g1, int b1, int a1,
-                         int r2, int g2, int b2, int a2,
-                         int u0, int v0,
-                         int u1, int v1)
+float ctx_get_float (Ctx *ctx, uint64_t hash)
 {
-
+  return ctx_state_get (&ctx->state, hash);
 }
-#endif
-
-static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke);
-static void
-ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
+int ctx_get_int (Ctx *ctx, uint64_t hash)
 {
-  float tx = rasterizer->state->x;
-  float ty = rasterizer->state->y - rasterizer->state->gstate.font_size;
-  float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size;
-  float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size;
-  _ctx_user_to_device (rasterizer->state, &tx, &ty);
-  _ctx_user_to_device (rasterizer->state, &tx2, &ty2);
-
-  if (tx2 < rasterizer->blit_x || ty2 < rasterizer->blit_y) return;
-  if (tx  > rasterizer->blit_x + rasterizer->blit_width ||
-      ty  > rasterizer->blit_y + rasterizer->blit_height)
-          return;
-  _ctx_glyph (rasterizer->ctx, unichar, stroke);
+  return ctx_state_get (&ctx->state, hash);
 }
-
-typedef struct _CtxTermGlyph CtxTermGlyph;
-
-struct _CtxTermGlyph
+void ctx_set_float (Ctx *ctx, uint64_t hash, float value)
 {
-  uint32_t unichar;
-  int      col;
-  int      row;
-  uint8_t  rgba_bg[4];
-  uint8_t  rgba_fg[4];
-};
+  ctx_state_set (&ctx->state, hash, value);
+}
+void ctx_set_string (Ctx *ctx, uint64_t hash, const char *value)
+{
+  ctx_state_set_string (&ctx->state, hash, value);
+}
+void ctx_set_color (Ctx *ctx, uint64_t hash, CtxColor *color)
+{
+  ctx_state_set_color (&ctx->state, hash, color);
+}
+int  ctx_get_color (Ctx *ctx, uint64_t hash, CtxColor *color)
+{
+  return ctx_state_get_color (&ctx->state, hash, color);
+}
+int ctx_is_set (Ctx *ctx, uint64_t hash)
+{
+  return ctx_get_float (ctx, hash) != -0.0f;
+}
+int ctx_is_set_now (Ctx *ctx, uint64_t hash)
+{
+  return ctx_is_set (ctx, hash);
+}
+#if CTX_RASTERIZER
 
-static void
-_ctx_text (Ctx        *ctx,
-           const char *string,
-           int         stroke,
-           int         visible);
-static void
-ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke)
+void ctx_compositor_setup_default (CtxRasterizer *rasterizer);
+
+inline static void
+ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
+                               uint8_t * __restrict__ dst,
+                               int            x,
+                               uint8_t * __restrict__ coverage,
+                               int            count)
 {
-#if CTX_BRAILLE_TEXT
-  if (rasterizer->term_glyphs && !stroke)
-  {
-    int col = rasterizer->x / 2 + 1;
-    int row = rasterizer->y / 4 + 1;
-    for (int i = 0; string[i]; i++, col++)
-    {
-      CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
-      ctx_list_prepend (&rasterizer->glyphs, glyph);
-      glyph->unichar = string[i];
-      glyph->col = col;
-      glyph->row = row;
-      ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color,
-                      glyph->rgba_fg);
-    }
-    //_ctx_text (rasterizer->ctx, string, stroke, 1);
-  }
+  if (rasterizer->format->apply_coverage)
+    rasterizer->format->apply_coverage(rasterizer, dst, rasterizer->color, x, coverage, count);
   else
-#endif
-  {
-    _ctx_text (rasterizer->ctx, string, stroke, 1);
-  }
+    rasterizer->comp_op (rasterizer, dst, rasterizer->color, x, coverage, count);
 }
 
-void
-_ctx_font (Ctx *ctx, const char *name);
 static void
-ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
+ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
 {
-  _ctx_font (rasterizer->ctx, font_name);
+  CtxGradient *gradient = &rasterizer->state->gradient;
+  CtxGradientStop *stop = &gradient->stops[gradient->n_stops];
+  stop->pos = pos;
+  ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]);
+  if (gradient->n_stops < 15) //we'll keep overwriting the last when out of stops
+    { gradient->n_stops++; }
 }
 
-static void
-ctx_rasterizer_arc (CtxRasterizer *rasterizer,
-                    float        x,
-                    float        y,
-                    float        radius,
-                    float        start_angle,
-                    float        end_angle,
-                    int          anticlockwise)
+static int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
 {
-  int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS;
-  full_segments = radius * CTX_PI;
-  if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS)
-    { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; }
-  float step = CTX_PI*2.0/full_segments;
-  int steps;
+  CtxEntry entry = {CTX_EDGE, {{0},}};
+  if (y1 < rasterizer->scan_min)
+    { rasterizer->scan_min = y1; }
+  if (y1 > rasterizer->scan_max)
+    { rasterizer->scan_max = y1; }
 
-  if (end_angle < -10.0)
-    end_angle = -10.0;
-  if (start_angle < -10.0)
-    start_angle = -10.0;
-  if (end_angle > 10.0)
-    end_angle = 10.0;
-  if (start_angle > 10.0)
-    start_angle = 10.0;
+  if (x1 < rasterizer->col_min)
+    { rasterizer->col_min = x1; }
+  if (x1 > rasterizer->col_max)
+    { rasterizer->col_max = x1; }
 
-  if (radius <= 0.0001)
-          return;
+  entry.data.s16[2]=x1;
+  entry.data.s16[3]=y1;
+  return ctx_drawlist_add_single (&rasterizer->edge_list, &entry);
+}
 
-  if (end_angle == start_angle)
-          // XXX also detect arcs fully outside render view
-    {
-    if (rasterizer->has_prev!=0)
-      ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                              y + ctx_sinf (end_angle) * radius);
-      else
-      ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                            y + ctx_sinf (end_angle) * radius);
-      return;
-    }
 #if 0
-  if ( (!anticlockwise && (end_angle - start_angle >= CTX_PI*2) ) ||
-       ( (anticlockwise && (start_angle - end_angle >= CTX_PI*2) ) ) )
-    {
-      end_angle = start_angle;
-      steps = full_segments - 1;
-    }
-  else
+#define CTX_SHAPE_CACHE_PRIME1   7853
+#define CTX_SHAPE_CACHE_PRIME2   4129
+#define CTX_SHAPE_CACHE_PRIME3   3371
+#define CTX_SHAPE_CACHE_PRIME4   4221
+#else
+#define CTX_SHAPE_CACHE_PRIME1   283
+#define CTX_SHAPE_CACHE_PRIME2   599
+#define CTX_SHAPE_CACHE_PRIME3   101
+#define CTX_SHAPE_CACHE_PRIME4   661
 #endif
+
+float ctx_shape_cache_rate = 0.0;
+#if CTX_SHAPE_CACHE
+
+//static CtxShapeCache ctx_cache = {{NULL,}, 0};
+
+static long hits = 0;
+static long misses = 0;
+
+
+/* this returns the buffer to use for rendering, it always
+   succeeds..
+ */
+static CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int height)
+{
+  /* use both some high and some low bits  */
+  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
+  int i;
+  {
+    static int i = 0;
+    i++;
+    if (i>1000)
+      {
+        ctx_shape_cache_rate = hits * 100.0  / (hits+misses);
+        i = 0;
+        hits = 0;
+        misses = 0;
+      }
+  }
+// XXX : this 1 one is needed  to silence a false positive:
+// ==90718== Invalid write of size 1
+// ==90718==    at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786)
+// ==90718==    by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907)
+//
+  int size = sizeof (CtxShapeEntry) + width * height + 1;
+
+  i = entry_no;
+  if (rasterizer->shape_cache.entries[i])
     {
-      steps = (end_angle - start_angle) / (CTX_PI*2) * full_segments;
-      if (steps > full_segments)
-              steps = full_segments;
-      if (anticlockwise)
-        { steps = full_segments - steps; };
-    }
-  if (anticlockwise) { step = step * -1; }
-  int first = 1;
-  if (steps == 0 /* || steps==full_segments -1  || (anticlockwise && steps == full_segments) */)
-    {
-      float xv = x + ctx_cosf (start_angle) * radius;
-      float yv = y + ctx_sinf (start_angle) * radius;
-      if (!rasterizer->has_prev)
-        { ctx_rasterizer_move_to (rasterizer, xv, yv); }
-      first = 0;
+      CtxShapeEntry *entry = rasterizer->shape_cache.entries[i];
+      int old_size = sizeof (CtxShapeEntry) + width + height + 1;
+      if (entry->hash == hash &&
+          entry->width == width &&
+          entry->height == height)
+        {
+          if (entry->uses < 1<<30)
+            { entry->uses++; }
+          hits ++;
+          return entry;
+        }
+
+      if (old_size >= size)
+      {
+      }
+      else
+      {
+        rasterizer->shape_cache.entries[i] = NULL;
+        rasterizer->shape_cache.size -= entry->width * entry->height;
+        rasterizer->shape_cache.size -= sizeof (CtxShapeEntry);
+        free (entry);
+        rasterizer->shape_cache.entries[i] =(CtxShapeEntry *) calloc (size, 1);
+      }
     }
   else
     {
-      for (float angle = start_angle, i = 0; i < steps; angle += step, i++)
-        {
-          float xv = x + ctx_cosf (angle) * radius;
-          float yv = y + ctx_sinf (angle) * radius;
-          if (first && !rasterizer->has_prev)
-            { ctx_rasterizer_move_to (rasterizer, xv, yv); }
-          else
-            { ctx_rasterizer_line_to (rasterizer, xv, yv); }
-          first = 0;
-        }
+        rasterizer->shape_cache.entries[i] =(CtxShapeEntry *) calloc (size, 1);
     }
-  ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                          y + ctx_sinf (end_angle) * radius);
-}
 
-static void
-ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
-                        float        cx,
-                        float        cy,
-                        float        x,
-                        float        y)
-{
-  /* XXX : it is probably cheaper/faster to do quad interpolation directly -
-   *       though it will increase the code-size, an
-   *       alternative is to turn everything into cubic
-   *       and deal with cubics more directly during
-   *       rasterization
-   */
-  ctx_rasterizer_curve_to (rasterizer,
-                           (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f,
-                           (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                           x,                              y);
+  misses ++;
+  
+  rasterizer->shape_cache.size += size;
+  rasterizer->shape_cache.entries[i]->hash=hash;
+  rasterizer->shape_cache.entries[i]->width=width;
+  rasterizer->shape_cache.entries[i]->height=height;
+  rasterizer->shape_cache.entries[i]->uses = 0;
+  return rasterizer->shape_cache.entries[i];
 }
 
-static void
-ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
-                            float cx, float cy,
-                            float x,  float y)
+#endif
+
+static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
 {
-  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
-                          x  + rasterizer->x, y  + rasterizer->y);
+  int16_t x = 0;
+  int16_t y = 0;
+
+  CtxEntry *entry = &rasterizer->edge_list.entries[0];
+  int ox = entry->data.s16[2];
+  int oy = entry->data.s16[3];
+  uint32_t hash = rasterizer->edge_list.count;
+  hash = ox;//(ox % CTX_SUBDIV);
+  hash *= CTX_SHAPE_CACHE_PRIME1;
+  hash += oy; //(oy % CTX_RASTERIZER_AA);
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
+    {
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      x = entry->data.s16[2];
+      y = entry->data.s16[3];
+      int dx = x-ox;
+      int dy = y-oy;
+      ox = x;
+      oy = y;
+      hash *= CTX_SHAPE_CACHE_PRIME3;
+      hash += dx;
+      hash *= CTX_SHAPE_CACHE_PRIME4;
+      hash += dy;
+    }
+  return hash;
 }
 
-#define LENGTH_OVERSAMPLE 1
-static void
-ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
+static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
 {
-  // XXX - we avoid rendering here x==0 - to keep with
-  //  an off-by one elsewhere
-  //
-  //  XXX onlt works in rgba8 formats
-  if (x <= 0 || y < 0 || x >= rasterizer->blit_width ||
-      y >= rasterizer->blit_height)
-    { return; }
-  uint8_t fg_color[4];
-  ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, fg_color);
-  uint8_t pixel[4];
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-  dst += y * rasterizer->blit_stride;
-  dst += x * rasterizer->format->bpp / 8;
-  if (!rasterizer->format->to_comp ||
-      !rasterizer->format->from_comp)
-    { return; }
-  if (cov == 255)
+  int16_t x = 0;
+  int16_t y = 0;
+  if (rasterizer->edge_list.count == 0)
+     return 0;
+#if CTX_SHAPE_CACHE
+  int aa = rasterizer->aa;
+  CtxEntry *entry = &rasterizer->edge_list.entries[0];
+  int ox = entry->data.s16[2];
+  int oy = entry->data.s16[3];
+  uint32_t hash = rasterizer->edge_list.count;
+  hash = (ox % CTX_SUBDIV);
+  hash *= CTX_SHAPE_CACHE_PRIME1;
+  hash += (oy % aa);
+#endif
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
     {
-      for (int c = 0; c < 4; c++)
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      if (entry->code == CTX_NEW_EDGE)
         {
-          pixel[c] = fg_color[c];
+          entry->code = CTX_EDGE;
+#if CTX_SHAPE_CACHE
+          hash *= CTX_SHAPE_CACHE_PRIME2;
+#endif
         }
-    }
-  else
-    {
-      rasterizer->format->to_comp (rasterizer, x, dst, &pixel[0], 1);
-      for (int c = 0; c < 4; c++)
+      else
         {
-          pixel[c] = ctx_lerp_u8 (pixel[c], fg_color[c], cov);
+          entry->data.s16[0] = x;
+          entry->data.s16[1] = y;
+        }
+      x = entry->data.s16[2];
+      y = entry->data.s16[3];
+#if CTX_SHAPE_CACHE
+      int dx = x-ox;
+      int dy = y-oy;
+      ox = x;
+      oy = y;
+      hash *= CTX_SHAPE_CACHE_PRIME3;
+      hash += dx;
+      hash *= CTX_SHAPE_CACHE_PRIME4;
+      hash += dy;
+#endif
+      if (entry->data.s16[3] < entry->data.s16[1])
+        {
+          *entry = ctx_s16 (CTX_EDGE_FLIPPED,
+                            entry->data.s16[2], entry->data.s16[3],
+                            entry->data.s16[0], entry->data.s16[1]);
         }
     }
-  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
+#if CTX_SHAPE_CACHE
+  return hash;
+#else
+  return 0;
+#endif
 }
 
-static void
-ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
+static void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y);
+
+static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
 {
-  int count = rasterizer->edge_list.count;
-  CtxEntry *temp = rasterizer->edge_list.entries;
-  float prev_x = 0.0f;
-  float prev_y = 0.0f;
-  int aa = rasterizer->aa;
-  int start = 0;
-  int end = 0;
-#if 0
-  float factor = 
-     ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
-                         ctx_fabsf (state->gstate.transform.m[0][1]) ),
-               ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
-                         ctx_fabsf (state->gstate.transform.m[1][1]) ) );
-#endif
+  if (rasterizer->has_shape && rasterizer->has_prev)
+    {
+      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
+      rasterizer->has_prev = 0;
+    }
+}
 
-  while (start < count)
+static void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  float tx = x; float ty = y;
+  int aa = rasterizer->aa;
+  rasterizer->x        = x;
+  rasterizer->y        = y;
+  rasterizer->first_x  = x;
+  rasterizer->first_y  = y;
+  rasterizer->has_prev = -1;
+  if (rasterizer->uses_transforms)
     {
-      int started = 0;
-      int i;
-      for (i = start; i < count; i++)
-        {
-          CtxEntry *entry = &temp[i];
-          float x, y;
-          if (entry->code == CTX_NEW_EDGE)
-            {
-              if (started)
-                {
-                  end = i - 1;
-                  goto foo;
-                }
-              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-              prev_y = entry->data.s16[1] * 1.0f / aa;
-              started = 1;
-              start = i;
-            }
-          x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-          y = entry->data.s16[3] * 1.0f / aa;
-          int dx = x - prev_x;
-          int dy = y - prev_y;
-          int length = ctx_maxf (abs (dx), abs (dy) );
-          if (length)
-            {
-              length *= LENGTH_OVERSAMPLE;
-              int len = length;
-              int tx = prev_x * 256;
-              int ty = prev_y * 256;
-              dx *= 256;
-              dy *= 256;
-              dx /= length;
-              dy /= length;
-              for (int i = 0; i < len; i++)
-                {
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
-                  tx += dx;
-                  ty += dy;
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
-                }
-            }
-          prev_x = x;
-          prev_y = y;
-        }
-      end = i-1;
-foo:
-      start = end+1;
+      _ctx_user_to_device (rasterizer->state, &tx, &ty);
     }
-  ctx_rasterizer_reset (rasterizer);
+
+  tx = (tx - rasterizer->blit_x) * CTX_SUBDIV;
+  ty = ty * aa;
+
+  if (ty < rasterizer->scan_min)
+    { rasterizer->scan_min = ty; }
+  if (ty > rasterizer->scan_max)
+    { rasterizer->scan_max = ty; }
+  if (tx < rasterizer->col_min)
+    { rasterizer->col_min = tx; }
+  if (tx > rasterizer->col_max)
+    { rasterizer->col_max = tx; }
+}
+
+static void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  float tx = x;
+  float ty = y;
+  float ox = rasterizer->x;
+  float oy = rasterizer->y;
+  if (rasterizer->uses_transforms)
+    {
+      _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    }
+  tx -= rasterizer->blit_x;
+#define MIN_Y -1000
+#define MAX_Y 1400
+
+  if (ty < MIN_Y) ty = MIN_Y;
+  if (ty > MAX_Y) ty = MAX_Y;
+  ctx_rasterizer_add_point (rasterizer, tx * CTX_SUBDIV, ty * rasterizer->aa);
+  if (rasterizer->has_prev<=0)
+    {
+      if (rasterizer->uses_transforms)
+      {
+        // storing transformed would save some processing for a tiny
+        // amount of runtime RAM XXX
+        _ctx_user_to_device (rasterizer->state, &ox, &oy);
+      }
+      ox -= rasterizer->blit_x;
+
+  if (oy < MIN_Y) oy = MIN_Y;
+  if (oy > MAX_Y) oy = MAX_Y;
+
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[0] = ox * CTX_SUBDIV;
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[1] = oy * rasterizer->aa;
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].code = CTX_NEW_EDGE;
+      rasterizer->has_prev = 1;
+    }
+  rasterizer->has_shape = 1;
+  rasterizer->y         = y;
+  rasterizer->x         = x;
+}
+
+
+CTX_INLINE static float
+ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
+{
+  float ab   = ctx_lerpf (x0, x1, dt);
+  float bc   = ctx_lerpf (x1, x2, dt);
+  float cd   = ctx_lerpf (x2, x3, dt);
+  float abbc = ctx_lerpf (ab, bc, dt);
+  float bccd = ctx_lerpf (bc, cd, dt);
+  return ctx_lerpf (abbc, bccd, dt);
+}
+
+inline static void
+ctx_bezier_sample (float x0, float y0,
+                   float x1, float y1,
+                   float x2, float y2,
+                   float x3, float y3,
+                   float dt, float *x, float *y)
+{
+  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
+  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
 }
 
 static void
-ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
+ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer,
+                              float ox, float oy,
+                              float x0, float y0,
+                              float x1, float y1,
+                              float x2, float y2,
+                              float sx, float sy,
+                              float ex, float ey,
+                              float s,
+                              float e,
+                              int   iteration,
+                              float tolerance)
 {
-  CtxState *state = rasterizer->state;
-  int count = rasterizer->edge_list.count;
-  int preserved = rasterizer->preserve;
-  // YYY
-  float factor = ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
-                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
-                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
-                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
-  int aa = rasterizer->aa;
-  CtxEntry temp[count]; /* copy of already built up path's poly line  */
-  memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) );
-#if 1
-  if (rasterizer->state->gstate.line_width * factor <= 0.0f &&
-      rasterizer->state->gstate.line_width * factor > -10.0f)
+  if (iteration > 8)
+    { return; }
+  float t = (s + e) * 0.5f;
+  float x, y, lx, ly, dx, dy;
+  ctx_bezier_sample (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y);
+  if (iteration)
     {
-      ctx_rasterizer_stroke_1px (rasterizer);
+      lx = ctx_lerpf (sx, ex, t);
+      ly = ctx_lerpf (sy, ey, t);
+      dx = lx - x;
+      dy = ly - y;
+      if ( (dx*dx+dy*dy) < tolerance)
+        /* bailing - because for the mid-point straight line difference is
+           tiny */
+        { return; }
+      dx = sx - ex;
+      dy = ey - ey;
+      if ( (dx*dx+dy*dy) < tolerance)
+        /* bailing on tiny segments */
+        { return; }
+    }
+  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
+                                sx, sy, x, y, s, t, iteration + 1,
+                                tolerance);
+  ctx_rasterizer_line_to (rasterizer, x, y);
+  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
+                                x, y, ex, ey, t, e, iteration + 1,
+                                tolerance);
+}
+
+static void
+ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
+                         float x0, float y0,
+                         float x1, float y1,
+                         float x2, float y2)
+{
+  float tolerance =
+    ctx_pow2 (rasterizer->state->gstate.transform.m[0][0]) +
+    ctx_pow2 (rasterizer->state->gstate.transform.m[1][1]);
+  float ox = rasterizer->x;
+  float oy = rasterizer->y;
+  ox = rasterizer->state->x;
+  oy = rasterizer->state->y;
+  tolerance = 1.0f/tolerance * 2;
+#if 1 // skipping this to preserve hash integrity
+  if (tolerance == 1.0f || 1)
+  {
+  float maxx = ctx_maxf (x1,x2);
+  maxx = ctx_maxf (maxx, ox);
+  maxx = ctx_maxf (maxx, x0);
+  float maxy = ctx_maxf (y1,y2);
+  maxy = ctx_maxf (maxy, oy);
+  maxy = ctx_maxf (maxy, y0);
+  float minx = ctx_minf (x1,x2);
+  minx = ctx_minf (minx, ox);
+  minx = ctx_minf (minx, x0);
+  float miny = ctx_minf (y1,y2);
+  miny = ctx_minf (miny, oy);
+  miny = ctx_minf (miny, y0);
+  
+  _ctx_user_to_device (rasterizer->state, &minx, &miny);
+  _ctx_user_to_device (rasterizer->state, &maxx, &maxy);
+
+    if(
+        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
+        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
+        (maxx < rasterizer->blit_x) ||
+        (maxy < rasterizer->blit_y) )
+    {
+    }
+    else
+    {
+      ctx_rasterizer_bezier_divide (rasterizer,
+                                    ox, oy, x0, y0,
+                                    x1, y1, x2, y2,
+                                    ox, oy, x2, y2,
+                                    0.0f, 1.0f, 0.0f, tolerance);
     }
+  }
   else
 #endif
     {
-      ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape  */
-      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
-      ctx_matrix_identity (&rasterizer->state->gstate.transform);
-      float prev_x = 0.0f;
-      float prev_y = 0.0f;
-      float half_width_x = rasterizer->state->gstate.line_width * factor/2;
-      float half_width_y = rasterizer->state->gstate.line_width * factor/2;
-      if (rasterizer->state->gstate.line_width <= 0.0f)
-        {
-          half_width_x = .5;
-          half_width_y = .5;
-        }
-      int start = 0;
-      int end   = 0;
-      while (start < count)
-        {
-          int started = 0;
-          int i;
-          for (i = start; i < count; i++)
-            {
-              CtxEntry *entry = &temp[i];
-              float x, y;
-              if (entry->code == CTX_NEW_EDGE)
-                {
-                  if (started)
-                    {
-                      end = i - 1;
-                      goto foo;
-                    }
-                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  prev_y = entry->data.s16[1] * 1.0f / aa;
-                  started = 1;
-                  start = i;
-                }
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              float dx = x - prev_x;
-              float dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-              if (length>0.001f)
-                {
-                  dx = dx/length * half_width_x;
-                  dy = dy/length * half_width_y;
-                  if (entry->code == CTX_NEW_EDGE)
-                    {
-                      ctx_rasterizer_finish_shape (rasterizer);
-                      ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx);
-                    }
-                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                  // XXX possible miter line-to
-                  ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
-                }
-              prev_x = x;
-              prev_y = y;
-            }
-          end = i-1;
-foo:
-          for (int i = end; i >= start; i--)
-            {
-              CtxEntry *entry = &temp[i];
-              float x, y, dx, dy;
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              dx = x - prev_x;
-              dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-              dx = dx/length * half_width_x;
-              dy = dy/length * half_width_y;
-              if (length>0.001f)
-                {
-                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                  // XXX possible miter line-to
-                  ctx_rasterizer_line_to (rasterizer, x-dy,      y+dx);
-                }
-              prev_x = x;
-              prev_y = y;
-              if (entry->code == CTX_NEW_EDGE)
-                {
-                  x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[1] * 1.0f / aa;
-                  dx = x - prev_x;
-                  dy = y - prev_y;
-                  length = ctx_fast_hypotf (dx, dy);
-                  if (length>0.001f)
-                    {
-                      dx = dx / length * half_width_x;
-                      dy = dy / length * half_width_y;
-                      ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                      ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
-                    }
-                }
-              if ( (prev_x != x) && (prev_y != y) )
-                {
-                  prev_x = x;
-                  prev_y = y;
-                }
-            }
-          start = end+1;
-        }
-      ctx_rasterizer_finish_shape (rasterizer);
-      switch (rasterizer->state->gstate.line_cap)
-        {
-          case CTX_CAP_SQUARE: // XXX:NYI
-          case CTX_CAP_NONE: /* nothing to do */
-            break;
-          case CTX_CAP_ROUND:
-            {
-              float x = 0, y = 0;
-              int has_prev = 0;
-              for (int i = 0; i < count; i++)
-                {
-                  CtxEntry *entry = &temp[i];
-                  if (entry->code == CTX_NEW_EDGE)
-                    {
-                      if (has_prev)
-                        {
-                          ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-                          ctx_rasterizer_finish_shape (rasterizer);
-                        }
-                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                      y = entry->data.s16[1] * 1.0f / aa;
-                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-                      ctx_rasterizer_finish_shape (rasterizer);
-                    }
-                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[3] * 1.0f / aa;
-                  has_prev = 1;
-                }
-              ctx_rasterizer_move_to (rasterizer, x, y);
-              ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-              ctx_rasterizer_finish_shape (rasterizer);
-              break;
-            }
-        }
-      switch (rasterizer->state->gstate.line_join)
-        {
-          case CTX_JOIN_BEVEL:
-          case CTX_JOIN_MITER:
-            break;
-          case CTX_JOIN_ROUND:
-            {
-              float x = 0, y = 0;
-              for (int i = 0; i < count-1; i++)
-                {
-                  CtxEntry *entry = &temp[i];
-                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[3] * 1.0f / aa;
-                  if (entry[1].code == CTX_EDGE)
-                    {
-                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1);
-                      ctx_rasterizer_finish_shape (rasterizer);
-                    }
-                }
-              break;
-            }
-        }
-      CtxFillRule rule_backup = rasterizer->state->gstate.fill_rule;
-      rasterizer->state->gstate.fill_rule = CTX_FILL_RULE_WINDING;
-      rasterizer->preserve = 0; // so fill isn't tripped
-      ctx_rasterizer_fill (rasterizer);
-      rasterizer->state->gstate.fill_rule = rule_backup;
-      //rasterizer->state->gstate.source = source_backup;
-      rasterizer->state->gstate.transform = transform_backup;
-    }
-  if (preserved)
-    {
-      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
-      rasterizer->preserve = 0;
+      ctx_rasterizer_bezier_divide (rasterizer,
+                                    ox, oy, x0, y0,
+                                    x1, y1, x2, y2,
+                                    ox, oy, x2, y2,
+                                    0.0f, 1.0f, 0.0f, tolerance);
     }
+  ctx_rasterizer_line_to (rasterizer, x2, y2);
 }
 
-#if CTX_1BIT_CLIP
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1
-#else
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8
-#endif
-
-
 static void
-ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer)
+ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
 {
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
-   ctx_buffer_free (rasterizer->clip_buffer);
-  rasterizer->clip_buffer = NULL;
-#endif
-  rasterizer->state->gstate.clip_min_x = rasterizer->blit_x;
-  rasterizer->state->gstate.clip_min_y = rasterizer->blit_y;
+  if (x == 0.f && y == 0.f)
+    { return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_move_to (rasterizer, x, y);
+}
 
-  rasterizer->state->gstate.clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1;
-  rasterizer->state->gstate.clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1;
+static void
+ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  if (x== 0.f && y==0.f)
+    { return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_line_to (rasterizer, x, y);
 }
 
 static void
-ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
-                           CtxEntry      *edges)
+ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer,
+                             float x0, float y0, float x1, float y1, float x2, float y2)
 {
-  int count = edges[0].data.u32[0];
-  int aa = rasterizer->aa;
+  x0 += rasterizer->x;
+  y0 += rasterizer->y;
+  x1 += rasterizer->x;
+  y1 += rasterizer->y;
+  x2 += rasterizer->x;
+  y2 += rasterizer->y;
+  ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2);
+}
 
-  int minx = 5000;
-  int miny = 5000;
-  int maxx = -5000;
-  int maxy = -5000;
-  int prev_x = 0;
-  int prev_y = 0;
-  for (int i = 0; i < count; i++)
-    {
-      CtxEntry *entry = &edges[i+1];
-      float x, y;
-      if (entry->code == CTX_NEW_EDGE)
-        {
-          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-          prev_y = entry->data.s16[1] * 1.0f / aa;
-          if (prev_x < minx) { minx = prev_x; }
-          if (prev_y < miny) { miny = prev_y; }
-          if (prev_x > maxx) { maxx = prev_x; }
-          if (prev_y > maxy) { maxy = prev_y; }
-        }
-      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-      y = entry->data.s16[3] * 1.0f / aa;
-      if (x < minx) { minx = x; }
-      if (y < miny) { miny = y; }
-      if (x > maxx) { maxx = x; }
-      if (y > maxy) { maxy = y; }
-    }
 
-#if CTX_ENABLE_CLIP
-  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
+static int
+ctx_rasterizer_find_texture (CtxRasterizer *rasterizer,
+                             const char *eid)
+{
+  int no;
+  for (no = 0; no < CTX_MAX_TEXTURES; no++)
   {
-    ctx_rasterizer_clip_reset (rasterizer);
-    return;//goto done;
+    if (rasterizer->texture_source->texture[no].data &&
+        rasterizer->texture_source->texture[no].eid &&
+        !strcmp (rasterizer->texture_source->texture[no].eid, eid))
+      return no;
   }
+  return -1;
+}
 
-  int we_made_it = 0;
-  CtxBuffer *clip_buffer;
- 
-
-  if (!rasterizer->clip_buffer)
-  {
-    rasterizer->clip_buffer = ctx_buffer_new (rasterizer->blit_width,
-                                              rasterizer->blit_height,
-                                              CTX_CLIP_FORMAT);
-    clip_buffer = rasterizer->clip_buffer;
-    we_made_it = 1;
-    if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
-    memset (rasterizer->clip_buffer->data, 0, rasterizer->blit_width * rasterizer->blit_height/8);
-    else
-    memset (rasterizer->clip_buffer->data, 0, rasterizer->blit_width * rasterizer->blit_height);
-  }
+static void
+ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
+                            const char *eid,
+                            float x,
+                            float y)
+{
+  int no = ctx_rasterizer_find_texture (rasterizer, eid);
+  if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; }
+  if (rasterizer->texture_source->texture[no].data == NULL)
+    {
+      fprintf (stderr, "ctx tex fail %p %s %i\n", rasterizer->texture_source, eid, no);
+      return;
+    }
   else
   {
-    clip_buffer = ctx_buffer_new (rasterizer->blit_width,
-                                  rasterizer->blit_height,
-                                  CTX_CLIP_FORMAT);
+    rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame;
   }
+  rasterizer->state->gstate.source_fill.type = CTX_SOURCE_TEXTURE;
+  rasterizer->state->gstate.source_fill.texture.buffer = &rasterizer->texture_source->texture[no];
+  //ctx_user_to_device (rasterizer->state, &x, &y);
+  rasterizer->state->gstate.source_fill.texture.x0 = 0;
+  rasterizer->state->gstate.source_fill.texture.y0 = 0;
+  rasterizer->state->gstate.source_fill.transform = rasterizer->state->gstate.transform;
+  ctx_matrix_translate (&rasterizer->state->gstate.source_fill.transform, x, y);
+  ctx_matrix_invert (&rasterizer->state->gstate.source_fill.transform);
+}
+
+
+static void ctx_rasterizer_define_texture (CtxRasterizer *rasterizer,
+                                           const char *eid,
+                                           int width,
+                                           int height,
+                                           int format,
+                                           char unsigned *data)
+{
+  _ctx_texture_lock (); // we're using the same texture_source from all threads, keeping allocaitons down
+                        // need synchronizing (it could be better to do a pre-pass)
+  ctx_texture_init (rasterizer->texture_source,
+                    eid,
+                    width,
+                    height,
+                    ctx_pixel_format_get_stride ((CtxPixelFormat)format, width),
+                    (CtxPixelFormat)format,
+#if CTX_ENABLE_CM
+                    (void*)rasterizer->state->gstate.texture_space,
+#else
+                    NULL,
+#endif
+                    data,
+                    ctx_buffer_pixels_free, (void*)23);
+                    /*  when userdata for ctx_buffer_pixels_free is 23, texture_init dups the data on
+                     *  use
+                     */
 
-  // for now only one level of clipping is supported
-  {
+  _ctx_texture_unlock ();
+  ctx_rasterizer_set_texture (rasterizer, eid, 0.0, 0.0);
+}
 
-  int prev_x = 0;
-  int prev_y = 0;
 
-    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, rasterizer->blit_width, rasterizer->blit_height,
-       rasterizer->blit_width,
-       CTX_CLIP_FORMAT);
+CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp)
+{
+  const CtxEntry *a = (const CtxEntry *) ap;
+  const CtxEntry *b = (const CtxEntry *) bp;
+  int ycompare = a->data.s16[1] - b->data.s16[1];
+  if (ycompare)
+    { return ycompare; }
+  int xcompare = a->data.s16[0] - b->data.s16[0];
+  return xcompare;
+}
 
-  for (int i = 0; i < count; i++)
+CTX_INLINE static int ctx_edge_qsort_partition (CtxEntry *A, int low, int high)
+{
+  CtxEntry pivot = A[ (high+low) /2];
+  int i = low;
+  int j = high;
+  while (i <= j)
     {
-      CtxEntry *entry = &edges[i+1];
-      float x, y;
-      if (entry->code == CTX_NEW_EDGE)
+      while (ctx_compare_edges (&A[i], &pivot) <0) { i ++; }
+      while (ctx_compare_edges (&pivot, &A[j]) <0) { j --; }
+      if (i <= j)
         {
-          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-          prev_y = entry->data.s16[1] * 1.0f / aa;
-          ctx_move_to (ctx, prev_x, prev_y);
+          CtxEntry tmp = A[i];
+          A[i] = A[j];
+          A[j] = tmp;
+          i++;
+          j--;
         }
-      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-      y = entry->data.s16[3] * 1.0f / aa;
-      ctx_line_to (ctx, x, y);
     }
-    ctx_gray (ctx, 1.0f);
-    ctx_fill (ctx);
-    ctx_free (ctx);
-  }
+  return i;
+}
 
-  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
-  {
-    for (int i = 0; i < rasterizer->blit_width * rasterizer->blit_height/8; i++)
-    {
-      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
-      (((uint8_t*)rasterizer->clip_buffer->data)[i] &
-      ((uint8_t*)clip_buffer->data)[i]);
-    }
-  }
-  else
+static void ctx_edge_qsort (CtxEntry *entries, int low, int high)
+{
   {
-    for (int i = 0; i < rasterizer->blit_width * rasterizer->blit_height; i++)
-    {
-      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
-      (((uint8_t*)rasterizer->clip_buffer->data)[i] *
-      ((uint8_t*)clip_buffer->data)[i])/255;
-    }
+    int p = ctx_edge_qsort_partition (entries, low, high);
+    if (low < p -1 )
+      { ctx_edge_qsort (entries, low, p - 1); }
+    if (low < high)
+      { ctx_edge_qsort (entries, p, high); }
   }
-  if (!we_made_it)
-   ctx_buffer_free (clip_buffer);
-#endif
-  
-  rasterizer->state->gstate.clip_min_x = ctx_maxi (minx,
-                                         rasterizer->state->gstate.clip_min_x);
-  rasterizer->state->gstate.clip_min_y = ctx_maxi (miny,
-                                         rasterizer->state->gstate.clip_min_y);
-  rasterizer->state->gstate.clip_max_x = ctx_mini (maxx,
-                                         rasterizer->state->gstate.clip_max_x);
-  rasterizer->state->gstate.clip_max_y = ctx_mini (maxy,
-                                         rasterizer->state->gstate.clip_max_y);
-//done:
-
 }
 
-static void
-ctx_rasterizer_clip (CtxRasterizer *rasterizer)
+static void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
 {
-  int count = rasterizer->edge_list.count;
-  CtxEntry temp[count+1]; /* copy of already built up path's poly line  */
-  rasterizer->state->has_clipped=1;
-  rasterizer->state->gstate.clipped=1;
-  //if (rasterizer->preserve)
-    { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0]));
-      temp[0].code = CTX_NOP;
-      temp[0].data.u32[0] = count;
-      ctx_state_set_blob (rasterizer->state, CTX_clip, (uint8_t*)temp, sizeof(temp));
-    }
-  ctx_rasterizer_clip_apply (rasterizer, temp);
-
-  ctx_rasterizer_reset (rasterizer);
-  if (rasterizer->preserve)
+  if (rasterizer->edge_list.count > 1)
     {
-      memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0]));
-      rasterizer->edge_list.count = count;
-      rasterizer->preserve = 0;
+      ctx_edge_qsort (& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
     }
 }
 
-static void
-ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
-                            int   no,
-                            float x,
-                            float y)
-{
-  if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; }
-  if (rasterizer->texture_source->texture[no].data == NULL)
-    {
-      ctx_log ("failed setting texture %i\n", no);
-      return;
-    }
-  rasterizer->state->gstate.source.type = CTX_SOURCE_IMAGE;
-  rasterizer->state->gstate.source.image.buffer = &rasterizer->texture_source->texture[no];
-  //ctx_user_to_device (rasterizer->state, &x, &y);
-  rasterizer->state->gstate.source.image.x0 = 0;
-  rasterizer->state->gstate.source.image.y0 = 0;
-  rasterizer->state->gstate.source.transform = rasterizer->state->gstate.transform;
-  ctx_matrix_translate (&rasterizer->state->gstate.source.transform, x, y);
-  ctx_matrix_invert (&rasterizer->state->gstate.source.transform);
-}
 
-#if 0
-static void
-ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
-                           const char  *path,
-                           float x,
-                           float y)
+static void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
 {
-  // decode PNG, put it in image is slot 1,
-  // magic width height stride format data
-  ctx_buffer_load_png (&rasterizer->ctx->texture[0], path);
-  ctx_rasterizer_set_texture (rasterizer, 0, x, y);
-}
+  int scanline = rasterizer->scanline;
+  int aa = rasterizer->aa;
+#if CTX_RASTERIZER_FORCE_AA==0
+  rasterizer->ending_edges = 0;
 #endif
-
-static void
-ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
-                          uint16_t x,
-                          uint16_t y,
-                          uint8_t r,
-                          uint8_t g,
-                          uint8_t b,
-                          uint8_t a)
-{
-  rasterizer->state->gstate.source.type = CTX_SOURCE_COLOR;
-  ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source.color, r, g, b, a);
-#if 0
-  // XXX : doesn't take transforms into account
-  ctx_rasterizer_pset (rasterizer, x, y, 255);
-#else
-  ctx_rasterizer_move_to (rasterizer, x, y);
-  ctx_rasterizer_rel_line_to (rasterizer, 1, 0);
-  ctx_rasterizer_rel_line_to (rasterizer, 0, 1);
-  ctx_rasterizer_rel_line_to (rasterizer, -1, 0);
-  ctx_rasterizer_fill (rasterizer);
+  for (int i = 0; i < rasterizer->active_edges; i++)
+    {
+      int edge_end =rasterizer->edge_list.entries[rasterizer->edges[i].index].data.s16[3];
+      if (edge_end < scanline)
+        {
+          int dx_dy = rasterizer->edges[i].dx;
+          if (abs(dx_dy)> CTX_RASTERIZER_AA_SLOPE_LIMIT)
+            { rasterizer->needs_aa --; }
+          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
+          rasterizer->active_edges--;
+          i--;
+        }
+#if CTX_RASTERIZER_FORCE_AA==0
+      else if (edge_end < scanline + aa)
+        rasterizer->ending_edges = 1;
 #endif
+    }
 }
 
-static void
-ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
-                          float x,
-                          float y,
-                          float width,
-                          float height)
-{
-  ctx_rasterizer_move_to (rasterizer, x, y);
-  ctx_rasterizer_rel_line_to (rasterizer, width, 0);
-  ctx_rasterizer_rel_line_to (rasterizer, 0, height);
-  ctx_rasterizer_rel_line_to (rasterizer, -width, 0);
-  ctx_rasterizer_rel_line_to (rasterizer, 0, -height);
-  ctx_rasterizer_rel_line_to (rasterizer, 0.3, 0);
-  ctx_rasterizer_finish_shape (rasterizer);
-}
-
-#if CTX_ENABLE_SHADOW_BLUR
-static float
-ctx_gaussian (float x, float mu, float sigma)
-{
-  float a = ( x- mu) / sigma;
-  return ctx_expf (-0.5 * a * a);
-}
-
-static void
-ctx_compute_gaussian_kernel (int dim, float radius, float *kernel)
+static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
 {
-  float sigma = radius / 2;
-  float sum = 0.0;
-  int i = 0;
-  //for (int row = 0; row < dim; row ++)
-    for (int col = 0; col < dim; col ++, i++)
+  for (int i = 0; i < rasterizer->active_edges; i++)
     {
-      float val = //ctx_gaussian (row, radius, sigma) *
-                            ctx_gaussian (col, radius, sigma);
-      kernel[i] = val;
-      sum += val;
+      rasterizer->edges[i].x += rasterizer->edges[i].dx * count;
+    }
+#if CTX_RASTERIZER_FORCE_AA==0
+  for (int i = 0; i < rasterizer->pending_edges; i++)
+    {
+      rasterizer->edges[CTX_MAX_EDGES-1-i].x += rasterizer->edges[CTX_MAX_EDGES-1-i].dx * count;
     }
-  i = 0;
-  //for (int row = 0; row < dim; row ++)
-    for (int col = 0; col < dim; col ++, i++)
-        kernel[i] /= sum;
-}
 #endif
-
-static void
-ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius)
-{
-  float aspect  = 1.0f;
-  float radius  = corner_radius / aspect;
-  float degrees = CTX_PI / 180.0f;
-
-  if (radius > width/2) radius = width/2;
-  if (radius > height/2) radius = height/2;
-
-  ctx_rasterizer_finish_shape (rasterizer);
-  ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * 
degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0);
-  ctx_rasterizer_finish_shape (rasterizer);
-}
-
-static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command);
-
-static int
-_ctx_is_rasterizer (Ctx *ctx)
-{
-  if (ctx->renderer && ctx->renderer->process == ctx_rasterizer_process)
-    return 1;
-  return 0;
 }
 
-#if CTX_COMPOSITING_GROUPS
-static void
-ctx_rasterizer_start_group (CtxRasterizer *rasterizer)
+/* feeds up to rasterizer->scanline,
+   keeps a pending buffer of edges - that encompass
+   the full incoming scanline,
+   feed until the start of the scanline and check for need for aa
+   in all of pending + active edges, then
+   again feed_edges until middle of scanline if doing non-AA
+   or directly render when doing AA
+*/
+inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer)
 {
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-  // allocate buffer, and set it as temporary target
-  int no;
-  if (rasterizer->group[0] == NULL) // first group
-  {
-    rasterizer->saved_buf = rasterizer->buf;
-  }
-  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
-
-  if (no >= CTX_GROUP_MAX)
-     return;
-  rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width,
-                                          rasterizer->blit_height,
-                                          rasterizer->format->composite_format);
-  rasterizer->buf = rasterizer->group[no]->data;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+  int miny;
+  CtxEntry *entries = rasterizer->edge_list.entries;
+#if CTX_RASTERIZER_FORCE_AA==0
+  for (int i = 0; i < rasterizer->pending_edges; i++)
+    {
+      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i].index].data.s16[1] <= rasterizer->scanline)
+        {
+          if (rasterizer->active_edges < CTX_MAX_EDGES-2)
+            {
+              int no = rasterizer->active_edges;
+              rasterizer->active_edges++;
+              rasterizer->edges[no] = rasterizer->edges[CTX_MAX_EDGES-1-i];
+              rasterizer->edges[CTX_MAX_EDGES-1-i] =
+                rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges + 1];
+              rasterizer->pending_edges--;
+              i--;
+            }
+        }
+    }
+#endif
+  int scanline = rasterizer->scanline;
+#if CTX_RASTERIZER_FORCE_AA==0
+  int aa = rasterizer->aa;
+#endif
+  while (rasterizer->edge_pos < rasterizer->edge_list.count &&
+         (miny=entries[rasterizer->edge_pos].data.s16[1]) <= scanline 
+#if CTX_RASTERIZER_FORCE_AA==0
+         + aa
+#endif
+         
+         )
+    {
+      if (rasterizer->active_edges < CTX_MAX_EDGES-2)
+        {
+          int dy = (entries[rasterizer->edge_pos].data.s16[3] -
+                    miny);
+          if (dy) /* skipping horizontal edges */
+            {
+              int yd = scanline - miny;
+              int no = rasterizer->active_edges;
+              rasterizer->active_edges++;
+              rasterizer->edges[no].index = rasterizer->edge_pos;
+              int index = rasterizer->edges[no].index;
+              int x0 = entries[index].data.s16[0];
+              int x1 = entries[index].data.s16[2];
+              rasterizer->edges[no].x = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER;
+              int dx_dy;
+              //  if (dy)
+              dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
+              //  else
+              //  dx_dy = 0;
+              rasterizer->edges[no].dx = dx_dy;
+              rasterizer->edges[no].x += (yd * dx_dy);
+              // XXX : even better minx and maxx can
+              //       be derived using y0 and y1 for scaling dx_dy
+              //       when ydelta to these are smaller than
+              //       ydelta to scanline
+#if 0
+              if (dx_dy < 0)
+                {
+                  rasterizer->edges[no].minx =
+                    rasterizer->edges[no].x + dx_dy/2;
+                  rasterizer->edges[no].maxx =
+                    rasterizer->edges[no].x - dx_dy/2;
+                }
+              else
+                {
+                  rasterizer->edges[no].minx =
+                    rasterizer->edges[no].x - dx_dy/2;
+                  rasterizer->edges[no].maxx =
+                    rasterizer->edges[no].x + dx_dy/2;
+                }
+#endif
+#if CTX_RASTERIZER_FORCE_AA==0
+              if (abs(dx_dy)> CTX_RASTERIZER_AA_SLOPE_LIMIT)
+                { rasterizer->needs_aa ++; }
+
+              if ((miny > scanline) )
+                {
+                  /* it is a pending edge - we add it to the end of the array
+                     and keep a different count for items stored here, like
+                     a heap and stack growing against each other
+                  */
+                  if (rasterizer->pending_edges < CTX_MAX_PENDING-1)
+                  {
+                    rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
+                    rasterizer->edges[no];
+                    rasterizer->pending_edges++;
+                    rasterizer->active_edges--;
+                  }
+                }
+#endif
+            }
+        }
+      rasterizer->edge_pos++;
+    }
 }
 
-static void
-ctx_rasterizer_end_group (CtxRasterizer *rasterizer)
+CTX_INLINE static int ctx_compare_edges2 (const void *ap, const void *bp)
 {
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-  int no = 0;
-  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
-  no--;
-
-  if (no < 0)
-    return;
+  const CtxEdge *a = (const CtxEdge *) ap;
+  const CtxEdge *b = (const CtxEdge *) bp;
+  return a->x - b->x;
+}
 
-  CtxCompositingMode comp = rasterizer->state->gstate.compositing_mode;
-  CtxBlend blend = rasterizer->state->gstate.blend_mode;
-  float global_alpha = rasterizer->state->gstate.global_alpha_f;
-  // fetch compositing, blending, global alpha
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
-  CtxEntry set_state[3]=
-  {
-    ctx_u8 (CTX_COMPOSITING_MODE, comp,  0,0,0,0,0,0,0),
-    ctx_u8 (CTX_BLEND_MODE,       blend, 0,0,0,0,0,0,0),
-    ctx_f  (CTX_GLOBAL_ALPHA,     global_alpha, 0.0)
-  };
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[0]);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[1]);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[2]);
-  if (no == 0)
-  {
-    rasterizer->buf = rasterizer->saved_buf;
-  }
-  else
-  {
-    rasterizer->buf = rasterizer->group[no-1]->data;
-  }
-  int id = ctx_texture_init (rasterizer->ctx, -1,
-                  rasterizer->blit_width,
-                  rasterizer->blit_height,
-                  rasterizer->blit_width * rasterizer->format->bpp/8,
-                  rasterizer->format->pixel_format,
-                  (uint8_t*)rasterizer->group[no]->data,
-                  NULL, NULL);
-  {
-     CtxEntry commands[2] =
-      {ctx_u32 (CTX_TEXTURE, id, 0),
-       ctx_f  (CTX_CONT, rasterizer->blit_x, rasterizer->blit_y)};
-     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
-  }
-  {
-    CtxEntry commands[2]=
+CTX_INLINE static int ctx_edge2_qsort_partition (CtxEdge *A, int low, int high)
+{
+  CtxEdge pivot = A[ (high+low) /2];
+  int i = low;
+  int j = high;
+  while (i <= j)
     {
-      ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y),
-      ctx_f (CTX_CONT,      rasterizer->blit_width, rasterizer->blit_height)
-    };
-    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
-  }
-  {
-    CtxEntry commands[1]= { ctx_void (CTX_FILL) };
-    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
-  }
-  ctx_texture_release (rasterizer->ctx, id);
-  ctx_buffer_free (rasterizer->group[no]);
-  rasterizer->group[no] = 0;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+      while (ctx_compare_edges2 (&A[i], &pivot) <0) { i ++; }
+      while (ctx_compare_edges2 (&pivot, &A[j]) <0) { j --; }
+      if (i <= j)
+        {
+          CtxEdge tmp = A[i];
+          A[i] = A[j];
+          A[j] = tmp;
+          i++;
+          j--;
+        }
+    }
+  return i;
 }
-#endif
 
-#if CTX_ENABLE_SHADOW_BLUR
-static void
-ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
+static void ctx_edge2_qsort (CtxEdge *entries, int low, int high)
 {
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
-
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
   {
-    int i = 0;
-    for (int v = 0; v < dim; v += 1, i++)
-      {
-        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
-        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command[0]);
-#if CTX_ENABLE_SHADOW_BLUR
-        rasterizer->in_shadow = 1;
-#endif
-        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
-        rasterizer->shadow_y = dy;
-        rasterizer->preserve = 1;
-        ctx_rasterizer_stroke (rasterizer);
-#if CTX_ENABLE_SHADOW_BLUR
-        rasterizer->in_shadow = 0;
-#endif
-      }
+    int p = ctx_edge2_qsort_partition (entries, low, high);
+    if (low < p -1 )
+      { ctx_edge2_qsort (entries, low, p - 1); }
+    if (low < high)
+      { ctx_edge2_qsort (entries, p, high); }
   }
-  //free (kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
 }
 
-static void
-ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str)
-{
-  float x = rasterizer->state->x;
-  float y = rasterizer->state->y;
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
-
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry move_to_command [1]=
-  {
-    ctx_f (CTX_MOVE_TO, x, y),
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 
+static void ctx_rasterizer_sort_active_edges (CtxRasterizer *rasterizer)
+{
+  CtxEdge *edges = rasterizer->edges;
+  /* we use sort networks for the very frequent cases of few active edges
+   * the built in qsort is fast, but sort networks are even faster
+   */
+  switch (rasterizer->active_edges)
   {
-      {
-        move_to_command[0].data.f[0] = x;
-        move_to_command[0].data.f[1] = y;
-        set_color_command[2].data.f[0] = rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
-        rasterizer->in_shadow=1;
-        ctx_rasterizer_text (rasterizer, str, 0);
-        rasterizer->in_shadow=0;
+    case 0:
+    case 1: break;
+#if CTX_BLOATY_FAST_PATHS
+    case 2:
+#define COMPARE(a,b) \
+      if (ctx_compare_edges2 (&edges[a], &edges[b])>0)\
+      {\
+        CtxEdge tmp = edges[a];\
+        edges[a] = edges[b];\
+        edges[b] = tmp;\
       }
+      COMPARE(0,1);
+      break;
+    case 3:
+      COMPARE(0,1); COMPARE(0,2); COMPARE(1,2);
+      break;
+    case 4:
+      COMPARE(0,1); COMPARE(2,3); COMPARE(0,2); COMPARE(1,3); COMPARE(1,2);
+      break;
+    case 5:
+      COMPARE(1,2); COMPARE(0,2); COMPARE(0,1); COMPARE(3,4); COMPARE(0,3);
+      COMPARE(1,4); COMPARE(2,4); COMPARE(1,3); COMPARE(2,3);
+      break;
+    case 6:
+      COMPARE(1,2); COMPARE(0,2); COMPARE(0,1); COMPARE(4,5);
+      COMPARE(3,5); COMPARE(3,4); COMPARE(0,3); COMPARE(1,4);
+      COMPARE(2,5); COMPARE(2,4); COMPARE(1,3); COMPARE(2,3);
+      break;
+#endif
+    default:
+      ctx_edge2_qsort (&edges[0], 0, rasterizer->active_edges-1);
+      break;
   }
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
-  move_to_command[0].data.f[0] = x;
-  move_to_command[0].data.f[1] = y;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
 }
 
-static void
-ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer)
+inline static void
+ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
+                                  int            minx,
+                                  int            maxx,
+                                  uint8_t       *coverage,
+                                  int            winding,
+                                  int            aa_factor)
 {
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
+  CtxEntry *entries = rasterizer->edge_list.entries;;
+  CtxEdge  *edges = rasterizer->edges;
+  int scanline     = rasterizer->scanline;
+  int active_edges = rasterizer->active_edges;
+  int parity = 0;
+  int fraction = 255/aa_factor;
+  coverage -= minx;
+#define CTX_EDGE(no)      entries[edges[no].index]
+#define CTX_EDGE_YMIN(no) CTX_EDGE(no).data.s16[1]
+#define CTX_EDGE_X(no)     (rasterizer->edges[no].x)
+  for (int t = 0; t < active_edges -1;)
+    {
+      int ymin = CTX_EDGE_YMIN (t);
+      int next_t = t + 1;
+      if (scanline != ymin)
+        {
+          if (winding)
+            { parity += ( (CTX_EDGE (t).code == CTX_EDGE_FLIPPED) ?1:-1); }
+          else
+            { parity = 1 - parity; }
+        }
+      if (parity)
+        {
+          int x0 = CTX_EDGE_X (t)      / CTX_SUBDIV ;
+          int x1 = CTX_EDGE_X (next_t) / CTX_SUBDIV ;
+          int first = x0 / CTX_RASTERIZER_EDGE_MULTIPLIER;
+          int last  = x1 / CTX_RASTERIZER_EDGE_MULTIPLIER;
 
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
+          int graystart = 255 - ( (x0 * 256/CTX_RASTERIZER_EDGE_MULTIPLIER) & 0xff);
+          int grayend   = (x1 * 256/CTX_RASTERIZER_EDGE_MULTIPLIER) & 0xff;
 
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+          if (first < minx)
+            { first = minx;
+              graystart=255;
+            }
+          if (last > maxx)
+            { last = maxx;
+              grayend=255;
+            }
+          if (first == last)
+          {
+            coverage[first] += (graystart-(255-grayend))/ aa_factor;
+          }
+          else if (first < last)
+          {
+                  /*
+            if (aa_factor == 1)
+            {
+              coverage[first] += graystart;
+              for (int x = first + 1; x < last; x++)
+                coverage[x] = 255;
+              coverage[last] = grayend;
+            }
+            else
+            */
+            {
+              coverage[first] += graystart/ aa_factor;
+              for (int x = first + 1; x < last; x++)
+                coverage[x] += fraction;
+              coverage[last]  += grayend/ aa_factor;
+            }
+          }
+        }
+      t = next_t;
+    }
 
+#if CTX_ENABLE_SHADOW_BLUR
+  if (rasterizer->in_shadow)
   {
-    for (int v = 0; v < dim; v ++)
-      {
-        int i = v;
-        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
-        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
-        rasterizer->in_shadow = 1;
-        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
-        rasterizer->shadow_y = dy;
-        rasterizer->preserve = 1;
-        ctx_rasterizer_fill (rasterizer);
-        rasterizer->in_shadow = 0;
-      }
+    float radius = rasterizer->state->gstate.shadow_blur;
+    int dim = 2 * radius + 1;
+    if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+      dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+    {
+      uint16_t temp[maxx-minx+1];
+      memset (temp, 0, sizeof (temp));
+      for (int x = dim/2; x < maxx-minx + 1 - dim/2; x ++)
+        for (int u = 0; u < dim; u ++)
+        {
+            temp[x] += coverage[minx+x+u-dim/2] * rasterizer->kernel[u] * 256;
+        }
+      for (int x = 0; x < maxx-minx + 1; x ++)
+        coverage[minx+x] = temp[x] >> 8;
+    }
   }
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
-}
 #endif
 
-static void
-ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, int count, float *dashes)
-{
-  if (!dashes)
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
   {
-    rasterizer->state->gstate.n_dashes = 0;
-    return;
+    /* perhaps not working right for clear? */
+    int y = scanline / rasterizer->aa;
+    uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y];
+    // XXX SIMD candidate
+    for (int x = minx; x <= maxx; x ++)
+    {
+#if CTX_1BIT_CLIP
+        coverage[x] = (coverage[x] * ((clip_line[x/8]&(1<<(x%8)))?255:0))/255;
+#else
+        coverage[x] = (coverage[x] * clip_line[x])/255;
+#endif
+    }
   }
-  count = CTX_MIN(count, CTX_PARSER_MAX_ARGS-1);
-  rasterizer->state->gstate.n_dashes = count;
-  memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float));
-  for (int i = 0; i < count; i ++)
+  if (rasterizer->aa == 1)
   {
-    if (rasterizer->state->gstate.dashes[i] < 0.0001f)
-      rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection
+    for (int x = minx; x <= maxx; x ++)
+     coverage[x] = coverage[x] > 127?255:0;
   }
+#endif
 }
 
+#undef CTX_EDGE_Y0
+#undef CTX_EDGE
+
 static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command)
+ctx_rasterizer_reset (CtxRasterizer *rasterizer)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int clear_clip = 0;
-  switch (c->code)
-    {
-#if CTX_ENABLE_SHADOW_BLUR
-      case CTX_SHADOW_COLOR:
-        {
-          CtxColor  col;
-          CtxColor *color = &col;
-          //state->gstate.source.type = CTX_SOURCE_COLOR;
-          switch ((int)c->rgba.model)
-            {
-              case CTX_RGB:
-                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
-                break;
-              case CTX_RGBA:
-                //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-              case CTX_DRGBA:
-                ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-#if CTX_ENABLE_CMYK
-              case CTX_CMYKA:
-                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_CMYK:
-                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
-                break;
-              case CTX_DCMYKA:
-                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_DCMYK:
-                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
-                break;
-#endif
-              case CTX_GRAYA:
-                ctx_color_set_graya (state, color, c->graya.g, c->graya.a);
-                break;
-              case CTX_GRAY:
-                ctx_color_set_graya (state, color, c->graya.g, 1.0f);
-                break;
-            }
-          ctx_set_color (rasterizer->ctx, CTX_shadowColor, color);
-        }
-        break;
+#if CTX_RASTERIZER_FORCE_AA==0
+  rasterizer->pending_edges   = 0;
 #endif
-      case CTX_LINE_DASH:
-        if (c->line_dash.count)
-          {
-            ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data);
-          }
-        else
-        ctx_rasterizer_line_dash (rasterizer, 0, NULL);
-        break;
+  rasterizer->active_edges    = 0;
+  rasterizer->has_shape       = 0;
+  rasterizer->has_prev        = 0;
+  rasterizer->edge_list.count = 0; // ready for new edges
+  rasterizer->edge_pos        = 0;
+  rasterizer->needs_aa        = 0;
+  rasterizer->scanline        = 0;
+  if (!rasterizer->preserve)
+  {
+    rasterizer->scan_min      = 5000;
+    rasterizer->scan_max      = -5000;
+    rasterizer->col_min       = 5000;
+    rasterizer->col_max       = -5000;
+  }
+  //rasterizer->comp_op       = NULL;
+}
 
-      case CTX_LINE_TO:
-        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_MOVE_TO:
-        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_MOVE_TO:
-        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_CURVE_TO:
-        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                 c->c.x1, c->c.y1,
-                                 c->c.x2, c->c.y2);
-        break;
-      case CTX_REL_CURVE_TO:
-        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                     c->c.x1, c->c.y1,
-                                     c->c.x2, c->c.y2);
-        break;
-      case CTX_QUAD_TO:
-        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_REL_QUAD_TO:
-        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_ARC:
-        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
-        break;
-      case CTX_RECTANGLE:
-        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                  c->rectangle.width, c->rectangle.height);
-        break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                        c->rectangle.width, c->rectangle.height,
-                                        c->rectangle.radius);
-        break;
-      case CTX_SET_PIXEL:
-        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
-                                  c->set_pixel.rgba[0],
-                                  c->set_pixel.rgba[1],
-                                  c->set_pixel.rgba[2],
-                                  c->set_pixel.rgba[3]);
-        break;
-      case CTX_TEXTURE:
-        ctx_rasterizer_set_texture (rasterizer, ctx_arg_u32 (0),
-                                    ctx_arg_float (2), ctx_arg_float (3) );
-        rasterizer->comp_op = NULL;
-        break;
-#if 0
-      case CTX_LOAD_IMAGE:
-        ctx_rasterizer_load_image (rasterizer, ctx_arg_string(),
-                                   ctx_arg_float (0), ctx_arg_float (1) );
-        break;
+static void
+ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, int winding
+#if CTX_SHAPE_CACHE
+                                ,CtxShapeEntry *shape
 #endif
-#if CTX_GRADIENTS
-      case CTX_GRADIENT_STOP:
+                               )
+{
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+  int aa = rasterizer->aa;
+
+  int scan_start = rasterizer->blit_y * aa;
+  int scan_end   = scan_start + rasterizer->blit_height * aa;
+  int blit_width = rasterizer->blit_width;
+  int blit_max_x = rasterizer->blit_x + blit_width;
+  int minx       = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x;
+  int maxx       = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - rasterizer->blit_x;
+
+#if 1
+  if (
+#if CTX_SHAPE_CACHE
+    !shape &&
+#endif
+    maxx > blit_max_x - 1)
+    { maxx = blit_max_x - 1; }
+#endif
+#if 1
+  if (rasterizer->state->gstate.clip_min_x>
+      minx)
+    { minx = rasterizer->state->gstate.clip_min_x; }
+  if (rasterizer->state->gstate.clip_max_x <
+      maxx)
+    { maxx = rasterizer->state->gstate.clip_max_x; }
+#endif
+  if (minx < 0)
+    { minx = 0; }
+  if (minx >= maxx)
+    {
+      ctx_rasterizer_reset (rasterizer);
+      return;
+    }
+#if CTX_SHAPE_CACHE
+  uint8_t _coverage[shape?2:maxx-minx+1];
+#else
+  uint8_t _coverage[maxx-minx+1];
+#endif
+  uint8_t *coverage = &_coverage[0];
+
+  ctx_compositor_setup_default (rasterizer);
+
+#if CTX_SHAPE_CACHE
+  if (shape)
+    {
+      coverage = &shape->data[0];
+    }
+#endif
+  ctx_assert (coverage);
+  rasterizer->scan_min -= (rasterizer->scan_min % aa);
+#if CTX_SHAPE_CACHE
+  if (shape)
+    {
+      scan_start = rasterizer->scan_min;
+      scan_end   = rasterizer->scan_max;
+    }
+  else
+#endif
+    {
+      if (rasterizer->scan_min > scan_start)
         {
-          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
-                         };
-          ctx_rasterizer_gradient_add_stop (rasterizer,
-                                            ctx_arg_float (0), rgba);
+          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / aa);
+          scan_start = rasterizer->scan_min;
         }
-        break;
-      case CTX_LINEAR_GRADIENT:
-        ctx_state_gradient_clear_stops (rasterizer->state);
-        rasterizer->comp_op = NULL;
-        break;
-      case CTX_RADIAL_GRADIENT:
-        ctx_state_gradient_clear_stops (rasterizer->state);
-        rasterizer->comp_op = NULL;
-        break;
+      if (rasterizer->scan_max < scan_end)
+        { scan_end = rasterizer->scan_max; }
+    }
+  if (rasterizer->state->gstate.clip_min_y * aa > scan_start )
+    { 
+       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * aa -scan_start) / aa);
+       scan_start = rasterizer->state->gstate.clip_min_y * aa; 
+    }
+  if (rasterizer->state->gstate.clip_max_y * aa < scan_end)
+    { scan_end = rasterizer->state->gstate.clip_max_y * aa; }
+  if (scan_start > scan_end ||
+      (scan_start > (rasterizer->blit_y + rasterizer->blit_height) * aa) ||
+      (scan_end < (rasterizer->blit_y) * aa))
+  { 
+    /* not affecting this rasterizers scanlines */
+    ctx_rasterizer_reset (rasterizer);
+    return;
+  }
+  ctx_rasterizer_sort_edges (rasterizer);
+  if (maxx>minx)
+  {
+#if CTX_RASTERIZER_FORCE_AA==0
+    int halfstep2 = aa/2;
+    int halfstep  = aa/2 + 1;
 #endif
-      case CTX_PRESERVE:
-        rasterizer->preserve = 1;
-        break;
-      case CTX_COLOR:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-        rasterizer->comp_op = NULL;
-        break;
-#if CTX_COMPOSITING_GROUPS
-      case CTX_START_GROUP:
-        ctx_rasterizer_start_group (rasterizer);
-        break;
-      case CTX_END_GROUP:
-        ctx_rasterizer_end_group (rasterizer);
-        break;
+    rasterizer->needs_aa = 0;
+    rasterizer->scanline = scan_start-aa*200;
+    ctx_rasterizer_feed_edges (rasterizer);
+    ctx_rasterizer_discard_edges (rasterizer);
+    ctx_rasterizer_increment_edges (rasterizer, aa * 200);
+    rasterizer->scanline = scan_start;
+    ctx_rasterizer_feed_edges (rasterizer);
+    ctx_rasterizer_discard_edges (rasterizer);
+
+  for (rasterizer->scanline = scan_start; rasterizer->scanline <= scan_end;)
+    {
+      ctx_memset (coverage, 0,
+#if CTX_SHAPE_CACHE
+                  shape?shape->width:
+#endif
+                  sizeof (_coverage) );
+#if CTX_RASTERIZER_FORCE_AA==1
+      rasterizer->needs_aa = 1;
 #endif
 
-      case CTX_RESTORE:
-        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
-             i < state->gstate.keydb_pos; i++)
+#if CTX_RASTERIZER_FORCE_AA==0
+      if (rasterizer->needs_aa
+        || rasterizer->pending_edges
+        || rasterizer->ending_edges
+        || rasterizer->force_aa
+        || aa == 1
+          )
+#endif
         {
-          if (state->keydb[i].key == CTX_clip)
+          switch (aa)
           {
-            clear_clip = 1;
+#if CTX_FORCE_INLINES
+            case 5:
+              if (winding)
+              for (int i = 0; i < 5; i++)
+              {
+                ctx_rasterizer_sort_active_edges (rasterizer);
+                ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, 1, 5);
+                rasterizer->scanline ++;
+                ctx_rasterizer_increment_edges (rasterizer, 1);
+                ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
+              }
+              else
+              for (int i = 0; i < 5; i++)
+              {
+                ctx_rasterizer_sort_active_edges (rasterizer);
+                ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, 1, 5);
+                rasterizer->scanline ++;
+                ctx_rasterizer_increment_edges (rasterizer, 1);
+                ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
+              }
+              break;
+            case 15: /* */
+              if (winding)
+              for (int i = 0; i < 15; i++)
+              {
+                ctx_rasterizer_sort_active_edges (rasterizer);
+                ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, 1, 15);
+                rasterizer->scanline ++;
+                ctx_rasterizer_increment_edges (rasterizer, 1);
+                ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
+              }
+              else
+              for (int i = 0; i < 15; i++)
+              {
+                ctx_rasterizer_sort_active_edges (rasterizer);
+                ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, 0, 15);
+                rasterizer->scanline ++;
+                ctx_rasterizer_increment_edges (rasterizer, 1);
+                ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
+              }
+              break;
+#endif
+            default:
+              for (int i = 0; i < aa; i++)
+              {
+                ctx_rasterizer_sort_active_edges (rasterizer);
+                ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, aa);
+                rasterizer->scanline ++;
+                ctx_rasterizer_increment_edges (rasterizer, 1);
+                ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
+              }
           }
         }
-        /* FALLTHROUGH */
-      case CTX_ROTATE:
-      case CTX_SCALE:
-      case CTX_TRANSLATE:
-      case CTX_SAVE:
-        rasterizer->comp_op = NULL;
-        rasterizer->uses_transforms = 1;
-        ctx_interpret_transforms (rasterizer->state, entry, NULL);
-        if (clear_clip)
-        {
-          ctx_rasterizer_clip_reset (rasterizer);
-        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
-             i < state->gstate.keydb_pos; i++)
+#if CTX_RASTERIZER_FORCE_AA==0
+      else
         {
-          if (state->keydb[i].key == CTX_clip)
-          {
-            int idx = ctx_float_to_string_index (state->keydb[i].value);
-            if (idx >=0)
-            {
-              CtxEntry *edges = (CtxEntry*)&state->stringpool[idx];
-              ctx_rasterizer_clip_apply (rasterizer, edges);
-            }
-          }
-        }
+          ctx_rasterizer_increment_edges (rasterizer, halfstep);
+          ctx_rasterizer_sort_active_edges (rasterizer);
+          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, 1);
+          ctx_rasterizer_increment_edges (rasterizer, halfstep2);
+          rasterizer->scanline += aa;
+          ctx_rasterizer_feed_edges (rasterizer);
+  ctx_rasterizer_discard_edges (rasterizer);
         }
-        break;
-      case CTX_STROKE:
-#if CTX_ENABLE_SHADOW_BLUR
-        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
-            !rasterizer->in_text)
-          ctx_rasterizer_shadow_stroke (rasterizer);
 #endif
-        if (rasterizer->state->gstate.n_dashes)
         {
-          int n_dashes = rasterizer->state->gstate.n_dashes;
-          float *dashes = rasterizer->state->gstate.dashes;
-
-          float factor = 
-              ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
-                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
-                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
-                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
-
-          int count = rasterizer->edge_list.count;
-          int aa = rasterizer->aa;
-          CtxEntry temp[count]; /* copy of already built up path's poly line  */
-          memcpy (temp, rasterizer->edge_list.entries, sizeof (temp));
-          int start = 0;
-          int end   = 0;
-      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
-      ctx_matrix_identity (&rasterizer->state->gstate.transform);
-      ctx_rasterizer_reset (rasterizer); /* for dashing we create
-                                            a dashed path to stroke */
-      float prev_x = 0.0f;
-      float prev_y = 0.0f;
-      float pos = 0.0;
-
-      int   dash_no  = 0.0;
-      float dash_lpos = 0.0;
-      int   is_down = 0;
-
-          while (start < count)
-          {
-            int started = 0;
-            int i;
-            is_down = 0;
-
-            if (!is_down)
-            {
-              CtxEntry *entry = &temp[0];
-              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-              prev_y = entry->data.s16[1] * 1.0f / aa;
-              ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
-              is_down = 1;
-            }
-
-
-            for (i = start; i < count; i++)
+#if CTX_SHAPE_CACHE
+          if (shape == NULL)
+#endif
             {
-              CtxEntry *entry = &temp[i];
-              float x, y;
-              if (entry->code == CTX_NEW_EDGE)
-                {
-                  if (started)
-                    {
-                      end = i - 1;
-                      dash_no = 0;
-                      dash_lpos = 0.0;
-                      goto foo;
-                    }
-                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  prev_y = entry->data.s16[1] * 1.0f / aa;
-                  started = 1;
-                  start = i;
-                  is_down = 1;
-                  ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
-                }
-
-again:
-
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              float dx = x - prev_x;
-              float dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-
-
-              if (dash_lpos + length >= dashes[dash_no] * factor)
-              {
-                float p = (dashes[dash_no] * factor - dash_lpos) / length;
-                float splitx = x * p + (1.0f - p) * prev_x;
-                float splity = y * p + (1.0f - p) * prev_y;
-                if (is_down)
-                {
-                  ctx_rasterizer_line_to (rasterizer, splitx, splity);
-                  is_down = 0;
-                }
-                else
-                {
-                  ctx_rasterizer_move_to (rasterizer, splitx, splity);
-                  is_down = 1;
-                }
-                prev_x = splitx;
-                prev_y = splity;
-                dash_no++;
-                dash_lpos=0;
-                if (dash_no >= n_dashes) dash_no = 0;
-                goto again;
-              }
-              else
+#if 0
+              if (aa==1)
               {
-                pos += length;
-                dash_lpos += length;
-                {
-                  if (is_down)
-                    ctx_rasterizer_line_to (rasterizer, x, y);
-                }
+                for (int x = 0; x < maxx-minx; x++)
+                  coverage
               }
-              prev_x = x;
-              prev_y = y;
+#endif
+              ctx_rasterizer_apply_coverage (rasterizer,
+                                             &dst[(minx * rasterizer->format->bpp) /8],
+                                             minx,
+                                             coverage, maxx-minx + 1);
             }
-          end = i-1;
-foo:
-          start = end+1;
         }
-      rasterizer->state->gstate.transform = transform_backup;
+#if CTX_SHAPE_CACHE
+      if (shape)
+        {
+          coverage += shape->width;
         }
-
-        ctx_rasterizer_stroke (rasterizer);
-        break;
-      case CTX_FONT:
-        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
-        break;
-      case CTX_TEXT:
-        rasterizer->in_text++;
-#if CTX_ENABLE_SHADOW_BLUR
-        if (rasterizer->state->gstate.shadow_blur > 0.0)
-          ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ());
-#endif
-        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0);
-        rasterizer->in_text--;
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_TEXT_STROKE:
-        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1);
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_GLYPH:
-        ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], entry[0].data.u8[4]);
-        break;
-      case CTX_FILL:
-#if CTX_ENABLE_SHADOW_BLUR
-        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
-            !rasterizer->in_text)
-          ctx_rasterizer_shadow_fill (rasterizer);
 #endif
-        ctx_rasterizer_fill (rasterizer);
-        break;
-      case CTX_RESET:
-      case CTX_BEGIN_PATH:
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_CLIP:
-        ctx_rasterizer_clip (rasterizer);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_rasterizer_finish_shape (rasterizer);
-        break;
+      dst += rasterizer->blit_stride;
     }
-  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
-  ctx_interpret_style (rasterizer->state, entry, NULL);
-}
-
-void
-ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
-{
-  ctx_drawlist_deinit (&rasterizer->edge_list);
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
-  {
-    ctx_buffer_free (rasterizer->clip_buffer);
-    rasterizer->clip_buffer = NULL;
   }
-#endif
-#if CTX_SHAPE_CACHE
-  for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++)
-    if (rasterizer->shape_cache.entries[i])
-    {
-      free (rasterizer->shape_cache.entries[i]);
-      rasterizer->shape_cache.entries[i] = NULL;
-    }
 
-#endif
-  free (rasterizer);
-}
-
-int ctx_renderer_is_sdl (Ctx *ctx);
-int ctx_renderer_is_fb  (Ctx *ctx);
-
-static int _ctx_is_rasterizer (Ctx *ctx);
-CtxAntialias ctx_get_antialias (Ctx *ctx)
-{
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  if (rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OUT ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_IN ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_IN ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_COPY ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_ATOP ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_CLEAR)
   {
-     CtxThreaded *fb = (CtxThreaded*)(ctx->renderer);
-     return fb->antialias;
-  }
-#endif
-  if (!_ctx_is_rasterizer (ctx)) return CTX_ANTIALIAS_DEFAULT;
+     /* fill in the rest of the blitrect when compositing mode permits it */
+     uint8_t nocoverage[rasterizer->blit_width];
+     //int gscan_start = rasterizer->state->gstate.clip_min_y * aa;
+     int gscan_start = rasterizer->state->gstate.clip_min_y * aa;
+     int gscan_end = rasterizer->state->gstate.clip_max_y * aa;
+     memset (nocoverage, 0, sizeof(nocoverage));
+     int startx   = rasterizer->state->gstate.clip_min_x;
+     int endx     = rasterizer->state->gstate.clip_max_x;
+     int clipw    = endx-startx + 1;
+     uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-  switch (((CtxRasterizer*)(ctx->renderer))->aa)
-  {
-    case 1: return CTX_ANTIALIAS_NONE;
-    case 3: return CTX_ANTIALIAS_FAST;
-    case 5: return CTX_ANTIALIAS_GOOD;
-    default:
-    case 15: return CTX_ANTIALIAS_DEFAULT;
-    case 17: return CTX_ANTIALIAS_BEST;
-  }
-}
-
-int _ctx_antialias_to_aa (CtxAntialias antialias)
-{
-  switch (antialias)
-  {
-    case CTX_ANTIALIAS_NONE: return 1;
-    case CTX_ANTIALIAS_FAST: return 3;
-    case CTX_ANTIALIAS_GOOD: return 5;
-    default:
-    case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA;
-    case CTX_ANTIALIAS_BEST: return 17;
-  }
-}
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / aa);
+     for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, clipw);
+       rasterizer->scanline += aa;
+       dst += rasterizer->blit_stride;
+     }
+     if (minx < startx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / aa);
+     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, minx-startx);
+       dst += rasterizer->blit_stride;
+     }
+     }
+     if (endx > maxx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / aa);
+     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (maxx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, endx-maxx);
 
-void
-ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
-{
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
-  {
-     CtxThreaded *fb = (CtxThreaded*)(ctx->renderer);
-     fb->antialias = antialias;
-     for (int i = 0; i < _ctx_max_threads; i++)
+       rasterizer->scanline += aa;
+       dst += rasterizer->blit_stride;
+     }
+     }
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / aa);
+     // XXX valgrind/asan this
+     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/aa < gscan_end-1;)
      {
-       ctx_set_antialias (fb->host[i], antialias);
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, clipw-1);
+
+       rasterizer->scanline += aa;
+       dst += rasterizer->blit_stride;
      }
-     return;
   }
-#endif
-  if (!_ctx_is_rasterizer (ctx)) return;
-
-  ((CtxRasterizer*)(ctx->renderer))->aa = 
-     _ctx_antialias_to_aa (antialias);
-/* vertical level of supersampling at full/forced AA.
- *
- * 1 is none, 3 is fast 5 is good 15 or 17 is best for 8bit
- *
- * valid values:  - for other values we do not add up to 255
- * 3 5 15 17 51
- *
- */
+  ctx_rasterizer_reset (rasterizer);
 }
 
-CtxRasterizer *
-ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, 
int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias)
-{
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
-    ctx_buffer_free (rasterizer->clip_buffer);
-#endif
-  if (rasterizer->edge_list.size)
-    ctx_drawlist_deinit (&rasterizer->edge_list);
-
-  ctx_memset (rasterizer, 0, sizeof (CtxRasterizer) );
-  rasterizer->vfuncs.process = ctx_rasterizer_process;
-  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
-  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
-  rasterizer->state       = state;
-  rasterizer->ctx         = ctx;
-  rasterizer->texture_source = texture_source?texture_source:ctx;
-  rasterizer->aa          = _ctx_antialias_to_aa (antialias);
-  rasterizer->force_aa    = 0;
-  ctx_state_init (rasterizer->state);
-  rasterizer->buf         = data;
-  rasterizer->blit_x      = x;
-  rasterizer->blit_y      = y;
-  rasterizer->blit_width  = width;
-  rasterizer->blit_height = height;
-  rasterizer->state->gstate.clip_min_x  = x;
-  rasterizer->state->gstate.clip_min_y  = y;
-  rasterizer->state->gstate.clip_max_x  = x + width - 1;
-  rasterizer->state->gstate.clip_max_y  = y + height - 1;
-  rasterizer->blit_stride = stride;
-  rasterizer->scan_min    = 5000;
-  rasterizer->scan_max    = -5000;
-  rasterizer->format = ctx_pixel_format_info (pixel_format);
-
-  return rasterizer;
-}
 
-Ctx *
-ctx_new_for_buffer (CtxBuffer *buffer)
+static int
+ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
+                          int          x0,
+                          int          y0,
+                          int          x1,
+                          int          y1)
 {
-  Ctx *ctx = ctx_new ();
-  ctx_set_renderer (ctx,
-                    ctx_rasterizer_init ( (CtxRasterizer *) malloc (sizeof (CtxRasterizer) ),
-                                          ctx, NULL, &ctx->state,
-                                          buffer->data, 0, 0, buffer->width, buffer->height,
-                                          buffer->stride, buffer->format->pixel_format,
-                                          CTX_ANTIALIAS_DEFAULT));
-  return ctx;
+  int aa = rasterizer->aa;
+  if (x0>x1) { // && y0>y1) { 
+     int tmp = x1;
+     x1 = x0;
+     x0 = tmp;
+     tmp = y1;
+     y1 = y0;
+     y0 = tmp;
+  }
+  if (x1 % CTX_SUBDIV ||
+      x0 % CTX_SUBDIV ||
+      y1 % aa ||
+      y0 % aa)
+    { return 0; }
+  x1 /= CTX_SUBDIV;
+  x0 /= CTX_SUBDIV;
+  y1 /= aa;
+  y0 /= aa;
+  uint8_t coverage[x1-x0 + 1];
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+  ctx_compositor_setup_default (rasterizer);
+  ctx_memset (coverage, 0xff, sizeof (coverage) );
+  if (x0 < rasterizer->blit_x)
+    { x0 = rasterizer->blit_x; }
+  if (y0 < rasterizer->blit_y)
+    { y0 = rasterizer->blit_y; }
+  if (y1 > rasterizer->blit_y + rasterizer->blit_height)
+    { y1 = rasterizer->blit_y + rasterizer->blit_height; }
+  if (x1 > rasterizer->blit_x + rasterizer->blit_width)
+    { x1 = rasterizer->blit_x + rasterizer->blit_width; }
+  dst += (y0 - rasterizer->blit_y) * rasterizer->blit_stride;
+  int width = x1 - x0;
+  if (width > 0)
+    {
+      rasterizer->scanline = y0 * aa;
+      for (int y = y0; y < y1; y++)
+        {
+          rasterizer->scanline += aa;
+          ctx_rasterizer_apply_coverage (rasterizer,
+                                         &dst[ (x0) * rasterizer->format->bpp/8],
+                                         x0,
+                                         coverage, width);
+          dst += rasterizer->blit_stride;
+        }
+    }
+  return 1;
 }
 
-Ctx *
-ctx_new_for_framebuffer (void *data, int width, int height,
-                         int stride,
-                         CtxPixelFormat pixel_format)
+inline static int
+ctx_is_transparent (CtxRasterizer *rasterizer, int stroke)
 {
-  Ctx *ctx = ctx_new ();
-  CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1),
-                                          ctx, NULL, &ctx->state, data, 0, 0, width, height,
-                                          stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
-  ctx_set_renderer (ctx, r);
-  return ctx;
+  CtxGState *gstate = &rasterizer->state->gstate;
+  if (gstate->global_alpha_u8 == 0)
+    return 1;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  {
+    uint8_t ga[2];
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
+    if (ga[1] == 0)
+      return 1;
+  }
+  return 0;
 }
 
-// ctx_new_for_stream (FILE *stream);
-
-#if 0
-CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height,
-                                   int stride, CtxPixelFormat pixel_format)
+static void
+ctx_rasterizer_fill (CtxRasterizer *rasterizer)
 {
-  CtxState    *state    = (CtxState *) malloc (sizeof (CtxState) );
-  CtxRasterizer *rasterizer = (CtxRasterizer *) malloc (sizeof (CtxRenderer) );
-  ctx_rasterizer_init (rasterizer, state, data, x, y, width, height,
-                       stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
-}
-#endif
-
-CtxPixelFormatInfo *ctx_pixel_formats = NULL;
-
-extern CtxPixelFormatInfo ctx_pixel_formats_default[];
+  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
+  int aa = rasterizer->aa;
 
+  CtxEntry temp[count]; /* copy of already built up path's poly line
+                          XXX - by building a large enough path
+                          the stack can be smashed!
+                         */
+  if (rasterizer->preserve)
+    { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); }
 
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format)
-{
-  if (!ctx_pixel_formats)
+#if CTX_ENABLE_SHADOW_BLUR
+  if (rasterizer->in_shadow)
   {
-    ctx_pixel_formats = ctx_pixel_formats_default;
-
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
+    {
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      entry->data.s16[2] += rasterizer->shadow_x * CTX_SUBDIV;
+      entry->data.s16[3] += rasterizer->shadow_y * aa;
+    }
+    rasterizer->scan_min += rasterizer->shadow_y * aa;
+    rasterizer->scan_max += rasterizer->shadow_y * aa;
+    rasterizer->col_min  += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    rasterizer->col_max  += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
   }
+#endif
 
-  for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++)
+  if (ctx_is_transparent (rasterizer, 0) ||
+      rasterizer->scan_min / aa > rasterizer->blit_y + rasterizer->blit_height ||
+      rasterizer->scan_max / aa < rasterizer->blit_y ||
+      rasterizer->col_min / CTX_SUBDIV > rasterizer->blit_x + rasterizer->blit_width ||
+      rasterizer->col_max / CTX_SUBDIV < rasterizer->blit_x)
     {
-      if (ctx_pixel_formats[i].pixel_format == format)
-        {
-          return &ctx_pixel_formats[i];
-        }
+      ctx_rasterizer_reset (rasterizer);
     }
-  return NULL;
-}
-#else
+  else
+  {
+    ctx_compositor_setup_default (rasterizer);
 
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format)
-{
-  return NULL;
-}
+    rasterizer->state->min_x =
+      ctx_mini (rasterizer->state->min_x, rasterizer->col_min / CTX_SUBDIV);
+    rasterizer->state->max_x =
+      ctx_maxi (rasterizer->state->max_x, rasterizer->col_max / CTX_SUBDIV);
+    rasterizer->state->min_y =
+      ctx_mini (rasterizer->state->min_y, rasterizer->scan_min / aa);
+    rasterizer->state->max_y =
+      ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / aa);
+    if (rasterizer->edge_list.count == 6)
+      {
+        CtxEntry *entry0 = &rasterizer->edge_list.entries[0];
+        CtxEntry *entry1 = &rasterizer->edge_list.entries[1];
+        CtxEntry *entry2 = &rasterizer->edge_list.entries[2];
+        CtxEntry *entry3 = &rasterizer->edge_list.entries[3];
+        if (!rasterizer->state->gstate.clipped &&
+             (entry0->data.s16[2] == entry1->data.s16[2]) &&
+             (entry0->data.s16[3] == entry3->data.s16[3]) &&
+             (entry1->data.s16[3] == entry2->data.s16[3]) &&
+             (entry2->data.s16[2] == entry3->data.s16[2]) &&
+             ((entry3->data.s16[2] & (CTX_SUBDIV-1)) == 0)  &&
+             ((entry3->data.s16[3] & (aa-1)) == 0) 
+#if CTX_ENABLE_SHADOW_BLUR
+             && !rasterizer->in_shadow
 #endif
+           )
+        if (ctx_rasterizer_fill_rect (rasterizer,
+                                      entry3->data.s16[2],
+                                      entry3->data.s16[3],
+                                      entry1->data.s16[2],
+                                      entry1->data.s16[3]) )
+          {
+            ctx_rasterizer_reset (rasterizer);
+            goto done;
+          }
+      }
+    ctx_rasterizer_finish_shape (rasterizer);
 
-void
-ctx_current_point (Ctx *ctx, float *x, float *y)
-{
-  if (!ctx)
-    { 
-      if (x) { *x = 0.0f; }
-      if (y) { *y = 0.0f; }
-    }
-#if CTX_RASTERIZER
-  if (ctx->renderer)
+    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
+    if (hash){};
+
+#if CTX_SHAPE_CACHE
+    int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1;
+    int height = (rasterizer->scan_max + (aa-1) ) / aa - rasterizer->scan_min / aa + 1;
+    if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1
+        && width < CTX_SHAPE_CACHE_MAX_DIM
+        && height < CTX_SHAPE_CACHE_MAX_DIM 
+#if CTX_ENABLE_SHADOW_BLUR
+        && !rasterizer->in_shadow
+#endif
+        )
+      {
+        int scan_min = rasterizer->scan_min;
+        int col_min = rasterizer->col_min;
+        scan_min -= (scan_min % aa);
+        int y0 = scan_min / aa;
+        int y1 = y0 + height;
+        int x0 = col_min / CTX_SUBDIV;
+        int ymin = y0;
+        int x1 = x0 + width;
+        int clip_x_min = rasterizer->blit_x;
+        int clip_x_max = rasterizer->blit_x + rasterizer->blit_width - 1;
+        int clip_y_min = rasterizer->blit_y;
+        int clip_y_max = rasterizer->blit_y + rasterizer->blit_height - 1;
+
+        int dont_cache = 0;
+        if (x1 >= clip_x_max)
+          { x1 = clip_x_max;
+            dont_cache = 1;
+          }
+        int xo = 0;
+        if (x0 < clip_x_min)
+          {
+            xo = clip_x_min - x0;
+            x0 = clip_x_min;
+            dont_cache = 1;
+          }
+        if (y0 < clip_y_min || y1 >= clip_y_max)
+          dont_cache = 1;
+        if (dont_cache)
+        {
+          ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
+#if CTX_SHAPE_CACHE
+                                        , NULL
+#endif
+                                       );
+        }
+        else
+        {
+
+        rasterizer->scanline = scan_min;
+        CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); 
+
+        if (shape->uses == 0)
+          {
+            CtxBuffer *buffer_backup = rasterizer->clip_buffer;
+            rasterizer->clip_buffer = NULL;
+            ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape);
+            rasterizer->clip_buffer = buffer_backup;
+          }
+        rasterizer->scanline = scan_min;
+
+        int ewidth = x1 - x0;
+        if (ewidth>0)
+        {
+          if (rasterizer->clip_buffer && !rasterizer->clip_rectangle)
+          {
+          uint8_t composite[ewidth];
+          for (int y = y0; y < y1; y++)
+            {
+              if ( (y >= clip_y_min) && (y <= clip_y_max) )
+                {
+                    for (int x = 0; x < ewidth; x++)
+                    {
+                      int val = shape->data[shape->width * (int)(y-ymin) + xo + x];
+                      // XXX : not valid for 1bit clip buffers
+                      val = (val*((uint8_t*)rasterizer->clip_buffer->data) [
+                              ((y-rasterizer->blit_y) * rasterizer->blit_width) + x0 + x])/255;
+                      composite[x] = val;
+                    }
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-rasterizer->blit_y) * 
rasterizer->blit_stride + (int) (x0) * rasterizer->format->bpp/8,
+                                                 x0, // is 0
+                                                 composite,
+                                                 ewidth );
+               rasterizer->scanline += aa;
+            }
+          }
+          }
+          else
+          for (int y = y0; y < y1; y++)
+            {
+              if ( (y >= clip_y_min) && (y <= clip_y_max) )
+                {
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-rasterizer->blit_y) * 
rasterizer->blit_stride + (int) (x0) * rasterizer->format->bpp/8,
+                                                 x0,
+                                                 &shape->data[shape->width * (int) (y-ymin) + xo],
+                                                 ewidth );
+                }
+               rasterizer->scanline += aa;
+            }
+        }
+        if (shape->uses != 0)
+          {
+            ctx_rasterizer_reset (rasterizer);
+          }
+        }
+      }
+    else
+#endif
+    ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
+#if CTX_SHAPE_CACHE
+                                    , NULL
+#endif
+                                   );
+  }
+  done:
+  if (rasterizer->preserve)
     {
-      if (x) { *x = ( (CtxRasterizer *) (ctx->renderer) )->x; }
-      if (y) { *y = ( (CtxRasterizer *) (ctx->renderer) )->y; }
-      return;
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
     }
+#if CTX_ENABLE_SHADOW_BLUR
+  if (rasterizer->in_shadow)
+  {
+    rasterizer->scan_min -= rasterizer->shadow_y * aa;
+    rasterizer->scan_max -= rasterizer->shadow_y * aa;
+    rasterizer->col_min  -= (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    rasterizer->col_max  -= (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+  }
 #endif
-  if (x) { *x = ctx->state.x; }
-  if (y) { *y = ctx->state.y; }
+  rasterizer->preserve = 0;
 }
 
-float ctx_x (Ctx *ctx)
+#if 0
+static void
+ctx_rasterizer_triangle (CtxRasterizer *rasterizer,
+                         int x0, int y0,
+                         int x1, int y1,
+                         int x2, int y2,
+                         int r0, int g0, int b0, int a0,
+                         int r1, int g1, int b1, int a1,
+                         int r2, int g2, int b2, int a2,
+                         int u0, int v0,
+                         int u1, int v1)
 {
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return x;
+
 }
+#endif
 
-float ctx_y (Ctx *ctx)
+
+typedef struct _CtxTermGlyph CtxTermGlyph;
+
+struct _CtxTermGlyph
 {
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return y;
+  uint32_t unichar;
+  int      col;
+  int      row;
+  uint8_t  rgba_bg[4];
+  uint8_t  rgba_fg[4];
+};
+
+static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke);
+static void
+ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
+{
+  float tx = rasterizer->state->x;
+  float ty = rasterizer->state->y - rasterizer->state->gstate.font_size;
+  float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size;
+  float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size;
+  _ctx_user_to_device (rasterizer->state, &tx, &ty);
+  _ctx_user_to_device (rasterizer->state, &tx2, &ty2);
+
+  if (tx2 < rasterizer->blit_x || ty2 < rasterizer->blit_y) return;
+  if (tx  > rasterizer->blit_x + rasterizer->blit_width ||
+      ty  > rasterizer->blit_y + rasterizer->blit_height)
+          return;
+
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  int ch = 1;
+  int cw = 1;
+
+  if (rasterizer->term_glyphs)
+  {
+    float tx = 0;
+    float ty = rasterizer->state->gstate.font_size;
+    float txb = 0;
+    float tyb = 0;
+
+    ch = ctx_term_get_cell_height (rasterizer->ctx);
+    cw = ctx_term_get_cell_width (rasterizer->ctx);
+
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    _ctx_user_to_device (rasterizer->state, &txb, &tyb);
+    font_size = ty-tyb;
+  }
+  if (rasterizer->term_glyphs && !stroke &&
+      fabs (font_size - ch) < 0.5)
+  {
+    float tx = rasterizer->x;
+    float ty = rasterizer->y;
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    int col = tx / cw + 1;
+    int row = ty / ch + 1;
+    CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
+    ctx_list_append (&rasterizer->glyphs, glyph);
+    glyph->unichar = unichar;
+    glyph->col = col;
+    glyph->row = row;
+    ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
+                         &glyph->rgba_fg[0]);
+  }
+  else
+#endif
+  _ctx_glyph (rasterizer->ctx, unichar, stroke);
 }
 
-void
-ctx_process (Ctx *ctx, CtxEntry *entry)
+
+static void
+_ctx_text (Ctx        *ctx,
+           const char *string,
+           int         stroke,
+           int         visible);
+static void
+ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke)
 {
-#if CTX_CURRENT_PATH
-  switch (entry->code)
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  if (rasterizer->term_glyphs)
+  {
+  float tx = 0;
+  float ty = rasterizer->state->gstate.font_size;
+  _ctx_user_to_device (rasterizer->state, &tx, &ty);
+  font_size = ty;
+  }
+  int   ch = ctx_term_get_cell_height (rasterizer->ctx);
+  int   cw = ctx_term_get_cell_width (rasterizer->ctx);
+
+  if (rasterizer->term_glyphs && !stroke &&
+      fabs (font_size - ch) < 0.5)
+  {
+    float tx = rasterizer->x;
+    float ty = rasterizer->y;
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    int col = tx / cw + 1;
+    int row = ty / ch + 1;
+    for (int i = 0; string[i]; i++, col++)
     {
-      case CTX_TEXT:
-      case CTX_TEXT_STROKE:
-      case CTX_BEGIN_PATH:
-        ctx->current_path.count = 0;
-        break;
-      case CTX_CLIP:
-      case CTX_FILL:
-      case CTX_STROKE:
-              // XXX unless preserve
-        ctx->current_path.count = 0;
-        break;
-      case CTX_CLOSE_PATH:
-      case CTX_LINE_TO:
-      case CTX_MOVE_TO:
-      case CTX_QUAD_TO:
-      case CTX_SMOOTH_TO:
-      case CTX_SMOOTHQ_TO:
-      case CTX_REL_QUAD_TO:
-      case CTX_REL_SMOOTH_TO:
-      case CTX_REL_SMOOTHQ_TO:
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_ARC:
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-      case CTX_RECTANGLE:
-      case CTX_ROUND_RECTANGLE:
-        ctx_drawlist_add_entry (&ctx->current_path, entry);
-        break;
-      default:
-        break;
-    }
-#endif
-  if (ctx->renderer && ctx->renderer->process)
-    {
-      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
+      CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
+      ctx_list_prepend (&rasterizer->glyphs, glyph);
+      glyph->unichar = string[i];
+      glyph->col = col;
+      glyph->row = row;
+      ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
+                      glyph->rgba_fg);
     }
+  }
   else
-    {
-      /* these functions might alter the code and coordinates of
-         command that in the end gets added to the drawlist
-       */
-      ctx_interpret_style (&ctx->state, entry, ctx);
-      ctx_interpret_transforms (&ctx->state, entry, ctx);
-      ctx_interpret_pos (&ctx->state, entry, ctx);
-      ctx_drawlist_add_entry (&ctx->drawlist, entry);
-#if 1
-      if (entry->code == CTX_TEXT ||
-          entry->code == CTX_LINE_DASH ||
-          entry->code == CTX_COLOR_SPACE ||
-          entry->code == CTX_TEXT_STROKE ||
-          entry->code == CTX_FONT)
-        {
-          /* the image command and its data is submitted as one unit,
-           */
-          ctx_drawlist_add_entry (&ctx->drawlist, entry+1);
-          ctx_drawlist_add_entry (&ctx->drawlist, entry+2);
-        }
 #endif
-    }
+  {
+    _ctx_text (rasterizer->ctx, string, stroke, 1);
+  }
 }
 
-int ctx_gradient_cache_valid = 0;
-
 void
-ctx_state_gradient_clear_stops (CtxState *state)
-{
-//#if CTX_GRADIENT_CACHE
-//  ctx_gradient_cache_reset ();
-//#endif
-  ctx_gradient_cache_valid = 0;
-  state->gradient.n_stops = 0;
-}
-
-uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
-uint8_t ctx_gradient_cache_u8_a[CTX_GRADIENT_CACHE_ELEMENTS][4];
-
-/****  end of engine ****/
-
-CtxBuffer *ctx_buffer_new_bare (void)
-{
-  CtxBuffer *buffer = (CtxBuffer *) ctx_calloc (sizeof (CtxBuffer), 1);
-  return buffer;
-}
-
-void ctx_buffer_set_data (CtxBuffer *buffer,
-                          void *data, int width, int height,
-                          int stride,
-                          CtxPixelFormat pixel_format,
-                          void (*freefunc) (void *pixels, void *user_data),
-                          void *user_data)
+_ctx_font (Ctx *ctx, const char *name);
+static void
+ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
 {
-  if (buffer->free_func)
-    { buffer->free_func (buffer->data, buffer->user_data); }
-  buffer->data      = data;
-  buffer->width     = width;
-  buffer->height    = height;
-  buffer->stride    = stride;
-  buffer->format    = ctx_pixel_format_info (pixel_format);
-  buffer->free_func = freefunc;
-  buffer->user_data = user_data;
+  _ctx_font (rasterizer->ctx, font_name);
 }
 
-
-
-CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
-                                    int stride,
-                                    CtxPixelFormat pixel_format,
-                                    void (*freefunc) (void *pixels, void *user_data),
-                                    void *user_data)
+static void
+ctx_rasterizer_arc (CtxRasterizer *rasterizer,
+                    float        x,
+                    float        y,
+                    float        radius,
+                    float        start_angle,
+                    float        end_angle,
+                    int          anticlockwise)
 {
-  CtxBuffer *buffer = ctx_buffer_new_bare ();
-  ctx_buffer_set_data (buffer, data, width, height, stride, pixel_format,
-                       freefunc, user_data);
-  return buffer;
-}
-
+  int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS;
+  full_segments = radius * CTX_PI * 2;
+  if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS)
+    { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; }
+  float step = CTX_PI*2.0/full_segments;
+  int steps;
 
-static void ctx_buffer_pixels_free (void *pixels, void *userdata)
-{
-  free (pixels);
-}
+  if (end_angle < -30.0)
+    end_angle = -30.0;
+  if (start_angle < -30.0)
+    start_angle = -30.0;
+  if (end_angle > 30.0)
+    end_angle = 30.0;
+  if (start_angle > 30.0)
+    start_angle = 30.0;
 
-CtxBuffer *ctx_buffer_new (int width, int height,
-                           CtxPixelFormat pixel_format)
-{
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (pixel_format);
-  CtxBuffer *buffer = ctx_buffer_new_bare ();
-  int stride = width * info->ebpp;
-  uint8_t *pixels = (uint8_t*)ctx_calloc (stride, height + 1);
+  if (radius <= 0.0001)
+          return;
 
-  ctx_buffer_set_data (buffer, pixels, width, height, stride, pixel_format,
-                       ctx_buffer_pixels_free, NULL);
-  return buffer;
+  if (end_angle == start_angle)
+          // XXX also detect arcs fully outside render view
+    {
+    if (rasterizer->has_prev!=0)
+      ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                              y + ctx_sinf (end_angle) * radius);
+      else
+      ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                            y + ctx_sinf (end_angle) * radius);
+      return;
+    }
+#if 1
+  if ( (!anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||
+       ( (anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) 
+  ||   (anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||  (!anticlockwise && 
fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f )  )
+    {
+      start_angle = start_angle;
+      steps = full_segments - 1;
+    }
+  else
+#endif
+    {
+      steps = (end_angle - start_angle) / (CTX_PI*2) * full_segments;
+      if (anticlockwise)
+        { steps = full_segments - steps; };
+   // if (steps > full_segments)
+   //   steps = full_segments;
+    }
+  if (anticlockwise) { step = step * -1; }
+  int first = 1;
+  if (steps == 0 /* || steps==full_segments -1  || (anticlockwise && steps == full_segments) */)
+    {
+      float xv = x + ctx_cosf (start_angle) * radius;
+      float yv = y + ctx_sinf (start_angle) * radius;
+      if (!rasterizer->has_prev)
+        { ctx_rasterizer_move_to (rasterizer, xv, yv); }
+      first = 0;
+    }
+  else
+    {
+      for (float angle = start_angle, i = 0; i < steps; angle += step, i++)
+        {
+          float xv = x + ctx_cosf (angle) * radius;
+          float yv = y + ctx_sinf (angle) * radius;
+          if (first && !rasterizer->has_prev)
+            { ctx_rasterizer_move_to (rasterizer, xv, yv); }
+          else
+            { ctx_rasterizer_line_to (rasterizer, xv, yv); }
+          first = 0;
+        }
+    }
+  ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                          y + ctx_sinf (end_angle) * radius);
 }
 
-void ctx_buffer_deinit (CtxBuffer *buffer)
+static void
+ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
+                        float        cx,
+                        float        cy,
+                        float        x,
+                        float        y)
 {
-  if (buffer->free_func)
-    { buffer->free_func (buffer->data, buffer->user_data); }
-  buffer->data = NULL;
-  buffer->free_func = NULL;
-  buffer->user_data  = NULL;
+  /* XXX : it is probably cheaper/faster to do quad interpolation directly -
+   *       though it will increase the code-size, an
+   *       alternative is to turn everything into cubic
+   *       and deal with cubics more directly during
+   *       rasterization
+   */
+  ctx_rasterizer_curve_to (rasterizer,
+                           (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f,
+                           (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                           x,                              y);
 }
 
-void ctx_buffer_free (CtxBuffer *buffer)
+static void
+ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
+                            float cx, float cy,
+                            float x,  float y)
 {
-  ctx_buffer_deinit (buffer);
-  free (buffer);
+  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
+                          x  + rasterizer->x, y  + rasterizer->y);
 }
 
-void ctx_texture_release (Ctx *ctx, int id)
+#define LENGTH_OVERSAMPLE 1
+static void
+ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
 {
-  if (id < 0 || id >= CTX_MAX_TEXTURES)
+  // XXX - we avoid rendering here x==0 - to keep with
+  //  an off-by one elsewhere
+  //
+  //  XXX onlt works in rgba8 formats
+  if (x <= 0 || y < 0 || x >= rasterizer->blit_width ||
+      y >= rasterizer->blit_height)
     { return; }
-  ctx_buffer_deinit (&ctx->texture[id]);
-}
-
-static int ctx_allocate_texture_id (Ctx *ctx, int id)
-{
-  if (id < 0 || id >= CTX_MAX_TEXTURES)
+  uint8_t fg_color[4];
+  ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, fg_color);
+  uint8_t pixel[4];
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+  dst += y * rasterizer->blit_stride;
+  dst += x * rasterizer->format->bpp / 8;
+  if (!rasterizer->format->to_comp ||
+      !rasterizer->format->from_comp)
+    { return; }
+  if (cov == 255)
+    {
+      for (int c = 0; c < 4; c++)
+        {
+          pixel[c] = fg_color[c];
+        }
+    }
+  else
     {
-      for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
-        if (ctx->texture[i].data == NULL)
-          { return i; }
-      int sacrifice = random()%CTX_MAX_TEXTURES; // better to bias towards old
-      ctx_texture_release (ctx, sacrifice);
-      return sacrifice;
-      return -1; // eeek
+      rasterizer->format->to_comp (rasterizer, x, dst, &pixel[0], 1);
+      for (int c = 0; c < 4; c++)
+        {
+          pixel[c] = ctx_lerp_u8 (pixel[c], fg_color[c], cov);
+        }
     }
-  return id;
+  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
 }
 
-int ctx_texture_init (Ctx *ctx,
-                      int  id,     /*  should be a string? - also the auto-ids*/
-                      int  width,
-                      int  height,
-                      int  stride,
-                      CtxPixelFormat format,
-                      uint8_t       *pixels,
-                      void (*freefunc) (void *pixels, void *user_data),
-                      void *user_data)
+static void
+ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
 {
-  /* .. how to relay? ..
-   * fully serializing is one needed option - for that there is no free
-   * func..
-   *
-   * mmap texute bank - that is one of many in compositor, prefixed with "pid-",
-   * ... we want string identifiers instead of integers.
-   *
-   * a context to use as texturing source
-   *   implemented.
-   */
-  id = ctx_allocate_texture_id (ctx, id);
-  if (id < 0)
-    { return id; }
-  int bpp = ctx_pixel_format_bpp (format);
-  ctx_buffer_deinit (&ctx->texture[id]);
-
-  if (!stride)
-  {
-    stride = width * (bpp/8);
-  }
-
-  ctx_buffer_set_data (&ctx->texture[id],
-                       pixels, width, height,
-                       stride, format,
-                       freefunc, user_data);
-  return id;
-}
-
-int
-ctx_texture_load (Ctx *ctx, int id, const char *path, int *tw, int *th)
-{
-#ifdef STBI_INCLUDE_STB_IMAGE_H
-  int w, h, components;
-  unsigned char *data = stbi_load (path, &w, &h, &components, 0);
-  if (data)
-  {
-    CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
-    switch (components)
-    {
-      case 1: pixel_format = CTX_FORMAT_GRAY8; break;
-      case 2: pixel_format = CTX_FORMAT_GRAYA8; break;
-      case 3: pixel_format = CTX_FORMAT_RGB8; break;
-      case 4: pixel_format = CTX_FORMAT_RGBA8; break;
-    }
-    if (tw) *tw = w;
-    if (th) *th = h;
-    return ctx_texture_init (ctx, id, w, h, w * components, pixel_format, data, 
-                             ctx_buffer_pixels_free, NULL);
-  }
+  int count = rasterizer->edge_list.count;
+  CtxEntry *temp = rasterizer->edge_list.entries;
+  float prev_x = 0.0f;
+  float prev_y = 0.0f;
+  int aa = rasterizer->aa;
+  int start = 0;
+  int end = 0;
+#if 0
+  float factor = ctx_matrix_get_scale (&state->gstate.transform);
 #endif
-  return -1;
-}
-
-int ctx_utf8_len (const unsigned char first_byte)
-{
-  if      ( (first_byte & 0x80) == 0)
-    { return 1; } /* ASCII */
-  else if ( (first_byte & 0xE0) == 0xC0)
-    { return 2; }
-  else if ( (first_byte & 0xF0) == 0xE0)
-    { return 3; }
-  else if ( (first_byte & 0xF8) == 0xF0)
-    { return 4; }
-  return 1;
-}
-
 
-const char *ctx_utf8_skip (const char *s, int utf8_length)
-{
-  int count;
-  if (!s)
-    { return NULL; }
-  for (count = 0; *s; s++)
+  while (start < count)
     {
-      if ( (*s & 0xC0) != 0x80)
-        { count++; }
-      if (count == utf8_length + 1)
-        { return s; }
+      int started = 0;
+      int i;
+      for (i = start; i < count; i++)
+        {
+          CtxEntry *entry = &temp[i];
+          float x, y;
+          if (entry->code == CTX_NEW_EDGE)
+            {
+              if (started)
+                {
+                  end = i - 1;
+                  goto foo;
+                }
+              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+              prev_y = entry->data.s16[1] * 1.0f / aa;
+              started = 1;
+              start = i;
+            }
+          x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+          y = entry->data.s16[3] * 1.0f / aa;
+          int dx = x - prev_x;
+          int dy = y - prev_y;
+          int length = ctx_maxf (abs (dx), abs (dy) );
+          if (length)
+            {
+              length *= LENGTH_OVERSAMPLE;
+              int len = length;
+              int tx = prev_x * 256;
+              int ty = prev_y * 256;
+              dx *= 256;
+              dy *= 256;
+              dx /= length;
+              dy /= length;
+              for (int i = 0; i < len; i++)
+                {
+                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
+                  tx += dx;
+                  ty += dy;
+                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
+                }
+            }
+          prev_x = x;
+          prev_y = y;
+        }
+      end = i-1;
+foo:
+      start = end+1;
     }
-  return s;
+  ctx_rasterizer_reset (rasterizer);
 }
 
-//  XXX  :  unused
-int ctx_utf8_strlen (const char *s)
+static void
+ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
 {
-  int count;
-  if (!s)
-    { return 0; }
-  for (count = 0; *s; s++)
-    if ( (*s & 0xC0) != 0x80)
-      { count++; }
-  return count;
-}
+  CtxSource source_backup = rasterizer->state->gstate.source_fill;
+  rasterizer->state->gstate.source_fill = rasterizer->state->gstate.source_stroke;
+  CtxState *state = rasterizer->state;
+  int count = rasterizer->edge_list.count;
+  int preserved = rasterizer->preserve;
+  float factor = ctx_matrix_get_scale (&state->gstate.transform);
 
-int
-ctx_unichar_to_utf8 (uint32_t  ch,
-                     uint8_t  *dest)
-{
-  /* http://www.cprogramming.com/tutorial/utf8.c  */
-  /*  Basic UTF-8 manipulation routines
-    by Jeff Bezanson
-    placed in the public domain Fall 2005 ... */
-  if (ch < 0x80)
-    {
-      dest[0] = (char) ch;
-      return 1;
-    }
-  if (ch < 0x800)
-    {
-      dest[0] = (ch>>6) | 0xC0;
-      dest[1] = (ch & 0x3F) | 0x80;
-      return 2;
-    }
-  if (ch < 0x10000)
+  int aa = rasterizer->aa;
+  CtxEntry temp[count]; /* copy of already built up path's poly line  */
+  memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) );
+#if 1
+  if (rasterizer->state->gstate.line_width * factor <= 0.0f &&
+      rasterizer->state->gstate.line_width * factor > -10.0f)
     {
-      dest[0] = (ch>>12) | 0xE0;
-      dest[1] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[2] = (ch & 0x3F) | 0x80;
-      return 3;
+      ctx_rasterizer_stroke_1px (rasterizer);
     }
-  if (ch < 0x110000)
+  else
+#endif
     {
-      dest[0] = (ch>>18) | 0xF0;
-      dest[1] = ( (ch>>12) & 0x3F) | 0x80;
-      dest[2] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[3] = (ch & 0x3F) | 0x80;
-      return 4;
-    }
-  return 0;
-}
-
-uint32_t
-ctx_utf8_to_unichar (const char *input)
-{
-  const uint8_t *utf8 = (const uint8_t *) input;
-  uint8_t c = utf8[0];
-  if ( (c & 0x80) == 0)
-    { return c; }
-  else if ( (c & 0xE0) == 0xC0)
-    return ( (utf8[0] & 0x1F) << 6) |
-           (utf8[1] & 0x3F);
-  else if ( (c & 0xF0) == 0xE0)
-    return ( (utf8[0] & 0xF)  << 12) |
-           ( (utf8[1] & 0x3F) << 6) |
-           (utf8[2] & 0x3F);
-  else if ( (c & 0xF8) == 0xF0)
-    return ( (utf8[0] & 0x7)  << 18) |
-           ( (utf8[1] & 0x3F) << 12) |
-           ( (utf8[2] & 0x3F) << 6) |
-           (utf8[3] & 0x3F);
-  else if ( (c & 0xFC) == 0xF8)
-    return ( (utf8[0] & 0x3)  << 24) |
-           ( (utf8[1] & 0x3F) << 18) |
-           ( (utf8[2] & 0x3F) << 12) |
-           ( (utf8[3] & 0x3F) << 6) |
-           (utf8[4] & 0x3F);
-  else if ( (c & 0xFE) == 0xFC)
-    return ( (utf8[0] & 0x1)  << 30) |
-           ( (utf8[1] & 0x3F) << 24) |
-           ( (utf8[2] & 0x3F) << 18) |
-           ( (utf8[3] & 0x3F) << 12) |
-           ( (utf8[4] & 0x3F) << 6) |
-           (utf8[5] & 0x3F);
-  return 0;
-}
+      ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape  */
+      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
+      ctx_matrix_identity (&rasterizer->state->gstate.transform);
+      float prev_x = 0.0f;
+      float prev_y = 0.0f;
+      float half_width_x = rasterizer->state->gstate.line_width * factor/2;
+      float half_width_y = rasterizer->state->gstate.line_width * factor/2;
+      if (rasterizer->state->gstate.line_width <= 0.0f)
+        {
+          half_width_x = .5;
+          half_width_y = .5;
+        }
+      int start = 0;
+      int end   = 0;
+      while (start < count)
+        {
+          int started = 0;
+          int i;
+          for (i = start; i < count; i++)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  if (started)
+                    {
+                      end = i - 1;
+                      goto foo;
+                    }
+                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  prev_y = entry->data.s16[1] * 1.0f / aa;
+                  started = 1;
+                  start = i;
+                }
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              float dx = x - prev_x;
+              float dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
+              if (length>0.001f)
+                {
+                  dx = dx/length * half_width_x;
+                  dy = dy/length * half_width_y;
+                  if (entry->code == CTX_NEW_EDGE)
+                    {
+                      ctx_rasterizer_finish_shape (rasterizer);
+                      ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx);
+                    }
+                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                  
+                  // we need to know the slope of the other side
 
-#if CTX_RASTERIZER
+                  // XXX possible miter line-to
+                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+4, prev_y+dx+10);
+                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+8, prev_y+dx+0);
 
-struct _CtxHasher
-{
-  CtxRasterizer rasterizer;
-  int           cols;
-  int           rows;
-  uint8_t      *hashes;
-};
 
-#define SHA1_IMPLEMENTATION
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis gmail com, http://libtom.org
- *
- * The plain ANSIC sha1 functionality has been extracted from libtomcrypt,
- * and is included directly in the sources. /Øyvind K. - since libtomcrypt
- * is public domain the adaptations done here to make the sha1 self contained
- * also is public domain.
- */
-#ifndef __SHA1_H
-#define __SHA1_H
-#include <inttypes.h>
+                  ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
+                }
+              prev_x = x;
+              prev_y = y;
+            }
+          end = i-1;
+foo:
+          for (int i = end; i >= start; i--)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y, dx, dy;
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              dx = x - prev_x;
+              dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
+              dx = dx/length * half_width_x;
+              dy = dy/length * half_width_y;
+              if (length>0.001f)
+                {
+                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                  // XXX possible miter line-to
+             //   ctx_rasterizer_line_to (rasterizer, prev_x-dy+10, prev_y+dx+10);
+                  ctx_rasterizer_line_to (rasterizer, x-dy,      y+dx);
+                }
+              prev_x = x;
+              prev_y = y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[1] * 1.0f / aa;
+                  dx = x - prev_x;
+                  dy = y - prev_y;
+                  length = ctx_fast_hypotf (dx, dy);
+                  if (length>0.001f)
+                    {
+                      dx = dx / length * half_width_x;
+                      dy = dy / length * half_width_y;
+                      ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                      ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
+                    }
+                }
+              if ( (prev_x != x) && (prev_y != y) )
+                {
+                  prev_x = x;
+                  prev_y = y;
+                }
+            }
+          start = end+1;
+        }
+      ctx_rasterizer_finish_shape (rasterizer);
+      switch (rasterizer->state->gstate.line_cap)
+        {
+          case CTX_CAP_SQUARE: // XXX:NYI
+          case CTX_CAP_NONE: /* nothing to do */
+            break;
+          case CTX_CAP_ROUND:
+            {
+              float x = 0, y = 0;
+              int has_prev = 0;
+              for (int i = 0; i < count; i++)
+                {
+                  CtxEntry *entry = &temp[i];
+                  if (entry->code == CTX_NEW_EDGE)
+                    {
+                      if (has_prev)
+                        {
+                          ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+                          ctx_rasterizer_finish_shape (rasterizer);
+                        }
+                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                      y = entry->data.s16[1] * 1.0f / aa;
+                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+                      ctx_rasterizer_finish_shape (rasterizer);
+                    }
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  has_prev = 1;
+                }
+              ctx_rasterizer_move_to (rasterizer, x, y);
+              ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+              ctx_rasterizer_finish_shape (rasterizer);
+              break;
+            }
+        }
+      switch (rasterizer->state->gstate.line_join)
+        {
+          case CTX_JOIN_BEVEL:
+          case CTX_JOIN_MITER:
+            break;
+          case CTX_JOIN_ROUND:
+            {
+              float x = 0, y = 0;
+              for (int i = 0; i < count-1; i++)
+                {
+                  CtxEntry *entry = &temp[i];
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  if (entry[1].code == CTX_EDGE)
+                    {
+                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1);
+                      ctx_rasterizer_finish_shape (rasterizer);
+                    }
+                }
+              break;
+            }
+        }
+      CtxFillRule rule_backup = rasterizer->state->gstate.fill_rule;
+      rasterizer->state->gstate.fill_rule = CTX_FILL_RULE_WINDING;
+      rasterizer->preserve = 0; // so fill isn't tripped
+      ctx_rasterizer_fill (rasterizer);
+      rasterizer->state->gstate.fill_rule = rule_backup;
+      //rasterizer->state->gstate.source = source_backup;
+      rasterizer->state->gstate.transform = transform_backup;
+    }
+  if (preserved)
+    {
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
+      rasterizer->preserve = 0;
+    }
+  rasterizer->state->gstate.source_fill = source_backup;
+}
 
-struct _CtxSHA1 {
-    uint64_t length;
-    uint32_t state[5], curlen;
-    unsigned char buf[64];
-};
+#if CTX_1BIT_CLIP
+#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1
+#else
+#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8
+#endif
 
-int ctx_sha1_init(CtxSHA1 * sha1);
-CtxSHA1 *ctx_sha1_new (void)
+
+static void
+ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer)
 {
-  CtxSHA1 *state = calloc (sizeof (CtxSHA1), 1);
-  ctx_sha1_init (state);
-  return state;
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+   ctx_buffer_free (rasterizer->clip_buffer);
+  rasterizer->clip_buffer = NULL;
+#endif
+  rasterizer->state->gstate.clip_min_x = rasterizer->blit_x;
+  rasterizer->state->gstate.clip_min_y = rasterizer->blit_y;
+
+  rasterizer->state->gstate.clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1;
+  rasterizer->state->gstate.clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1;
 }
-void ctx_sha1_free (CtxSHA1 *sha1)
+
+static void
+ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
+                           CtxEntry      *edges)
 {
-  free (sha1);
-}
+  int count = edges[0].data.u32[0];
+  int aa = rasterizer->aa;
 
-#if 0
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-#endif
+  int minx = 5000;
+  int miny = 5000;
+  int maxx = -5000;
+  int maxy = -5000;
+  int prev_x = 0;
+  int prev_y = 0;
+  int blit_width = rasterizer->blit_width;
+  int blit_height = rasterizer->blit_height;
 
-#ifdef FF0
-#undef FF0
-#endif
-#ifdef FF1
-#undef FF1
-#endif
 
-#ifdef SHA1_IMPLEMENTATION
-#include <stdlib.h>
-#include <string.h>
+  float coords[6][2];
 
-#define STORE64H(x,                                                             y)                           
                                          \
-   { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned                char)(((x)>>48)&255);     \
-     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned                char)(((x)>>32)&255);     \
-     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned                char)(((x)>>16)&255);     \
-     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+  for (int i = 0; i < count; i++)
+    {
+      CtxEntry *entry = &edges[i+1];
+      float x, y;
+      if (entry->code == CTX_NEW_EDGE)
+        {
+          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+          prev_y = entry->data.s16[1] * 1.0f / aa;
+          if (prev_x < minx) { minx = prev_x; }
+          if (prev_y < miny) { miny = prev_y; }
+          if (prev_x > maxx) { maxx = prev_x; }
+          if (prev_y > maxy) { maxy = prev_y; }
+        }
+      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+      y = entry->data.s16[3] * 1.0f / aa;
+      coords[i][0] = x;
+      coords[i][1] = y;
+      if (x < minx) { minx = x; }
+      if (y < miny) { miny = y; }
+      if (x > maxx) { maxx = x; }
+      if (y > maxy) { maxy = y; }
+    }
 
-#define STORE32H(x,                                                             y)                           
                                          \
-     { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned              char)(((x)>>16)&255);   \
-       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned               char)((x)&255); }
+  if (rasterizer->clip_rectangle || !rasterizer->clip_buffer)
+  {
+    if (count == 6)
+    {
+      if (coords[3][0] == coords[5][0] &&
+          coords[3][1] == coords[5][1])
+      {
+#if 0
+        printf ("%d,%d %dx%d\n", minx, miny,
+                                       maxx-minx+1, maxy-miny+1);
+#endif
 
-#define LOAD32H(x, y)                            \
-     { x = ((unsigned long)((y)[0] & 255)<<24) | \
-           ((unsigned long)((y)[1] & 255)<<16) | \
-           ((unsigned long)((y)[2] & 255)<<8)  | \
-           ((unsigned long)((y)[3] & 255)); }
+         rasterizer->state->gstate.clip_min_x =
+            ctx_maxi (minx, rasterizer->state->gstate.clip_min_x);
+         rasterizer->state->gstate.clip_min_y =
+            ctx_maxi (miny, rasterizer->state->gstate.clip_min_y);
+         rasterizer->state->gstate.clip_max_x =
+            ctx_mini (maxx, rasterizer->state->gstate.clip_max_x);
+         rasterizer->state->gstate.clip_max_y =
+            ctx_mini (maxy, rasterizer->state->gstate.clip_max_y);
 
-/* rotates the hard way */
-#define ROL(x, y)  ((((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned 
long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
-#define ROLc(x, y) ROL(x,y)
+         rasterizer->clip_rectangle = 1;
+         return;
+      }
+    }
+  }
+  rasterizer->clip_rectangle = 0;
 
-#define CRYPT_OK     0
-#define CRYPT_ERROR  1
-#define CRYPT_NOP    2
+#if CTX_ENABLE_CLIP
+  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
+  {
+    ctx_rasterizer_clip_reset (rasterizer);
+    return;//goto done;
+  }
 
-#ifndef MAX
-   #define MAX(x, y) ( ((x)>(y))?(x):(y) )
-#endif
-#ifndef MIN
-   #define MIN(x, y) ( ((x)<(y))?(x):(y) )
-#endif
+  int we_made_it = 0;
+  CtxBuffer *clip_buffer;
 
-/* a simple macro for making hash "process" functions */
-#define HASH_PROCESS(func_name, compress_name, state_var, block_size)               \
-int func_name (CtxSHA1 *sha1, const unsigned char *in, unsigned long inlen)      \
-{                                                                                   \
-    unsigned long n;                                                                \
-    int           err;                                                              \
-    assert (sha1 != NULL);                                                          \
-    assert (in != NULL);                                                            \
-    if (sha1->curlen > sizeof(sha1->buf)) {                                         \
-       return -1;                                                                   \
-    }                                                                               \
-    while (inlen > 0) {                                                             \
-        if (sha1->curlen == 0 && inlen >= block_size) {                             \
-           if ((err = compress_name (sha1, (unsigned char *)in)) != CRYPT_OK) {     \
-              return err;                                                           \
-           }                                                                        \
-           sha1->length += block_size * 8;                                          \
-           in             += block_size;                                            \
-           inlen          -= block_size;                                            \
-        } else {                                                                    \
-           n = MIN(inlen, (block_size - sha1->curlen));                             \
-           memcpy(sha1->buf + sha1->curlen, in, (size_t)n);                         \
-           sha1->curlen += n;                                                       \
-           in             += n;                                                     \
-           inlen          -= n;                                                     \
-           if (sha1->curlen == block_size) {                                        \
-              if ((err = compress_name (sha1, sha1->buf)) != CRYPT_OK) {            \
-                 return err;                                                        \
-              }                                                                     \
-              sha1->length += 8*block_size;                                         \
-              sha1->curlen = 0;                                                     \
-           }                                                                        \
-       }                                                                            \
-    }                                                                               \
-    return CRYPT_OK;                                                                \
-}
+  if (!rasterizer->clip_buffer)
+  {
+    rasterizer->clip_buffer = ctx_buffer_new (blit_width,
+                                              blit_height,
+                                              CTX_CLIP_FORMAT);
+    clip_buffer = rasterizer->clip_buffer;
+    we_made_it = 1;
+    if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
+    memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height/8);
+    else
+    memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
+  }
+  else
+  {
+    clip_buffer = ctx_buffer_new (blit_width, blit_height,
+                                  CTX_CLIP_FORMAT);
+  }
 
-/**********************/
+  {
 
-#define F0(x,y,z)  (z ^ (x & (y ^ z)))
-#define F1(x,y,z)  (x ^ y ^ z)
-#define F2(x,y,z)  ((x & y) | (z & (x | y)))
-#define F3(x,y,z)  (x ^ y ^ z)
+  int prev_x = 0;
+  int prev_y = 0;
 
-static int  ctx_sha1_compress(CtxSHA1 *sha1, unsigned char *buf)
-{
-    uint32_t a,b,c,d,e,W[80],i;
+    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height,
+       blit_width,
+       CTX_CLIP_FORMAT);
 
-    /* copy the state into 512-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD32H(W[i], buf + (4*i));
+  for (int i = 0; i < count; i++)
+    {
+      CtxEntry *entry = &edges[i+1];
+      float x, y;
+      if (entry->code == CTX_NEW_EDGE)
+        {
+          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+          prev_y = entry->data.s16[1] * 1.0f / aa;
+          ctx_move_to (ctx, prev_x, prev_y);
+        }
+      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+      y = entry->data.s16[3] * 1.0f / aa;
+      ctx_line_to (ctx, x, y);
     }
+    ctx_gray (ctx, 1.0f);
+    ctx_fill (ctx);
+    ctx_free (ctx);
+  }
 
-    /* copy state */
-    a = sha1->state[0];
-    b = sha1->state[1];
-    c = sha1->state[2];
-    d = sha1->state[3];
-    e = sha1->state[4];
+  rasterizer->clip_rectangle = 0;
 
-    /* expand it */
-    for (i = 16; i < 80; i++) {
-        W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); 
+  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
+  {
+    int count = blit_width * blit_height / 8;
+    for (int i = 0; i < count; i++)
+    {
+      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
+      (((uint8_t*)rasterizer->clip_buffer->data)[i] &
+      ((uint8_t*)clip_buffer->data)[i]);
     }
+  }
+  else
+  {
+    int count = blit_width * blit_height;
 
-    /* compress */
-    /* round one */
-    #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
-    #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
-    #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
-    #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
- 
-    for (i = 0; i < 20; ) {
-       FF0(a,b,c,d,e,i++);
-       FF0(e,a,b,c,d,i++);
-       FF0(d,e,a,b,c,i++);
-       FF0(c,d,e,a,b,i++);
-       FF0(b,c,d,e,a,i++);
-    }
+    int maybe_rect = 1;
 
-    /* round two */
-    for (; i < 40; )  { 
-       FF1(a,b,c,d,e,i++);
-       FF1(e,a,b,c,d,i++);
-       FF1(d,e,a,b,c,i++);
-       FF1(c,d,e,a,b,i++);
-       FF1(b,c,d,e,a,i++);
+    int i;
+    int x0 = 0;
+    int y0 = 0;
+    int width = -1;
+    int next_stage = 0;
+    uint8_t *p_data = (uint8_t*)rasterizer->clip_buffer->data;
+    uint8_t *data = (uint8_t*)clip_buffer->data;
+
+    i=0;
+    /* find upper left */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
+      switch (val)
+      {
+        case 255:
+          x0 = i % blit_width;
+          y0 = i / blit_width;
+          next_stage = 1;
+          break;
+        case 0: break;
+        default:
+          maybe_rect = 0;
+          break;
+      }
     }
 
-    /* round three */
-    for (; i < 60; )  { 
-       FF2(a,b,c,d,e,i++);
-       FF2(e,a,b,c,d,i++);
-       FF2(d,e,a,b,c,i++);
-       FF2(c,d,e,a,b,i++);
-       FF2(b,c,d,e,a,i++);
-    }
+    next_stage = 0;
+    /* figure out with */
+    for (; i < count && !next_stage && maybe_rect; i++)
+    {
+      int x = i % blit_width;
+      int y = i / blit_width;
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-    /* round four */
-    for (; i < 80; )  { 
-       FF3(a,b,c,d,e,i++);
-       FF3(e,a,b,c,d,i++);
-       FF3(d,e,a,b,c,i++);
-       FF3(c,d,e,a,b,i++);
-       FF3(b,c,d,e,a,i++);
+      if (y == y0)
+      {
+        switch (val)
+        {
+          case 255:
+            width = x - x0 + 1;
+            break;
+          case 0:
+            next_stage = 1;
+            break;
+          default:
+            maybe_rect = 0;
+            break;
+        }
+        if (x % blit_width == blit_width - 1) next_stage = 1;
+      }
+      else next_stage = 1;
     }
 
-    #undef FF0
-    #undef FF1
-    #undef FF2
-    #undef FF3
-
-    /* store */
-    sha1->state[0] = sha1->state[0] + a;
-    sha1->state[1] = sha1->state[1] + b;
-    sha1->state[2] = sha1->state[2] + c;
-    sha1->state[3] = sha1->state[3] + d;
-    sha1->state[4] = sha1->state[4] + e;
-
-    return CRYPT_OK;
-}
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int ctx_sha1_init(CtxSHA1 * sha1)
-{
-   assert(sha1 != NULL);
-   sha1->state[0] = 0x67452301UL;
-   sha1->state[1] = 0xefcdab89UL;
-   sha1->state[2] = 0x98badcfeUL;
-   sha1->state[3] = 0x10325476UL;
-   sha1->state[4] = 0xc3d2e1f0UL;
-   sha1->curlen = 0;
-   sha1->length = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(ctx_sha1_process, ctx_sha1_compress, sha1, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (20 bytes)
-   @return CRYPT_OK if successful
-*/
-int ctx_sha1_done(CtxSHA1 * sha1, unsigned char *out)
-{
-    int i;
-
-    assert(sha1 != NULL);
-    assert(out != NULL);
+    next_stage = 0;
+    /* body */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      int x = i % blit_width;
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-    if (sha1->curlen >= sizeof(sha1->buf)) {
-       return -1;
+      if (x < x0)
+      {
+        if (val != 0){ maybe_rect = 0; next_stage = 1; }
+      } else if (x < x0 + width)
+      {
+        if (val != 255){ if (val != 0) maybe_rect = 0; next_stage = 1; }
+      } else {
+        if (val != 0){ maybe_rect = 0; next_stage = 1; }
+      }
     }
 
-    /* increase the length of the message */
-    sha1->length += sha1->curlen * 8;
-
-    /* append the '1' bit */
-    sha1->buf[sha1->curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (sha1->curlen > 56) {
-        while (sha1->curlen < 64) {
-            sha1->buf[sha1->curlen++] = (unsigned char)0;
-        }
-        ctx_sha1_compress(sha1, sha1->buf);
-        sha1->curlen = 0;
-    }
+    next_stage = 0;
+    /* foot */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-    /* pad upto 56 bytes of zeroes */
-    while (sha1->curlen < 56) {
-        sha1->buf[sha1->curlen++] = (unsigned char)0;
+      if (val != 0){ maybe_rect = 0; next_stage = 1; }
     }
 
-    /* store length */
-    STORE64H(sha1->length, sha1->buf+56);
-    ctx_sha1_compress(sha1, sha1->buf);
 
-    /* copy output */
-    for (i = 0; i < 5; i++) {
-        STORE32H(sha1->state[i], out+(4*i));
+    for (; i < count; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
     }
-    return CRYPT_OK;
-}
-#endif
 
+    if (maybe_rect)
+       rasterizer->clip_rectangle = 1;
+  }
+  if (!we_made_it)
+   ctx_buffer_free (clip_buffer);
 #endif
+  
+  rasterizer->state->gstate.clip_min_x = ctx_maxi (minx,
+                                         rasterizer->state->gstate.clip_min_x);
+  rasterizer->state->gstate.clip_min_y = ctx_maxi (miny,
+                                         rasterizer->state->gstate.clip_min_y);
+  rasterizer->state->gstate.clip_max_x = ctx_mini (maxx,
+                                         rasterizer->state->gstate.clip_max_x);
+  rasterizer->state->gstate.clip_max_y = ctx_mini (maxy,
+                                         rasterizer->state->gstate.clip_max_y);
+}
 
-static int
-ctx_rect_intersect (const CtxRectangle *a, const CtxRectangle *b)
+static void
+ctx_rasterizer_clip (CtxRasterizer *rasterizer)
 {
-  if (a->x >= b->x + b->width ||
-      b->x >= a->x + a->width ||
-      a->y >= b->y + b->height ||
-      b->y >= a->y + a->height) return 0;
-
-  return 1;
+  int count = rasterizer->edge_list.count;
+  CtxEntry temp[count+1]; /* copy of already built up path's poly line  */
+  rasterizer->state->has_clipped=1;
+  rasterizer->state->gstate.clipped=1;
+  //if (rasterizer->preserve)
+    { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0]));
+      temp[0].code = CTX_NOP;
+      temp[0].data.u32[0] = count;
+      ctx_state_set_blob (rasterizer->state, CTX_clip, (uint8_t*)temp, sizeof(temp));
+    }
+  ctx_rasterizer_clip_apply (rasterizer, temp);
+  ctx_rasterizer_reset (rasterizer);
+  if (rasterizer->preserve)
+    {
+      memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0]));
+      rasterizer->edge_list.count = count;
+      rasterizer->preserve = 0;
+    }
 }
 
+
+#if 0
 static void
-_ctx_add_hash (CtxHasher *hasher, CtxRectangle *shape_rect, char *hash)
+ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
+                           const char  *path,
+                           float x,
+                           float y)
 {
-  CtxRectangle rect = {0,0, hasher->rasterizer.blit_width/hasher->cols,
-                            hasher->rasterizer.blit_height/hasher->rows};
-  int hno = 0;
-  for (int row = 0; row < hasher->rows; row++)
-    for (int col = 0; col < hasher->cols; col++, hno++)
-     {
-      rect.x = col * rect.width;
-      rect.y = row * rect.height;
-      if (ctx_rect_intersect (shape_rect, &rect))
-      {
-        int temp = hasher->hashes[(row * hasher->cols + col)  *20 + 0];
-        for (int i = 0; i <19;i++)
-           hasher->hashes[(row * hasher->cols + col)  *20 + i] =
-             hasher->hashes[(row * hasher->cols + col)  *20 + i+1]^
-             hash[i];
-        hasher->hashes[(row * hasher->cols + col)  *20 + 19] =
-                temp ^ hash[19];
-      }
-    }
+  // decode PNG, put it in image is slot 1,
+  // magic width height stride format data
+  ctx_buffer_load_png (&rasterizer->ctx->texture[0], path);
+  ctx_rasterizer_set_texture (rasterizer, 0, x, y);
 }
-
+#endif
 
 static void
-ctx_hasher_process (void *user_data, CtxCommand *command)
+ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
+                          uint16_t x,
+                          uint16_t y,
+                          uint8_t r,
+                          uint8_t g,
+                          uint8_t b,
+                          uint8_t a)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxHasher *hasher = (CtxHasher*) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int aa = rasterizer->aa;
-  switch (c->code)
-    {
-      case CTX_TEXT:
-        {
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
-          char ctx_sha1_hash[20];
-          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
+  rasterizer->state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+  ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, r, g, b, a);
+#if 0
+  // XXX : doesn't take transforms into account
+  ctx_rasterizer_pset (rasterizer, x, y, 255);
+#else
+  ctx_rasterizer_move_to (rasterizer, x, y);
+  ctx_rasterizer_rel_line_to (rasterizer, 1, 0);
+  ctx_rasterizer_rel_line_to (rasterizer, 0, 1);
+  ctx_rasterizer_rel_line_to (rasterizer, -1, 0);
+  ctx_rasterizer_fill (rasterizer);
+#endif
+}
 
+static void
+ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
+                          float x,
+                          float y,
+                          float width,
+                          float height)
+{
+  ctx_rasterizer_move_to (rasterizer, x, y);
+  ctx_rasterizer_rel_line_to (rasterizer, width, 0);
+  ctx_rasterizer_rel_line_to (rasterizer, 0, height);
+  ctx_rasterizer_rel_line_to (rasterizer, -width, 0);
+  ctx_rasterizer_rel_line_to (rasterizer, 0, -height);
+  ctx_rasterizer_rel_line_to (rasterizer, 0.3, 0);
+  ctx_rasterizer_finish_shape (rasterizer);
+}
 
-          float height = ctx_get_font_size (rasterizer->ctx);
-           CtxRectangle shape_rect;
-          
-           shape_rect.x=rasterizer->x;
-           shape_rect.y=rasterizer->y - height,
-           shape_rect.width = width;
-           shape_rect.height = height * 2;
-          switch ((int)ctx_state_get (rasterizer->state, CTX_text_align))
-          {
-          case CTX_TEXT_ALIGN_LEFT:
-          case CTX_TEXT_ALIGN_START:
-                  break;
-          case CTX_TEXT_ALIGN_END:
-          case CTX_TEXT_ALIGN_RIGHT:
-           shape_rect.x -= shape_rect.width;
-           break;
-          case CTX_TEXT_ALIGN_CENTER:
-           shape_rect.x -= shape_rect.width/2;
-           break;
-                   // XXX : doesn't take all text-alignments into account
-          }
+#if CTX_ENABLE_SHADOW_BLUR
+static float
+ctx_gaussian (float x, float mu, float sigma)
+{
+  float a = ( x- mu) / sigma;
+  return ctx_expf (-0.5 * a * a);
+}
 
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, 
(uint8_t*)(&color));
-          ctx_sha1_process(&sha1, (const unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+static void
+ctx_compute_gaussian_kernel (int dim, float radius, float *kernel)
+{
+  float sigma = radius / 2;
+  float sum = 0.0;
+  int i = 0;
+  //for (int row = 0; row < dim; row ++)
+    for (int col = 0; col < dim; col ++, i++)
+    {
+      float val = //ctx_gaussian (row, radius, sigma) *
+                            ctx_gaussian (col, radius, sigma);
+      kernel[i] = val;
+      sum += val;
+    }
+  i = 0;
+  //for (int row = 0; row < dim; row ++)
+    for (int col = 0; col < dim; col ++, i++)
+        kernel[i] /= sum;
+}
+#endif
 
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
-        }
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_TEXT_STROKE:
-        {
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
-          char ctx_sha1_hash[20];
-          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
-          float height = ctx_get_font_size (rasterizer->ctx);
+static void
+ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius)
+{
+  float aspect  = 1.0f;
+  float radius  = corner_radius / aspect;
+  float degrees = CTX_PI / 180.0f;
 
-           CtxRectangle shape_rect = {
-              rasterizer->x, rasterizer->y - height,
-              width, height * 2
-           };
+  if (radius > width/2) radius = width/2;
+  if (radius > height/2) radius = height/2;
 
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, 
(uint8_t*)(&color));
-          ctx_sha1_process(&sha1, (unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+  ctx_rasterizer_finish_shape (rasterizer);
+  ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * 
degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0);
+  ctx_rasterizer_finish_shape (rasterizer);
+}
 
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
-        }
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_GLYPH:
-         {
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
+static void
+ctx_rasterizer_process (void *user_data, CtxCommand *command);
 
-          char ctx_sha1_hash[20];
-          uint8_t string[8];
-          string[ctx_unichar_to_utf8 (c->u32.a0, string)]=0;
-          float width = ctx_text_width (rasterizer->ctx, (char*)string);
-          float height = ctx_get_font_size (rasterizer->ctx);
+int
+_ctx_is_rasterizer (Ctx *ctx)
+{
+  if (ctx->renderer && ctx->renderer->process == ctx_rasterizer_process)
+    return 1;
+  return 0;
+}
 
-          float tx = rasterizer->x;
-          float ty = rasterizer->y;
-          float tw = width;
-          float th = height * 2;
+#if CTX_COMPOSITING_GROUPS
+static void
+ctx_rasterizer_start_group (CtxRasterizer *rasterizer)
+{
+  CtxEntry save_command = ctx_void(CTX_SAVE);
+  // allocate buffer, and set it as temporary target
+  int no;
+  if (rasterizer->group[0] == NULL) // first group
+  {
+    rasterizer->saved_buf = rasterizer->buf;
+  }
+  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
 
-          _ctx_user_to_device (rasterizer->state, &tx, &ty);
-          _ctx_user_to_device_distance (rasterizer->state, &tw, &th);
-          CtxRectangle shape_rect = {tx,ty-th/2,tw,th};
+  if (no >= CTX_GROUP_MAX)
+     return;
+  rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width,
+                                          rasterizer->blit_height,
+                                          rasterizer->format->composite_format);
+  rasterizer->buf = rasterizer->group[no]->data;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+}
 
+static void
+ctx_rasterizer_end_group (CtxRasterizer *rasterizer)
+{
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  CtxEntry save_command = ctx_void(CTX_SAVE);
+  int no = 0;
+  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
+  no--;
 
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, 
(uint8_t*)(&color));
-          ctx_sha1_process(&sha1, string, strlen ((const char*)string));
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+  if (no < 0)
+    return;
 
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
-          ctx_rasterizer_reset (rasterizer);
-         }
-        break;
-      case CTX_FILL:
-        {
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
-          char ctx_sha1_hash[20];
+  CtxCompositingMode comp = rasterizer->state->gstate.compositing_mode;
+  CtxBlend blend = rasterizer->state->gstate.blend_mode;
+  float global_alpha = rasterizer->state->gstate.global_alpha_f;
+  // fetch compositing, blending, global alpha
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+  CtxEntry set_state[3]=
+  {
+    ctx_u8 (CTX_COMPOSITING_MODE, comp,  0,0,0,0,0,0,0),
+    ctx_u8 (CTX_BLEND_MODE,       blend, 0,0,0,0,0,0,0),
+    ctx_f  (CTX_GLOBAL_ALPHA,     global_alpha, 0.0)
+  };
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[0]);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[1]);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[2]);
+  if (no == 0)
+  {
+    rasterizer->buf = rasterizer->saved_buf;
+  }
+  else
+  {
+    rasterizer->buf = rasterizer->group[no-1]->data;
+  }
+  // XXX use texture_source ?
+   ctx_texture_init (rasterizer->ctx, ".ctx-group", // XXX ? count groups..
+                  rasterizer->blit_width,  // or have group based on thread-id?
+                  rasterizer->blit_height, // .. this would mean threadsafe
+                                           // allocation
+                  rasterizer->blit_width * rasterizer->format->bpp/8,
+                  rasterizer->format->pixel_format,
+                  NULL, // space
+                  (uint8_t*)rasterizer->group[no]->data,
+                  NULL, NULL);
+  {
+     char *eid = ".ctx-group";
+     int   eid_len = strlen (eid);
 
-          /* we eant this hasher to be as good as possible internally,
-           * since it is also used in the small shapes rasterization
-           * cache
-           */
-        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer);
-        CtxRectangle shape_rect = {
-          rasterizer->col_min / CTX_SUBDIV,
-          rasterizer->scan_min / aa,
-          (rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV,
-          (rasterizer->scan_max - rasterizer->scan_min + 1) / aa
-        };
+     CtxEntry commands[4] =
+      {
+       ctx_f  (CTX_TEXTURE, rasterizer->blit_x, rasterizer->blit_y), 
+       ctx_u32 (CTX_DATA, eid_len, eid_len/9+1),
+       ctx_u32 (CTX_CONT, 0,0),
+       ctx_u32 (CTX_CONT, 0,0)
+      };
+     memcpy( (char *) &commands[2].data.u8[0], eid, eid_len);
+     ( (char *) (&commands[2].data.u8[0]) ) [eid_len]=0;
 
-        hash ^= (rasterizer->state->gstate.fill_rule * 23);
-        hash ^= (rasterizer->state->gstate.source.type * 117);
+     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+  }
+  {
+    CtxEntry commands[2]=
+    {
+      ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y),
+      ctx_f (CTX_CONT,      rasterizer->blit_width, rasterizer->blit_height)
+    };
+    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+  }
+  {
+    CtxEntry commands[1]= { ctx_void (CTX_FILL) };
+    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+  }
+  //ctx_texture_release (rasterizer->ctx, ".ctx-group");
+  ctx_buffer_free (rasterizer->group[no]);
+  rasterizer->group[no] = 0;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+}
+#endif
 
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+#if CTX_ENABLE_SHADOW_BLUR
+static void
+ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
+{
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-        uint32_t color;
-        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, (uint8_t*)(&color));
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+  CtxEntry set_color_command [3]=
+  {
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+  {
+    int i = 0;
+    for (int v = 0; v < dim; v += 1, i++)
+      {
+        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
+        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command[0]);
+#if CTX_ENABLE_SHADOW_BLUR
+        rasterizer->in_shadow = 1;
+#endif
+        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
+        rasterizer->shadow_y = dy;
+        rasterizer->preserve = 1;
+        ctx_rasterizer_stroke (rasterizer);
+#if CTX_ENABLE_SHADOW_BLUR
+        rasterizer->in_shadow = 0;
+#endif
+      }
+  }
+  //free (kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+}
 
-        if (!rasterizer->preserve)
-          ctx_rasterizer_reset (rasterizer);
-        rasterizer->preserve = 0;
-        }
-        break;
-      case CTX_STROKE:
-        {
-          CtxSHA1 sha1;
-          ctx_sha1_init (&sha1);
-          char ctx_sha1_hash[20];
-        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer);
-        CtxRectangle shape_rect = {
-          rasterizer->col_min / CTX_SUBDIV - rasterizer->state->gstate.line_width,
-          rasterizer->scan_min / aa - rasterizer->state->gstate.line_width,
-          (rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV + 
rasterizer->state->gstate.line_width,
-          (rasterizer->scan_max - rasterizer->scan_min + 1) / aa + rasterizer->state->gstate.line_width
-        };
+static void
+ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str)
+{
+  float x = rasterizer->state->x;
+  float y = rasterizer->state->y;
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-        shape_rect.width += rasterizer->state->gstate.line_width * 2;
-        shape_rect.height += rasterizer->state->gstate.line_width * 2;
-        shape_rect.x -= rasterizer->state->gstate.line_width;
-        shape_rect.y -= rasterizer->state->gstate.line_width;
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
-        hash ^= (rasterizer->state->gstate.line_cap * 23);
-        hash ^= (rasterizer->state->gstate.source.type * 117);
+  CtxEntry set_color_command [3]=
+  {
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry move_to_command [1]=
+  {
+    ctx_f (CTX_MOVE_TO, x, y),
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+  {
+      {
+        move_to_command[0].data.f[0] = x;
+        move_to_command[0].data.f[1] = y;
+        set_color_command[2].data.f[0] = rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
+        rasterizer->in_shadow=1;
+        ctx_rasterizer_text (rasterizer, str, 0);
+        rasterizer->in_shadow=0;
+      }
+  }
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  move_to_command[0].data.f[0] = x;
+  move_to_command[0].data.f[1] = y;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
+}
 
-        uint32_t color;
-        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source.color, (uint8_t*)(&color));
+static void
+ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer)
+{
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
-        }
-        if (!rasterizer->preserve)
-          ctx_rasterizer_reset (rasterizer);
-        rasterizer->preserve = 0;
-        break;
-        /* the above cases are the painting cases and 
-         * the only ones differing from the rasterizer's process switch
-         */
+  CtxEntry set_color_command [3]=
+  {
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 
-      case CTX_LINE_TO:
-        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_MOVE_TO:
-        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_MOVE_TO:
-        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_CURVE_TO:
-        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                 c->c.x1, c->c.y1,
-                                 c->c.x2, c->c.y2);
-        break;
-      case CTX_REL_CURVE_TO:
-        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                     c->c.x1, c->c.y1,
-                                     c->c.x2, c->c.y2);
-        break;
-      case CTX_QUAD_TO:
-        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_REL_QUAD_TO:
-        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_ARC:
-        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
-        break;
-      case CTX_RECTANGLE:
-        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                  c->rectangle.width, c->rectangle.height);
-        break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                        c->rectangle.width, c->rectangle.height,
-                                        c->rectangle.radius);
-        break;
-      case CTX_SET_PIXEL:
-        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
-                                  c->set_pixel.rgba[0],
+  {
+    for (int v = 0; v < dim; v ++)
+      {
+        int i = v;
+        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
+        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
+        rasterizer->in_shadow = 1;
+        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
+        rasterizer->shadow_y = dy;
+        rasterizer->preserve = 1;
+        ctx_rasterizer_fill (rasterizer);
+        rasterizer->in_shadow = 0;
+      }
+  }
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+}
+#endif
+
+static void
+ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, int count, float *dashes)
+{
+  if (!dashes)
+  {
+    rasterizer->state->gstate.n_dashes = 0;
+    return;
+  }
+  count = CTX_MIN(count, CTX_PARSER_MAX_ARGS-1);
+  rasterizer->state->gstate.n_dashes = count;
+  memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float));
+  for (int i = 0; i < count; i ++)
+  {
+    if (rasterizer->state->gstate.dashes[i] < 0.0001f)
+      rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection
+  }
+}
+
+
+static void
+ctx_rasterizer_process (void *user_data, CtxCommand *command)
+{
+  CtxEntry *entry = &command->entry;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
+  CtxState *state = rasterizer->state;
+  CtxCommand *c = (CtxCommand *) entry;
+  int clear_clip = 0;
+  switch (c->code)
+    {
+#if CTX_ENABLE_SHADOW_BLUR
+      case CTX_SHADOW_COLOR:
+        {
+          CtxColor  col;
+          CtxColor *color = &col;
+          //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+          switch ((int)c->rgba.model)
+            {
+              case CTX_RGB:
+                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
+                break;
+              case CTX_RGBA:
+                //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+              case CTX_DRGBA:
+                ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+#if CTX_ENABLE_CMYK
+              case CTX_CMYKA:
+                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_CMYK:
+                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
+                break;
+              case CTX_DCMYKA:
+                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_DCMYK:
+                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
+                break;
+#endif
+              case CTX_GRAYA:
+                ctx_color_set_graya (state, color, c->graya.g, c->graya.a);
+                break;
+              case CTX_GRAY:
+                ctx_color_set_graya (state, color, c->graya.g, 1.0f);
+                break;
+            }
+          ctx_set_color (rasterizer->ctx, CTX_shadowColor, color);
+        }
+        break;
+#endif
+      case CTX_LINE_DASH:
+        if (c->line_dash.count)
+          {
+            ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data);
+          }
+        else
+        ctx_rasterizer_line_dash (rasterizer, 0, NULL);
+        break;
+
+      case CTX_LINE_TO:
+        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_MOVE_TO:
+        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_CURVE_TO:
+        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                 c->c.x1, c->c.y1,
+                                 c->c.x2, c->c.y2);
+        break;
+      case CTX_REL_CURVE_TO:
+        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                     c->c.x1, c->c.y1,
+                                     c->c.x2, c->c.y2);
+        break;
+      case CTX_QUAD_TO:
+        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_REL_QUAD_TO:
+        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_ARC:
+        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                  c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                        c->rectangle.width, c->rectangle.height,
+                                        c->rectangle.radius);
+        break;
+      case CTX_SET_PIXEL:
+        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
+                                  c->set_pixel.rgba[0],
                                   c->set_pixel.rgba[1],
                                   c->set_pixel.rgba[2],
                                   c->set_pixel.rgba[3]);
         break;
+      case CTX_DEFINE_TEXTURE:
+        {
+          uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
+          ctx_rasterizer_define_texture (rasterizer, c->define_texture.eid,
+                                         c->define_texture.width, c->define_texture.height,
+                                         c->define_texture.format,
+                                         pixel_data);
+        }
+        break;
       case CTX_TEXTURE:
-#if 0
-        ctx_rasterizer_set_texture (rasterizer, ctx_arg_u32 (0),
-                                    ctx_arg_float (2), ctx_arg_float (3) );
-#endif
+        ctx_rasterizer_set_texture (rasterizer, c->texture.eid,
+                                    c->texture.x, c->texture.y);
+        rasterizer->comp_op = NULL;
         break;
 #if 0
       case CTX_LOAD_IMAGE:
@@ -12151,3850 +12435,5526 @@ ctx_hasher_process (void *user_data, CtxCommand *command)
         break;
       case CTX_LINEAR_GRADIENT:
         ctx_state_gradient_clear_stops (rasterizer->state);
+        rasterizer->comp_op = NULL;
         break;
       case CTX_RADIAL_GRADIENT:
         ctx_state_gradient_clear_stops (rasterizer->state);
+        rasterizer->comp_op = NULL;
         break;
 #endif
       case CTX_PRESERVE:
         rasterizer->preserve = 1;
         break;
+      case CTX_COLOR:
+      case CTX_COMPOSITING_MODE:
+      case CTX_BLEND_MODE:
+        rasterizer->comp_op = NULL;
+        break;
+#if CTX_COMPOSITING_GROUPS
+      case CTX_START_GROUP:
+        ctx_rasterizer_start_group (rasterizer);
+        break;
+      case CTX_END_GROUP:
+        ctx_rasterizer_end_group (rasterizer);
+        break;
+#endif
+
+      case CTX_RESTORE:
+        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
+             i < state->gstate.keydb_pos; i++)
+        {
+          if (state->keydb[i].key == CTX_clip)
+          {
+            clear_clip = 1;
+          }
+        }
+        /* FALLTHROUGH */
       case CTX_ROTATE:
       case CTX_SCALE:
       case CTX_TRANSLATE:
       case CTX_SAVE:
-      case CTX_RESTORE:
+        rasterizer->comp_op = NULL;
         rasterizer->uses_transforms = 1;
         ctx_interpret_transforms (rasterizer->state, entry, NULL);
-
-        
-        break;
-      case CTX_FONT:
-        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
-        break;
-      case CTX_BEGIN_PATH:
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_CLIP:
-        // should perhaps modify a global state to include
-        // in hash?
-        ctx_rasterizer_clip (rasterizer);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_rasterizer_finish_shape (rasterizer);
+        if (clear_clip)
+        {
+          ctx_rasterizer_clip_reset (rasterizer);
+        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
+             i < state->gstate.keydb_pos; i++)
+        {
+          if (state->keydb[i].key == CTX_clip)
+          {
+            int idx = ctx_float_to_string_index (state->keydb[i].value);
+            if (idx >=0)
+            {
+              CtxEntry *edges = (CtxEntry*)&state->stringpool[idx];
+              ctx_rasterizer_clip_apply (rasterizer, edges);
+            }
+          }
+        }
+        }
         break;
-    }
-  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
-  ctx_interpret_style (rasterizer->state, entry, NULL);
-  if (command->code == CTX_LINE_WIDTH)
-    {
-      float x = state->gstate.line_width;
-      /* normalize line width according to scaling factor
-       */
-      x = x * ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
-                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
-                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
-                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
-      state->gstate.line_width = x;
-    }
-}
+      case CTX_STROKE:
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
+            !rasterizer->in_text)
+          ctx_rasterizer_shadow_stroke (rasterizer);
+#endif
+        if (rasterizer->state->gstate.n_dashes)
+        {
+          int n_dashes = rasterizer->state->gstate.n_dashes;
+          float *dashes = rasterizer->state->gstate.dashes;
+          float factor = ctx_matrix_get_scale (&state->gstate.transform);
 
+          int count = rasterizer->edge_list.count;
+          int aa = rasterizer->aa;
+          CtxEntry temp[count]; /* copy of already built up path's poly line  */
+          memcpy (temp, rasterizer->edge_list.entries, sizeof (temp));
+          int start = 0;
+          int end   = 0;
+      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
+      ctx_matrix_identity (&rasterizer->state->gstate.transform);
+      ctx_rasterizer_reset (rasterizer); /* for dashing we create
+                                            a dashed path to stroke */
+      float prev_x = 0.0f;
+      float prev_y = 0.0f;
+      float pos = 0.0;
 
-static CtxRasterizer *
-ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width, int height, int cols, int 
rows)
-{
-  CtxHasher *hasher = (CtxHasher*)rasterizer;
-  ctx_memset (rasterizer, 0, sizeof (CtxHasher) );
-  rasterizer->vfuncs.process = ctx_hasher_process;
-  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
-  // XXX need own destructor to not leak ->hashes
-  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
-  rasterizer->state       = state;
-  rasterizer->ctx         = ctx;
-  ctx_state_init (rasterizer->state);
-  rasterizer->blit_x      = 0;
-  rasterizer->blit_y      = 0;
-  rasterizer->blit_width  = width;
-  rasterizer->blit_height = height;
-  rasterizer->state->gstate.clip_min_x  = 0;
-  rasterizer->state->gstate.clip_min_y  = 0;
-  rasterizer->state->gstate.clip_max_x  = width - 1;
-  rasterizer->state->gstate.clip_max_y  = height - 1;
-  rasterizer->scan_min    = 5000;
-  rasterizer->scan_max    = -5000;
-  rasterizer->aa          = 5;
-  rasterizer->force_aa    = 0;
-
-  hasher->rows = rows;
-  hasher->cols = cols;
+      int   dash_no  = 0.0;
+      float dash_lpos = rasterizer->state->gstate.line_dash_offset * factor;
+      int   is_down = 0;
 
-  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
+          while (start < count)
+          {
+            int started = 0;
+            int i;
+            is_down = 0;
 
-  return rasterizer;
-}
+            if (!is_down)
+            {
+              CtxEntry *entry = &temp[0];
+              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+              prev_y = entry->data.s16[1] * 1.0f / aa;
+              ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
+              is_down = 1;
+            }
 
-Ctx *ctx_hasher_new (int width, int height, int cols, int rows)
-{
-  Ctx *ctx = ctx_new ();
-  CtxState    *state    = &ctx->state;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_calloc (sizeof (CtxHasher), 1);
-  ctx_hasher_init (rasterizer, ctx, state, width, height, cols, rows);
-  ctx_set_renderer (ctx, (void*)rasterizer);
-  return ctx;
-}
-uint8_t *ctx_hasher_get_hash (Ctx *ctx, int col, int row)
-{
-  CtxHasher *hasher = (CtxHasher*)ctx->renderer;
-  if (row < 0) row =0;
-  if (col < 0) col =0;
-  if (row >= hasher->rows) row = hasher->rows-1;
-  if (col >= hasher->cols) col = hasher->cols-1;
 
-  return &hasher->hashes[(row*hasher->cols+col)*20];
-}
+            for (i = start; i < count; i++)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  if (started)
+                    {
+                      end = i - 1;
+                      dash_no = 0;
+                      dash_lpos = 0.0;
+                      goto foo;
+                    }
+                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  prev_y = entry->data.s16[1] * 1.0f / aa;
+                  started = 1;
+                  start = i;
+                  is_down = 1;
+                  ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
+                }
 
-#endif
-#if CTX_EVENTS
+again:
 
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              float dx = x - prev_x;
+              float dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
 
-#include <fcntl.h>
-#include <sys/ioctl.h>
 
-int ctx_terminal_width (void)
-{
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 80;
-  return ws.ws_xpixel;
-} 
+              if (dash_lpos + length >= dashes[dash_no] * factor)
+              {
+                float p = (dashes[dash_no] * factor - dash_lpos) / length;
+                float splitx = x * p + (1.0f - p) * prev_x;
+                float splity = y * p + (1.0f - p) * prev_y;
+                if (is_down)
+                {
+                  ctx_rasterizer_line_to (rasterizer, splitx, splity);
+                  is_down = 0;
+                }
+                else
+                {
+                  ctx_rasterizer_move_to (rasterizer, splitx, splity);
+                  is_down = 1;
+                }
+                prev_x = splitx;
+                prev_y = splity;
+                dash_no++;
+                dash_lpos=0;
+                if (dash_no >= n_dashes) dash_no = 0;
+                goto again;
+              }
+              else
+              {
+                pos += length;
+                dash_lpos += length;
+                {
+                  if (is_down)
+                    ctx_rasterizer_line_to (rasterizer, x, y);
+                }
+              }
+              prev_x = x;
+              prev_y = y;
+            }
+          end = i-1;
+foo:
+          start = end+1;
+        }
+      rasterizer->state->gstate.transform = transform_backup;
+        }
 
-int ctx_terminal_height (void)
-{
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 80;
-  return ws.ws_ypixel;
-} 
+        ctx_rasterizer_stroke (rasterizer);
+        break;
+      case CTX_FONT:
+        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
+        break;
+      case CTX_TEXT:
+        rasterizer->in_text++;
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0)
+          ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ());
+#endif
+        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0);
+        rasterizer->in_text--;
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_STROKE_TEXT:
+        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1);
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_GLYPH:
+        ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], entry[0].data.u8[4]);
+        break;
+      case CTX_FILL:
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
+            !rasterizer->in_text)
+          ctx_rasterizer_shadow_fill (rasterizer);
+#endif
+        ctx_rasterizer_fill (rasterizer);
+        break;
+      case CTX_RESET:
+      case CTX_BEGIN_PATH:
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_CLIP:
+        ctx_rasterizer_clip (rasterizer);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_rasterizer_finish_shape (rasterizer);
+        break;
+    }
+  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
+  ctx_interpret_style (rasterizer->state, entry, NULL);
+}
 
-int ctx_terminal_cols (void)
+void
+ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
 {
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 80;
-  return ws.ws_col;
-} 
+  ctx_drawlist_deinit (&rasterizer->edge_list);
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+  {
+    ctx_buffer_free (rasterizer->clip_buffer);
+    rasterizer->clip_buffer = NULL;
+  }
+#endif
+#if CTX_SHAPE_CACHE
+  for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++)
+    if (rasterizer->shape_cache.entries[i])
+    {
+      free (rasterizer->shape_cache.entries[i]);
+      rasterizer->shape_cache.entries[i] = NULL;
+    }
 
-int ctx_terminal_rows (void)
-{
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 25;
-  return ws.ws_row;
+#endif
+  free (rasterizer);
 }
 
+int ctx_renderer_is_sdl (Ctx *ctx);
+int ctx_renderer_is_fb  (Ctx *ctx);
 
-
-
-
-#define DECTCEM_CURSOR_SHOW      "\033[?25h"
-#define DECTCEM_CURSOR_HIDE      "\033[?25l"
-#define TERMINAL_MOUSE_OFF       "\033[?1000l\033[?1003l"
-#define TERMINAL_MOUSE_ON_BASIC  "\033[?1000h"
-#define TERMINAL_MOUSE_ON_DRAG   "\033[?1000h\033[?1003h" /* +ON_BASIC for wider */
-#define TERMINAL_MOUSE_ON_FULL   "\033[?1000h\033[?1004h" /* compatibility */
-#define XTERM_ALTSCREEN_ON       "\033[?47h"
-#define XTERM_ALTSCREEN_OFF      "\033[?47l"
-
-/*************************** input handling *************************/
-
-#include <termios.h>
-#include <errno.h>
-#include <signal.h>
-
-#define DELAY_MS  100  
-
-#ifndef MIN
-#define MIN(a,b) (((a)<(b))?(a):(b))
+int _ctx_is_rasterizer (Ctx *ctx);
+CtxAntialias ctx_get_antialias (Ctx *ctx)
+{
+#if CTX_EVENTS
+  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  {
+     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
+     return fb->antialias;
+  }
 #endif
+  if (!_ctx_is_rasterizer (ctx)) return CTX_ANTIALIAS_DEFAULT;
 
-static int  size_changed = 0;       /* XXX: global state */
-static int  signal_installed = 0;   /* XXX: global state */
+  switch (((CtxRasterizer*)(ctx->renderer))->aa)
+  {
+    case 1: return CTX_ANTIALIAS_NONE;
+    case 3: return CTX_ANTIALIAS_FAST;
+    case 5: return CTX_ANTIALIAS_GOOD;
+    default:
+    case 15: return CTX_ANTIALIAS_DEFAULT;
+    case 17: return CTX_ANTIALIAS_BEST;
+  }
+}
 
-static const char *mouse_modes[]=
-{TERMINAL_MOUSE_OFF,
- TERMINAL_MOUSE_ON_BASIC,
- TERMINAL_MOUSE_ON_DRAG,
- TERMINAL_MOUSE_ON_FULL,
- NULL};
+int _ctx_antialias_to_aa (CtxAntialias antialias)
+{
+  switch (antialias)
+  {
+    case CTX_ANTIALIAS_NONE: return 1;
+    case CTX_ANTIALIAS_FAST: return 3;
+    case CTX_ANTIALIAS_GOOD: return 5;
+    default:
+    case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA;
+    case CTX_ANTIALIAS_BEST: return 17;
+  }
+}
 
-/* note that a nick can have multiple occurences, the labels
- * should be kept the same for all occurences of a combination. */
-typedef struct NcKeyCode {
-  const char *nick;          /* programmers name for key (combo) */
-  const char *label;         /* utf8 label for key */
-  const char  sequence[10];  /* terminal sequence */
-} NcKeyCode;
-static const NcKeyCode keycodes[]={  
+void
+ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
+{
+#if CTX_EVENTS
+  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  {
+     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
+     fb->antialias = antialias;
+     for (int i = 0; i < _ctx_max_threads; i++)
+     {
+       ctx_set_antialias (fb->host[i], antialias);
+     }
+     return;
+  }
+#endif
+  if (!_ctx_is_rasterizer (ctx)) return;
 
-  {"up",                  "↑",     "\033[A"},
-  {"down",                "↓",     "\033[B"},
-  {"right",               "→",     "\033[C"},
-  {"left",                "←",     "\033[D"},
+  ((CtxRasterizer*)(ctx->renderer))->aa = 
+     _ctx_antialias_to_aa (antialias);
+/* vertical level of supersampling at full/forced AA.
+ *
+ * 1 is none, 3 is fast 5 is good 15 or 17 is best for 8bit
+ *
+ * valid values:  - for other values we do not add up to 255
+ * 3 5 15 17 51
+ *
+ */
+}
 
-  {"shift-up",            "⇧↑",    "\033[1;2A"},
-  {"shift-down",          "⇧↓",    "\033[1;2B"},
-  {"shift-right",         "⇧→",    "\033[1;2C"},
-  {"shift-left",          "⇧←",    "\033[1;2D"},
+CtxRasterizer *
+ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, 
int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias)
+{
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+    ctx_buffer_free (rasterizer->clip_buffer);
+#endif
+  if (rasterizer->edge_list.size)
+    ctx_drawlist_deinit (&rasterizer->edge_list);
 
-  {"alt-up",              "^↑",    "\033[1;3A"},
-  {"alt-down",            "^↓",    "\033[1;3B"},
-  {"alt-right",           "^→",    "\033[1;3C"},
-  {"alt-left",            "^←",    "\033[1;3D"},
+  ctx_memset (rasterizer, 0, sizeof (CtxRasterizer) );
+  rasterizer->vfuncs.process = ctx_rasterizer_process;
+  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
+  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
+  rasterizer->state       = state;
+  rasterizer->ctx         = ctx;
+  rasterizer->texture_source = texture_source?texture_source:ctx;
+  rasterizer->aa          = _ctx_antialias_to_aa (antialias);
+  rasterizer->force_aa    = 0;
+  ctx_state_init (rasterizer->state);
+  rasterizer->buf         = data;
+  rasterizer->blit_x      = x;
+  rasterizer->blit_y      = y;
+  rasterizer->blit_width  = width;
+  rasterizer->blit_height = height;
+  rasterizer->state->gstate.clip_min_x  = x;
+  rasterizer->state->gstate.clip_min_y  = y;
+  rasterizer->state->gstate.clip_max_x  = x + width - 1;
+  rasterizer->state->gstate.clip_max_y  = y + height - 1;
+  rasterizer->blit_stride = stride;
+  rasterizer->scan_min    = 5000;
+  rasterizer->scan_max    = -5000;
+  rasterizer->format = ctx_pixel_format_info (pixel_format);
 
-  {"alt-shift-up",        "alt-s↑", "\033[1;4A"},
-  {"alt-shift-down",      "alt-s↓", "\033[1;4B"},
-  {"alt-shift-right",     "alt-s→", "\033[1;4C"},
-  {"alt-shift-left",      "alt-s←", "\033[1;4D"},
+  return rasterizer;
+}
 
-  {"control-up",          "^↑",    "\033[1;5A"},
-  {"control-down",        "^↓",    "\033[1;5B"},
-  {"control-right",       "^→",    "\033[1;5C"},
-  {"control-left",        "^←",    "\033[1;5D"},
+Ctx *
+ctx_new_for_buffer (CtxBuffer *buffer)
+{
+  Ctx *ctx = ctx_new ();
+  ctx_set_renderer (ctx,
+                    ctx_rasterizer_init ( (CtxRasterizer *) malloc (sizeof (CtxRasterizer) ),
+                                          ctx, NULL, &ctx->state,
+                                          buffer->data, 0, 0, buffer->width, buffer->height,
+                                          buffer->stride, buffer->format->pixel_format,
+                                          CTX_ANTIALIAS_DEFAULT));
+  return ctx;
+}
 
-  /* putty */
-  {"control-up",          "^↑",    "\033OA"},
-  {"control-down",        "^↓",    "\033OB"},
-  {"control-right",       "^→",    "\033OC"},
-  {"control-left",        "^←",    "\033OD"},
+Ctx *
+ctx_new_for_framebuffer (void *data, int width, int height,
+                         int stride,
+                         CtxPixelFormat pixel_format)
+{
+  Ctx *ctx = ctx_new ();
+  CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1),
+                                          ctx, NULL, &ctx->state, data, 0, 0, width, height,
+                                          stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
+  ctx_set_renderer (ctx, r);
+  return ctx;
+}
 
-  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
-  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
-  {"control-shift-right", "^⇧→",   "\033[1;6C"},
-  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
+// ctx_new_for_stream (FILE *stream);
 
-  {"control-up",          "^↑",    "\033Oa"},
-  {"control-down",        "^↓",    "\033Ob"},
-  {"control-right",       "^→",    "\033Oc"},
-  {"control-left",        "^←",    "\033Od"},
+#if 0
+CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height,
+                                   int stride, CtxPixelFormat pixel_format)
+{
+  CtxState    *state    = (CtxState *) malloc (sizeof (CtxState) );
+  CtxRasterizer *rasterizer = (CtxRasterizer *) malloc (sizeof (CtxRenderer) );
+  ctx_rasterizer_init (rasterizer, state, data, x, y, width, height,
+                       stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
+}
+#endif
 
-  {"shift-up",            "⇧↑",    "\033[a"},
-  {"shift-down",          "⇧↓",    "\033[b"},
-  {"shift-right",         "⇧→",    "\033[c"},
-  {"shift-left",          "⇧←",    "\033[d"},
+CtxPixelFormatInfo *ctx_pixel_formats = NULL;
 
-  {"insert",              "ins",   "\033[2~"},
-  {"delete",              "del",   "\033[3~"},
-  {"page-up",             "PgUp",  "\033[5~"},
-  {"page-down",           "PdDn",  "\033[6~"},
-  {"home",                "Home",  "\033OH"},
-  {"end",                 "End",   "\033OF"},
-  {"home",                "Home",  "\033[H"},
-  {"end",                 "End",   "\033[F"},
-  {"control-delete",      "^del",  "\033[3;5~"},
-  {"shift-delete",        "⇧del",  "\033[3;2~"},
-  {"control-shift-delete","^⇧del", "\033[3;6~"},
+extern CtxPixelFormatInfo ctx_pixel_formats_default[];
 
-  {"F1",        "F1",  "\033[11~"},
-  {"F2",        "F2",  "\033[12~"},
-  {"F3",        "F3",  "\033[13~"},
-  {"F4",        "F4",  "\033[14~"},
-  {"F1",        "F1",  "\033OP"},
-  {"F2",        "F2",  "\033OQ"},
-  {"F3",        "F3",  "\033OR"},
-  {"F4",        "F4",  "\033OS"},
-  {"F5",        "F5",  "\033[15~"},
-  {"F6",        "F6",  "\033[16~"},
-  {"F7",        "F7",  "\033[17~"},
-  {"F8",        "F8",  "\033[18~"},
-  {"F9",        "F9",  "\033[19~"},
-  {"F9",        "F9",  "\033[20~"},
-  {"F10",       "F10", "\033[21~"},
-  {"F11",       "F11", "\033[22~"},
-  {"F12",       "F12", "\033[23~"},
-  {"tab",       "↹",     {9, '\0'}},
-  {"shift-tab", "shift+↹",  "\033[Z"},
-  {"backspace", "⌫",  {127, '\0'}},
-  {"space",     "␣",   " "},
-  {"esc",        "␛",  "\033"},
-  {"return",    "⏎",  {10,0}},
-  {"return",    "⏎",  {13,0}},
-  /* this section could be autogenerated by code */
-  {"control-a", "^A",  {1,0}},
-  {"control-b", "^B",  {2,0}},
-  {"control-c", "^C",  {3,0}},
-  {"control-d", "^D",  {4,0}},
-  {"control-e", "^E",  {5,0}},
-  {"control-f", "^F",  {6,0}},
-  {"control-g", "^G",  {7,0}},
-  {"control-h", "^H",  {8,0}}, /* backspace? */
-  {"control-i", "^I",  {9,0}}, /* tab */
-  {"control-j", "^J",  {10,0}},
-  {"control-k", "^K",  {11,0}},
-  {"control-l", "^L",  {12,0}},
-  {"control-n", "^N",  {14,0}},
-  {"control-o", "^O",  {15,0}},
-  {"control-p", "^P",  {16,0}},
-  {"control-q", "^Q",  {17,0}},
-  {"control-r", "^R",  {18,0}},
-  {"control-s", "^S",  {19,0}},
-  {"control-t", "^T",  {20,0}},
-  {"control-u", "^U",  {21,0}},
-  {"control-v", "^V",  {22,0}},
-  {"control-w", "^W",  {23,0}},
-  {"control-x", "^X",  {24,0}},
-  {"control-y", "^Y",  {25,0}},
-  {"control-z", "^Z",  {26,0}},
-  {"alt-0",     "%0",  "\0330"},
-  {"alt-1",     "%1",  "\0331"},
-  {"alt-2",     "%2",  "\0332"},
-  {"alt-3",     "%3",  "\0333"},
-  {"alt-4",     "%4",  "\0334"},
-  {"alt-5",     "%5",  "\0335"},
-  {"alt-6",     "%6",  "\0336"},
-  {"alt-7",     "%7",  "\0337"}, /* backspace? */
-  {"alt-8",     "%8",  "\0338"},
-  {"alt-9",     "%9",  "\0339"},
-  {"alt-+",     "%+",  "\033+"},
-  {"alt--",     "%-",  "\033-"},
-  {"alt-/",     "%/",  "\033/"},
-  {"alt-a",     "%A",  "\033a"},
-  {"alt-b",     "%B",  "\033b"},
-  {"alt-c",     "%C",  "\033c"},
-  {"alt-d",     "%D",  "\033d"},
-  {"alt-e",     "%E",  "\033e"},
-  {"alt-f",     "%F",  "\033f"},
-  {"alt-g",     "%G",  "\033g"},
-  {"alt-h",     "%H",  "\033h"}, /* backspace? */
-  {"alt-i",     "%I",  "\033i"},
-  {"alt-j",     "%J",  "\033j"},
-  {"alt-k",     "%K",  "\033k"},
-  {"alt-l",     "%L",  "\033l"},
-  {"alt-n",     "%N",  "\033m"},
-  {"alt-n",     "%N",  "\033n"},
-  {"alt-o",     "%O",  "\033o"},
-  {"alt-p",     "%P",  "\033p"},
-  {"alt-q",     "%Q",  "\033q"},
-  {"alt-r",     "%R",  "\033r"},
-  {"alt-s",     "%S",  "\033s"},
-  {"alt-t",     "%T",  "\033t"},
-  {"alt-u",     "%U",  "\033u"},
-  {"alt-v",     "%V",  "\033v"},
-  {"alt-w",     "%W",  "\033w"},
-  {"alt-x",     "%X",  "\033x"},
-  {"alt-y",     "%Y",  "\033y"},
-  {"alt-z",     "%Z",  "\033z"},
-  {"shift-tab", "shift-↹", {27, 9, 0}},
-  /* Linux Console  */
-  {"home",      "Home", "\033[1~"},
-  {"end",       "End",  "\033[4~"},
-  {"F1",        "F1",   "\033[[A"},
-  {"F2",        "F2",   "\033[[B"},
-  {"F3",        "F3",   "\033[[C"},
-  {"F4",        "F4",   "\033[[D"},
-  {"F5",        "F5",   "\033[[E"},
-  {"F6",        "F6",   "\033[[F"},
-  {"F7",        "F7",   "\033[[G"},
-  {"F8",        "F8",   "\033[[H"},
-  {"F9",        "F9",   "\033[[I"},
-  {"F10",       "F10",  "\033[[J"},
-  {"F11",       "F11",  "\033[[K"},
-  {"F12",       "F12",  "\033[[L"}, 
-  {"ok",        "",     "\033[0n"},
-  {NULL, }
-};
 
-static struct termios orig_attr;    /* in order to restore at exit */
-static int    nc_is_raw = 0;
-static int    atexit_registered = 0;
-static int    mouse_mode = NC_MOUSE_NONE;
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
+{
+  if (!ctx_pixel_formats)
+  {
+    ctx_pixel_formats = ctx_pixel_formats_default;
 
-static void _nc_noraw (void)
+  }
+
+  for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++)
+    {
+      if (ctx_pixel_formats[i].pixel_format == format)
+        {
+          return &ctx_pixel_formats[i];
+        }
+    }
+  return NULL;
+}
+#else
+
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
 {
-  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
-    nc_is_raw = 0;
+  return NULL;
 }
+#endif
 
 void
-nc_at_exit (void)
+ctx_current_point (Ctx *ctx, float *x, float *y)
 {
-  printf (TERMINAL_MOUSE_OFF);
-  printf (XTERM_ALTSCREEN_OFF);
-  _nc_noraw();
-  fprintf (stdout, "\e[?25h");
-  //if (ctx_native_events)
-  fprintf (stdout, "\e[?201l");
-  fprintf (stdout, "\e[?1049l");
+  if (!ctx)
+    { 
+      if (x) { *x = 0.0f; }
+      if (y) { *y = 0.0f; }
+    }
+#if CTX_RASTERIZER
+  if (ctx->renderer)
+    {
+      if (x) { *x = ( (CtxRasterizer *) (ctx->renderer) )->x; }
+      if (y) { *y = ( (CtxRasterizer *) (ctx->renderer) )->y; }
+      return;
+    }
+#endif
+  if (x) { *x = ctx->state.x; }
+  if (y) { *y = ctx->state.y; }
 }
 
-static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
+float ctx_x (Ctx *ctx)
 {
-  static int prev_state = 0;
-  const char *ret = "mouse-motion";
-  float relx, rely;
-  signed char buf[3];
-  read (n->mouse_fd, buf, 3);
-  relx = buf[1];
-  rely = -buf[2];
-
-  n->mouse_x += relx * 0.1;
-  n->mouse_y += rely * 0.1;
-
-  if (n->mouse_x < 1) n->mouse_x = 1;
-  if (n->mouse_y < 1) n->mouse_y = 1;
-  if (n->mouse_x >= n->events.width)  n->mouse_x = n->events.width;
-  if (n->mouse_y >= n->events.height) n->mouse_y = n->events.height;
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return x;
+}
 
-  if (x) *x = n->mouse_x;
-  if (y) *y = n->mouse_y;
+float ctx_y (Ctx *ctx)
+{
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return y;
+}
 
-  if ((prev_state & 1) != (buf[0] & 1))
+void
+ctx_process (Ctx *ctx, CtxEntry *entry)
+{
+#if CTX_CURRENT_PATH
+  switch (entry->code)
     {
-      if (buf[0] & 1) ret = "mouse-press";
+      case CTX_TEXT:
+      case CTX_STROKE_TEXT:
+      case CTX_BEGIN_PATH:
+        ctx->current_path.count = 0;
+        break;
+      case CTX_CLIP:
+      case CTX_FILL:
+      case CTX_STROKE:
+              // XXX unless preserve
+        ctx->current_path.count = 0;
+        break;
+      case CTX_CLOSE_PATH:
+      case CTX_LINE_TO:
+      case CTX_MOVE_TO:
+      case CTX_QUAD_TO:
+      case CTX_SMOOTH_TO:
+      case CTX_SMOOTHQ_TO:
+      case CTX_REL_QUAD_TO:
+      case CTX_REL_SMOOTH_TO:
+      case CTX_REL_SMOOTHQ_TO:
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_ARC:
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+      case CTX_RECTANGLE:
+      case CTX_ROUND_RECTANGLE:
+        ctx_drawlist_add_entry (&ctx->current_path, entry);
+        break;
+      default:
+        break;
     }
-  else if (buf[0] & 1)
-    ret = "mouse-drag";
-
-  if ((prev_state & 2) != (buf[0] & 2))
+#endif
+  if (ctx->renderer && ctx->renderer->process)
     {
-      if (buf[0] & 2) ret = "mouse2-press";
+      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
     }
-  else if (buf[0] & 2)
-    ret = "mouse2-drag";
-
-  if ((prev_state & 4) != (buf[0] & 4))
+  else
     {
-      if (buf[0] & 4) ret = "mouse1-press";
+      /* these functions might alter the code and coordinates of
+         command that in the end gets added to the drawlist
+       */
+      ctx_interpret_style (&ctx->state, entry, ctx);
+      ctx_interpret_transforms (&ctx->state, entry, ctx);
+      ctx_interpret_pos (&ctx->state, entry, ctx);
+      ctx_drawlist_add_entry (&ctx->drawlist, entry);
     }
-  else if (buf[0] & 4)
-    ret = "mouse1-drag";
+}
 
-  prev_state = buf[0];
-  return ret;
+
+int ctx_gradient_cache_valid = 0;
+
+void
+ctx_state_gradient_clear_stops (CtxState *state)
+{
+//#if CTX_GRADIENT_CACHE
+//  ctx_gradient_cache_reset ();
+//#endif
+  ctx_gradient_cache_valid = 0;
+  state->gradient.n_stops = 0;
 }
 
-static const char *mev_type = NULL;
-static int         mev_x = 0;
-static int         mev_y = 0;
-static int         mev_q = 0;
+uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
+uint8_t ctx_gradient_cache_u8_a[CTX_GRADIENT_CACHE_ELEMENTS][4];
 
-static const char *mouse_get_event (Ctx  *n, int *x, int *y)
+/****  end of engine ****/
+
+CtxBuffer *ctx_buffer_new_bare (void)
 {
-  if (!mev_q)
-    return NULL;
-  *x = mev_x;
-  *y = mev_y;
-  mev_q = 0;
-  return mev_type;
+  CtxBuffer *buffer = (CtxBuffer *) ctx_calloc (sizeof (CtxBuffer), 1);
+  return buffer;
 }
 
-static int mouse_has_event (Ctx *n)
+void ctx_buffer_set_data (CtxBuffer *buffer,
+                          void *data, int width, int height,
+                          int stride,
+                          CtxPixelFormat pixel_format,
+                          void (*freefunc) (void *pixels, void *user_data),
+                          void *user_data)
 {
-  struct timeval tv;
-  int retval;
+  if (buffer->free_func)
+    { buffer->free_func (buffer->data, buffer->user_data); }
+  if (stride <= 0)
+    stride = ctx_pixel_format_get_stride (pixel_format, width);
+  buffer->data      = data;
+  buffer->width     = width;
+  buffer->height    = height;
+  buffer->stride    = stride;
+  buffer->format    = ctx_pixel_format_info (pixel_format);
+  buffer->free_func = freefunc;
+  buffer->user_data = user_data;
+}
 
-  if (mouse_mode == NC_MOUSE_NONE)
-    return 0;
+CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
+                                    int stride,
+                                    CtxPixelFormat pixel_format,
+                                    void (*freefunc) (void *pixels, void *user_data),
+                                    void *user_data)
+{
+  CtxBuffer *buffer = ctx_buffer_new_bare ();
+  ctx_buffer_set_data (buffer, data, width, height, stride, pixel_format,
+                       freefunc, user_data);
+  return buffer;
+}
 
-  if (mev_q)
-    return 1;
+void ctx_buffer_pixels_free (void *pixels, void *userdata)
+{
+  free (pixels);
+}
 
-  if (n->mouse_fd == 0)
-    return 0;
-  return 0;
+CtxBuffer *ctx_buffer_new (int width, int height,
+                           CtxPixelFormat pixel_format)
+{
+  //CtxPixelFormatInfo *info = ctx_pixel_format_info (pixel_format);
+  CtxBuffer *buffer = ctx_buffer_new_bare ();
+  int stride = ctx_pixel_format_get_stride (pixel_format, width);
+  uint8_t *pixels = (uint8_t*)ctx_calloc (stride, height + 1);
 
+  ctx_buffer_set_data (buffer, pixels, width, height, stride, pixel_format,
+                       ctx_buffer_pixels_free, NULL);
+  return buffer;
+}
+
+void ctx_buffer_deinit (CtxBuffer *buffer)
+{
+  if (buffer->free_func)
+    buffer->free_func (buffer->data, buffer->user_data);
+  if (buffer->eid)
   {
-    fd_set rfds;
-    FD_ZERO (&rfds);
-    FD_SET(n->mouse_fd, &rfds);
-    tv.tv_sec = 0; tv.tv_usec = 0;
-    retval = select (n->mouse_fd+1, &rfds, NULL, NULL, &tv);
+    free (buffer->eid);
   }
-
-  if (retval != 0)
-    {
-      int nx = 0, ny = 0;
-      const char *type = mouse_get_event_int (n, &nx, &ny);
-
-      if ((mouse_mode < NC_MOUSE_DRAG && mev_type && !strcmp (mev_type, "drag")) ||
-          (mouse_mode < NC_MOUSE_ALL && mev_type && !strcmp (mev_type, "motion")))
-        {
-          mev_q = 0;
-          return mouse_has_event (n);
-        }
-
-      if ((mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse-motion")) ||
-         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse1-drag")) ||
-         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse2-drag")))
-        {
-          if (nx == mev_x && ny == mev_y)
-          {
-            mev_q = 0;
-            return mouse_has_event (n);
-          }
-        }
-      mev_x = nx;
-      mev_y = ny;
-      mev_type = type;
-      mev_q = 1;
+  buffer->eid = NULL;
+  buffer->data = NULL;
+  buffer->free_func = NULL;
+  buffer->user_data  = NULL;
+  if (buffer->color_managed)
+  {
+    if (buffer->color_managed != buffer)
+    {
+      ctx_buffer_free (buffer->color_managed);
     }
-  return retval != 0;
+    buffer->color_managed = NULL;
+  }
 }
 
+void ctx_buffer_free (CtxBuffer *buffer)
+{
+  ctx_buffer_deinit (buffer);
+  free (buffer);
+}
 
-static int _nc_raw (void)
+static int
+ctx_texture_check_eid (Ctx *ctx, const char *eid, int *tw, int *th)
 {
-  struct termios raw;
-  if (!isatty (STDIN_FILENO))
-    return -1;
-  if (!atexit_registered)
+  for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+  {
+    if (ctx->texture[i].data &&
+        ctx->texture[i].eid  &&
+        !strcmp (ctx->texture[i].eid, eid))
     {
-      atexit (nc_at_exit);
-      atexit_registered = 1;
+      if (tw) *tw = ctx->texture[i].width;
+      if (th) *th = ctx->texture[i].height;
+      ctx->texture[i].frame = ctx->texture_cache->frame;
+      return i;
     }
-  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
-    return -1;
-  raw = orig_attr;  /* modify the original mode */
-  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-  raw.c_oflag &= ~(OPOST);
-  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return -1;
-  nc_is_raw = 1;
-  tcdrain(STDIN_FILENO);
-  tcflush(STDIN_FILENO, 1);
-  return 0;
+  }
+  return -1;
 }
 
-static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
+const char* ctx_texture_init (Ctx           *ctx,
+                              const char    *eid,
+                              int            width,
+                              int            height,
+                              int            stride,
+                              CtxPixelFormat format,
+                              void          *space,
+                              uint8_t       *pixels,
+                              void (*freefunc) (void *pixels, void *user_data),
+                              void *user_data)
 {
-  int i;
-  int matches = 0;
-
-  if (!strncmp (buf, "\033[M", MIN(length,3)))
+  int id = 0;
+  if (eid)
+  {
+    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
     {
-      if (length >= 6)
-        return 9001;
-      return 2342;
-    }
-  for (i = 0; keycodes[i].nick; i++)
-    if (!strncmp (buf, keycodes[i].sequence, length))
+      if (ctx->texture[i].data &&
+          ctx->texture[i].eid &&
+          !strcmp (ctx->texture[i].eid, eid))
       {
-        matches ++;
-        if ((int)strlen (keycodes[i].sequence) == length && ret)
-          {
-            *ret = &keycodes[i];
-            return 1;
-          }
+        ctx->texture[i].frame = ctx->texture_cache->frame;
+        if (freefunc && user_data != (void*)23)
+          freefunc (pixels, user_data);
+        return ctx->texture[i].eid;
       }
-  if (matches != 1 && ret)
-    *ret = NULL;
-  return matches==1?2:matches;
+      if (ctx->texture[i].data == NULL 
+          ||   (ctx->texture_cache->frame - ctx->texture[i].frame >= 2))
+        id = i;
+    }
+  } else
+  {
+    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+    {
+      if (ctx->texture[i].data == NULL 
+          || (ctx->texture_cache->frame - ctx->texture[i].frame > 2))
+        id = i;
+    }
+  }
+  //int bpp = ctx_pixel_format_bits_per_pixel (format);
+  ctx_buffer_deinit (&ctx->texture[id]);
+
+  if (stride<=0)
+  {
+    stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
+  }
+
+  if (freefunc == ctx_buffer_pixels_free && user_data == (void*)23)
+  {
+     uint8_t *tmp = (uint8_t*)malloc (height * stride);
+     memcpy (tmp, pixels, height * stride);
+     pixels = tmp;
+  }
+
+  ctx_buffer_set_data (&ctx->texture[id],
+                       pixels, width, height,
+                       stride, format,
+                       freefunc, user_data);
+#if CTX_ENABLE_CM
+  ctx->texture[id].space = space;
+#endif
+  ctx->texture[id].frame = ctx->texture_cache->frame;
+  if (eid)
+  {
+    /* we got an eid, this is the fast path */
+    ctx->texture[id].eid = strdup (eid);
+  }
+  else
+  {
+    uint8_t hash[20];
+    char ascii[41];
+
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    ctx_sha1_process (sha1, pixels, stride * height);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    ctx->texture[id].eid = strdup (ascii);
+  }
+  return ctx->texture[id].eid;
 }
 
-static void nc_resize_term (int  dummy)
+void
+_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
+                                      CtxBuffer     *buffer)
 {
-  size_changed = 1;
+   switch (buffer->format->pixel_format)
+   {
+#ifndef NO_BABL
+#if CTX_BABL
+     case CTX_FORMAT_RGBA8:
+        {
+          buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
+                                                  CTX_FORMAT_RGBA8);
+          babl_process (
+             babl_fish (babl_format_with_space ("R'G'B'A u8", buffer->space),
+                        babl_format_with_space ("R'G'B'A u8", rasterizer->state->gstate.device_space)),
+             buffer->data, buffer->color_managed->data,
+             buffer->width * buffer->height
+             );
+       }
+       break;
+     case CTX_FORMAT_RGB8:
+       {
+       buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
+                                               CTX_FORMAT_RGB8);
+          babl_process (
+             babl_fish (babl_format_with_space ("R'G'B' u8", buffer->space),
+                        babl_format_with_space ("R'G'B' u8", rasterizer->state->gstate.device_space)),
+             buffer->data, buffer->color_managed->data,
+             buffer->width * buffer->height
+             );
+       }
+       break;
+#endif
+#endif
+     default:
+       buffer->color_managed = buffer;
+   }
 }
 
-int ctx_has_event (Ctx  *n, int delay_ms)
-{
-  struct timeval tv;
-  int retval;
-  fd_set rfds;
 
-  if (size_changed)
-    return 1;
-  FD_ZERO (&rfds);
-  FD_SET (STDIN_FILENO, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = delay_ms * 1000; 
-  retval = select (1, &rfds, NULL, NULL, &tv);
-  if (size_changed)
-    return 1;
-  return retval == 1 && retval != -1;
-}
 
-const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
+int ctx_utf8_len (const unsigned char first_byte)
 {
-  unsigned char buf[20];
-  int length;
-
+  if      ( (first_byte & 0x80) == 0)
+    { return 1; } /* ASCII */
+  else if ( (first_byte & 0xE0) == 0xC0)
+    { return 2; }
+  else if ( (first_byte & 0xF0) == 0xE0)
+    { return 3; }
+  else if ( (first_byte & 0xF8) == 0xF0)
+    { return 4; }
+  return 1;
+}
 
-  if (x) *x = -1;
-  if (y) *y = -1;
 
-  if (!signal_installed)
+const char *ctx_utf8_skip (const char *s, int utf8_length)
+{
+  int count;
+  if (!s)
+    { return NULL; }
+  for (count = 0; *s; s++)
     {
-      _nc_raw ();
-      signal_installed = 1;
-      signal (SIGWINCH, nc_resize_term);
+      if ( (*s & 0xC0) != 0x80)
+        { count++; }
+      if (count == utf8_length + 1)
+        { return s; }
     }
-  if (mouse_mode) // XXX too often to do it all the time!
-    printf(mouse_modes[mouse_mode]);
-
-  {
-    int elapsed = 0;
-    int got_event = 0;
-
-    do {
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      got_event = mouse_has_event (n);
-      if (!got_event)
-        got_event = ctx_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      /* only do this if the client has asked for idle events,
-       * and perhaps programmed the ms timer?
-       */
-      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
-      if (!got_event && timeoutms && elapsed >= timeoutms)
-        return "idle";
-    } while (!got_event);
-  }
+  return s;
+}
 
-  if (mouse_has_event (n))
-    return mouse_get_event (n, x, y);
+//  XXX  :  unused
+int ctx_utf8_strlen (const char *s)
+{
+  int count;
+  if (!s)
+    { return 0; }
+  for (count = 0; *s; s++)
+    if ( (*s & 0xC0) != 0x80)
+      { count++; }
+  return count;
+}
 
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const NcKeyCode *match = NULL;
+int
+ctx_unichar_to_utf8 (uint32_t  ch,
+                     uint8_t  *dest)
+{
+  /* http://www.cprogramming.com/tutorial/utf8.c  */
+  /*  Basic UTF-8 manipulation routines
+    by Jeff Bezanson
+    placed in the public domain Fall 2005 ... */
+  if (ch < 0x80)
+    {
+      dest[0] = (char) ch;
+      return 1;
+    }
+  if (ch < 0x800)
+    {
+      dest[0] = (ch>>6) | 0xC0;
+      dest[1] = (ch & 0x3F) | 0x80;
+      return 2;
+    }
+  if (ch < 0x10000)
+    {
+      dest[0] = (ch>>12) | 0xE0;
+      dest[1] = ( (ch>>6) & 0x3F) | 0x80;
+      dest[2] = (ch & 0x3F) | 0x80;
+      return 3;
+    }
+  if (ch < 0x110000)
+    {
+      dest[0] = (ch>>18) | 0xF0;
+      dest[1] = ( (ch>>12) & 0x3F) | 0x80;
+      dest[2] = ( (ch>>6) & 0x3F) | 0x80;
+      dest[3] = (ch & 0x3F) | 0x80;
+      return 4;
+    }
+  return 0;
+}
 
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
-          {
-            struct timeval tv;
-            fd_set rfds;
-            FD_ZERO (&rfds);
-            FD_SET (STDIN_FILENO, &rfds);
-            tv.tv_sec = 0;
-            tv.tv_usec = 1000 * DELAY_MS;
-            if (select (1, &rfds, NULL, NULL, &tv) == 0)
-              return "esc";
-          }
-
-        switch (match_keycode ((const char*)buf, length + 1, &match))
-          {
-            case 1: /* unique match */
-              if (!match)
-                return NULL;
-              if (!strcmp(match->nick, "ok"))
-              {
-                ctx_frame_ack = 1;
-                return NULL;
-              }
-              return match->nick;
-              break;
-            case 9001: /* mouse event */
-              if (x) *x = ((unsigned char)buf[4]-32)*1.0;
-              if (y) *y = ((unsigned char)buf[5]-32)*1.0;
-              switch (buf[3])
-                {
-                        /* XXX : todo reduce this to less string constants */
-                  case 32:  return "mouse-press";
-                  case 33:  return "mouse1-press";
-                  case 34:  return "mouse2-press";
-                  case 40:  return "alt-mouse-press";
-                  case 41:  return "alt-mouse1-press";
-                  case 42:  return "alt-mouse2-press";
-                  case 48:  return "control-mouse-press";
-                  case 49:  return "control-mouse1-press";
-                  case 50:  return "control-mouse2-press";
-                  case 56:  return "alt-control-mouse-press";
-                  case 57:  return "alt-control-mouse1-press";
-                  case 58:  return "alt-control-mouse2-press";
-                  case 64:  return "mouse-drag";
-                  case 65:  return "mouse1-drag";
-                  case 66:  return "mouse2-drag";
-                  case 71:  return "mouse-motion"; /* shift+motion */
-                  case 72:  return "alt-mouse-drag";
-                  case 73:  return "alt-mouse1-drag";
-                  case 74:  return "alt-mouse2-drag";
-                  case 75:  return "mouse-motion"; /* alt+motion */
-                  case 80:  return "control-mouse-drag";
-                  case 81:  return "control-mouse1-drag";
-                  case 82:  return "control-mouse2-drag";
-                  case 83:  return "mouse-motion"; /* ctrl+motion */
-                  case 91:  return "mouse-motion"; /* ctrl+alt+motion */
-                  case 95:  return "mouse-motion"; /* ctrl+alt+shift+motion */
-                  case 96:  return "scroll-up";
-                  case 97:  return "scroll-down";
-                  case 100: return "shift-scroll-up";
-                  case 101: return "shift-scroll-down";
-                  case 104: return "alt-scroll-up";
-                  case 105: return "alt-scroll-down";
-                  case 112: return "control-scroll-up";
-                  case 113: return "control-scroll-down";
-                  case 116: return "control-shift-scroll-up";
-                  case 117: return "control-shift-scroll-down";
-                  case 35: /* (or release) */
-                  case 51: /* (or ctrl-release) */
-                  case 43: /* (or alt-release) */
-                  case 67: return "mouse-motion";
-                           /* have a separate mouse-drag ? */
-                  default: {
-                             static char rbuf[100];
-                             sprintf (rbuf, "mouse (unhandled state: %i)", buf[3]);
-                             return rbuf;
-                           }
-                }
-            case 0: /* no matches, bail*/
-              { 
-                static char ret[256];
-                if (length == 0 && ctx_utf8_len (buf[0])>1) /* single unicode
-                                                               char */
-                  {
-                    read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
-                    buf[ctx_utf8_len(buf[0])]=0;
-                    strcpy (ret, (const char*)buf);
-                    return ret;
-                  }
-                if (length == 0) /* ascii */
-                  {
-                    buf[1]=0;
-                    strcpy (ret, (const char*)buf);
-                    return ret;
-                  }
-                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
-                  length>=0? buf[0]: 0, length>=0? buf[0]>31?buf[0]:'?': ' ', 
-                  length>=1? buf[1]: 0, length>=1? buf[1]>31?buf[1]:'?': ' ', 
-                  length>=2? buf[2]: 0, length>=2? buf[2]>31?buf[2]:'?': ' ', 
-                  length>=3? buf[3]: 0, length>=3? buf[3]>31?buf[3]:'?': ' ',
-                  length>=4? buf[4]: 0, length>=4? buf[4]>31?buf[4]:'?': ' ',
-                  length>=5? buf[5]: 0, length>=5? buf[5]>31?buf[5]:'?': ' ',
-                  length>=6? buf[6]: 0, length>=6? buf[6]>31?buf[6]:'?': ' ');
-                return ret;
-              }
-              return NULL;
-            default: /* continue */
-              break;
-          }
-      }
-    else
-      return "key read eek";
-  return "fail";
-}
-
-int ctx_nct_consume_events (Ctx *ctx)
+uint32_t
+ctx_utf8_to_unichar (const char *input)
 {
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
+  const uint8_t *utf8 = (const uint8_t *) input;
+  uint8_t c = utf8[0];
+  if ( (c & 0x80) == 0)
+    { return c; }
+  else if ( (c & 0xE0) == 0xC0)
+    return ( (utf8[0] & 0x1F) << 6) |
+           (utf8[1] & 0x3F);
+  else if ( (c & 0xF0) == 0xE0)
+    return ( (utf8[0] & 0xF)  << 12) |
+           ( (utf8[1] & 0x3F) << 6) |
+           (utf8[2] & 0x3F);
+  else if ( (c & 0xF8) == 0xF0)
+    return ( (utf8[0] & 0x7)  << 18) |
+           ( (utf8[1] & 0x3F) << 12) |
+           ( (utf8[2] & 0x3F) << 6) |
+           (utf8[3] & 0x3F);
+  else if ( (c & 0xFC) == 0xF8)
+    return ( (utf8[0] & 0x3)  << 24) |
+           ( (utf8[1] & 0x3F) << 18) |
+           ( (utf8[2] & 0x3F) << 12) |
+           ( (utf8[3] & 0x3F) << 6) |
+           (utf8[4] & 0x3F);
+  else if ( (c & 0xFE) == 0xFC)
+    return ( (utf8[0] & 0x1)  << 30) |
+           ( (utf8[1] & 0x3F) << 24) |
+           ( (utf8[2] & 0x3F) << 18) |
+           ( (utf8[3] & 0x3F) << 12) |
+           ( (utf8[4] & 0x3F) << 6) |
+           (utf8[5] & 0x3F);
+  return 0;
+}
 
-  {
-    float x, y;
-    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
+#if CTX_RASTERIZER
 
-    x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
-    y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
 
-    if (!strcmp (event, "mouse-press"))
-    {
-      ctx_pointer_press (ctx, x, y, 0, 0);
-      ctxctx->was_down = 1;
-    } else if (!strcmp (event, "mouse-release"))
-    {
-      ctx_pointer_release (ctx, x, y, 0, 0);
-      ctxctx->was_down = 0;
-    } else if (!strcmp (event, "mouse-motion"))
-    {
-      //nct_set_cursor_pos (backend->term, ix, iy);
-      //nct_flush (backend->term);
-      if (ctxctx->was_down)
-      {
-        ctx_pointer_release (ctx, x, y, 0, 0);
-        ctxctx->was_down = 0;
-      }
-      ctx_pointer_motion (ctx, x, y, 0, 0);
-    } else if (!strcmp (event, "mouse-drag"))
-    {
-      ctx_pointer_motion (ctx, x, y, 0, 0);
-    } else if (!strcmp (event, "size-changed"))
-    {
-#if 0
-      int width = nct_sys_terminal_width ();
-      int height = nct_sys_terminal_height ();
-      nct_set_size (backend->term, width, height);
-      width *= CPX;
-      height *= CPX;
-      free (mrg->glyphs);
-      free (mrg->styles);
-      free (backend->nct_pixels);
-      backend->nct_pixels = calloc (width * height * 4, 1);
-      mrg->glyphs = calloc ((width/CPX) * (height/CPX) * 4, 1);
-      mrg->styles = calloc ((width/CPX) * (height/CPX) * 1, 1);
-      mrg_set_size (mrg, width, height);
-      mrg_queue_draw (mrg, NULL);
-#endif
 
-    }
-    else
-    {
-      if (!strcmp (event, "esc"))
-        ctx_key_press (ctx, 0, "escape", 0);
-      else if (!strcmp (event, "space"))
-        ctx_key_press (ctx, 0, "space", 0);
-      else if (!strcmp (event, "enter"))
-        ctx_key_press (ctx, 0, "\n", 0);
-      else if (!strcmp (event, "return"))
-        ctx_key_press (ctx, 0, "\n", 0);
-      else
-      ctx_key_press (ctx, 0, event, 0);
-    }
-  }
+static int
+ctx_rect_intersect (const CtxIntRectangle *a, const CtxIntRectangle *b)
+{
+  if (a->x >= b->x + b->width ||
+      b->x >= a->x + a->width ||
+      a->y >= b->y + b->height ||
+      b->y >= a->y + a->height) return 0;
 
   return 1;
 }
 
-const char *ctx_native_get_event (Ctx *n, int timeoutms)
+static void
+_ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
 {
-  static unsigned char buf[256];
-  int length;
-
-  if (!signal_installed)
-    {
-      _nc_raw ();
-      signal_installed = 1;
-      signal (SIGWINCH, nc_resize_term);
+  CtxIntRectangle rect = {0,0, hasher->rasterizer.blit_width/hasher->cols,
+                            hasher->rasterizer.blit_height/hasher->rows};
+  int hno = 0;
+  for (int row = 0; row < hasher->rows; row++)
+    for (int col = 0; col < hasher->cols; col++, hno++)
+     {
+      rect.x = col * rect.width;
+      rect.y = row * rect.height;
+      if (ctx_rect_intersect (shape_rect, &rect))
+      {
+        int temp = hasher->hashes[(row * hasher->cols + col)  *20 + 0];
+        for (int i = 0; i <19;i++)
+           hasher->hashes[(row * hasher->cols + col)  *20 + i] =
+             hasher->hashes[(row * hasher->cols + col)  *20 + i+1]^
+             hash[i];
+        hasher->hashes[(row * hasher->cols + col)  *20 + 19] =
+                temp ^ hash[19];
+      }
     }
-//if (mouse_mode) // XXX too often to do it all the time!
-//  printf(mouse_modes[mouse_mode]);
+}
 
-    int got_event = 0;
-  {
-    int elapsed = 0;
 
-    do {
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      got_event = ctx_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      /* only do this if the client has asked for idle events,
-       * and perhaps programmed the ms timer?
-       */
-      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
-      if (!got_event && timeoutms && elapsed >= timeoutms)
-      {
-        return "idle";
-      }
-    } while (!got_event);
-  }
-
-  for (length = 0; got_event && length < 200; length ++)
-  {
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-         buf[length+1] = 0;
-         if (!strcmp ((char*)buf, "\e[0n"))
-         {
-           ctx_frame_ack = 1;
-           return NULL;
-         }
-         else if (buf[length]=='\n')
-         {
-           buf[length]=0;
-           return (const char*)buf;
-         }
-      }
-      got_event = ctx_has_event (n, 5);
-    }
-  return NULL;
-}
-
-const char *ctx_key_get_label (Ctx  *n, const char *nick)
+static void
+ctx_hasher_process (void *user_data, CtxCommand *command)
 {
-  int j;
-  int found = -1;
-  for (j = 0; keycodes[j].nick; j++)
-    if (found == -1 && !strcmp (keycodes[j].nick, nick))
-      return keycodes[j].label;
-  return NULL;
-}
+  CtxEntry *entry = &command->entry;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
+  CtxHasher *hasher = (CtxHasher*) user_data;
+  CtxState *state = rasterizer->state;
+  CtxCommand *c = (CtxCommand *) entry;
+  int aa = rasterizer->aa;
 
-void _ctx_mouse (Ctx *term, int mode)
-{
-  //if (term->is_st && mode > 1)
-  //  mode = 1;
-  if (mode != mouse_mode)
-  {
-    printf (mouse_modes[mode]);
-    fflush (stdout);
-  }
-  mouse_mode = mode;
-}
+  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
+  ctx_interpret_style (rasterizer->state, entry, NULL);
 
+  switch (c->code)
+    {
+      case CTX_TEXT:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
 
-#endif
 
-#include <sys/time.h>
+          float height = ctx_get_font_size (rasterizer->ctx);
+           CtxIntRectangle shape_rect;
+          
+           shape_rect.x=rasterizer->x;
+           shape_rect.y=rasterizer->y - height,
+           shape_rect.width = width;
+           shape_rect.height = height * 2;
+          switch ((int)ctx_state_get (rasterizer->state, CTX_text_align))
+          {
+          case CTX_TEXT_ALIGN_LEFT:
+          case CTX_TEXT_ALIGN_START:
+                  break;
+          case CTX_TEXT_ALIGN_END:
+          case CTX_TEXT_ALIGN_RIGHT:
+           shape_rect.x -= shape_rect.width;
+           break;
+          case CTX_TEXT_ALIGN_CENTER:
+           shape_rect.x -= shape_rect.width/2;
+           break;
+                   // XXX : doesn't take all text-alignments into account
+          }
 
+#if 0
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
+#endif
+          ctx_sha1_process(&sha1, (const unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+#endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
-#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+        }
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_STROKE_TEXT:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
+          float height = ctx_get_font_size (rasterizer->ctx);
 
-#if CTX_EVENTS
-static struct timeval start_time;
+           CtxIntRectangle shape_rect = {
+              rasterizer->x, rasterizer->y - height,
+              width, height * 2
+           };
 
-static void
-_ctx_init_ticks (void)
-{
-  static int done = 0;
-  if (done)
-    return;
-  done = 1;
-  gettimeofday (&start_time, NULL);
-}
+#if 0
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
+#endif
+          ctx_sha1_process(&sha1, (unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+#endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
-static inline unsigned long
-_ctx_ticks (void)
-{
-  struct timeval measure_time;
-  gettimeofday (&measure_time, NULL);
-  return usecs (measure_time) - usecs (start_time);
-}
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+        }
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_GLYPH:
+         {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
 
-unsigned long
-ctx_ticks (void)
-{
-  _ctx_init_ticks ();
-  return _ctx_ticks ();
-}
+          char ctx_sha1_hash[20];
+          uint8_t string[8];
+          string[ctx_unichar_to_utf8 (c->u32.a0, string)]=0;
+          float width = ctx_text_width (rasterizer->ctx, (char*)string);
+          float height = ctx_get_font_size (rasterizer->ctx);
 
-uint32_t ctx_ms (Ctx *ctx)
-{
-  return _ctx_ticks () / 1000;
-}
+          float tx = rasterizer->x;
+          float ty = rasterizer->y;
+          float tw = width;
+          float th = height * 2;
 
+          _ctx_user_to_device (rasterizer->state, &tx, &ty);
+          _ctx_user_to_device_distance (rasterizer->state, &tw, &th);
+          CtxIntRectangle shape_rect = {tx,ty-th/2,tw,th};
 
-typedef enum _CtxFlags CtxFlags;
 
-enum _CtxFlags {
-   CTX_FLAG_DIRECT = (1<<0),
-};
+#if 0
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
+#endif
+          ctx_sha1_process(&sha1, string, strlen ((const char*)string));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+#endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+          ctx_rasterizer_reset (rasterizer);
+         }
+        break;
 
-int _ctx_max_threads = 1;
-int _ctx_enable_hash_cache = 1;
+      case CTX_FILL:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
 
-void
-ctx_init (int *argc, char ***argv)
-{
-#if 0
-  if (!getenv ("CTX_VERSION"))
-  {
-    int i;
-    char *new_argv[*argc+3];
-    new_argv[0] = "ctx";
-    for (i = 0; i < *argc; i++)
-    {
-      new_argv[i+1] = *argv[i];
-    }
-    new_argv[i+1] = NULL;
-    execvp (new_argv[0], new_argv);
-    // if this fails .. we continue normal startup
-    // and end up in self-hosted braille
-  }
-#endif
-}
+          /* we eant this hasher to be as good as possible internally,
+           * since it is also used in the small shapes rasterization
+           * cache
+           */
+        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer); // + hasher->salt;
+        CtxIntRectangle shape_rect = {
+          rasterizer->col_min / CTX_SUBDIV,
+          rasterizer->scan_min / aa,
+          (rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV,
+          (rasterizer->scan_max - rasterizer->scan_min + 1) / aa
+        };
 
-int ctx_count (Ctx *ctx)
-{
-  return ctx->drawlist.count;
-}
+        hash ^= (rasterizer->state->gstate.fill_rule * 23);
 
-Ctx *ctx_new_ui (int width, int height)
-{
-  if (getenv ("CTX_THREADS"))
-  {
-    int val = atoi (getenv ("CTX_THREADS"));
-    _ctx_max_threads = val;
-  }
-  else
-  {
-    _ctx_max_threads = 2;
-#ifdef _SC_NPROCESSORS_ONLN
-    _ctx_max_threads = sysconf (_SC_NPROCESSORS_ONLN) / 2;
-#endif
-  }
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
 
-  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
-  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
+        {
+          int is = rasterizer->state->gstate.image_smoothing;
+          ctx_sha1_process(&sha1, (uint8_t*)&is, sizeof(int));
+        }
 
-  //fprintf (stderr, "ctx using %i threads\n", _ctx_max_threads);
-  const char *backend = getenv ("CTX_BACKEND");
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
-  if (backend && !strcmp (backend, "auto"))
-    backend = NULL;
-  if (backend && !strcmp (backend, "list"))
-  {
-    fprintf (stderr, "possible values for CTX_BACKEND:\n");
-    fprintf (stderr, " ctx");
-#if CTX_SDL
-    fprintf (stderr, " SDL");
-#endif
-#if CTX_FB
-    fprintf (stderr, " fb");
-    fprintf (stderr, " drm");
-#endif
-    fprintf (stderr, " braille");
-    fprintf (stderr, "\n");
-    exit (-1);
-  }
+        if (!rasterizer->preserve)
+          ctx_rasterizer_reset (rasterizer);
+        rasterizer->preserve = 0;
+        }
+        break;
+      case CTX_STROKE:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer);
+        CtxIntRectangle shape_rect = {
+          rasterizer->col_min / CTX_SUBDIV - rasterizer->state->gstate.line_width,
+          rasterizer->scan_min / aa - rasterizer->state->gstate.line_width,
+          (rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV + 
rasterizer->state->gstate.line_width,
+          (rasterizer->scan_max - rasterizer->scan_min + 1) / aa + rasterizer->state->gstate.line_width
+        };
 
-  Ctx *ret = NULL;
-  /* FIXME: to a terminal query instead - to avoid relying on
-   * environment variables - thus making it work reliably over
-   * ssh without configuration.
-   */
-  if (getenv ("CTX_VERSION"))
-  {
-    if (!backend || !strcmp (backend, "ctx"))
-    {
-      // full blown ctx protocol - in terminal or standalone
-      ret = ctx_new_ctx (width, height);
-    }
-  }
+        shape_rect.width += rasterizer->state->gstate.line_width * 2;
+        shape_rect.height += rasterizer->state->gstate.line_width * 2;
+        shape_rect.x -= rasterizer->state->gstate.line_width;
+        shape_rect.y -= rasterizer->state->gstate.line_width;
 
-#if CTX_SDL
-  if (!ret && getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "SDL")))
-      ret = ctx_new_sdl (width, height);
-  }
-#endif
+        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
+        hash ^= (rasterizer->state->gstate.line_cap * 23);
+        hash ^= (rasterizer->state->gstate.source_stroke.type * 117);
 
-#if CTX_FB
-  if (!ret && !getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "drm")))
-    ret = ctx_new_fb (width, height, 1);
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
 
-    if (!ret)
-    {
-      if ((backend==NULL) || (!strcmp (backend, "fb")))
-        ret = ctx_new_fb (width, height, 0);
-    }
-  }
-#endif
+        uint32_t color;
+        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
 
-#if CTX_RASTERIZER
-  // braille in terminal
-  if (!ret)
-  {
-    if ((backend==NULL) || (!strcmp (backend, "braille")))
-    ret = ctx_new_braille (width, height);
-  }
-#endif
-  if (!ret)
-  {
-    fprintf (stderr, "no interactive ctx backend\n");
-    exit (2);
-  }
-  ctx_get_event (ret); // enables events
-  return ret;
-}
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
 
-#endif
-void _ctx_resized (Ctx *ctx, int width, int height, long time);
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+        }
+        if (!rasterizer->preserve)
+          ctx_rasterizer_reset (rasterizer);
+        rasterizer->preserve = 0;
+        break;
+        /* the above cases are the painting cases and 
+         * the only ones differing from the rasterizer's process switch
+         */
 
-void ctx_set_size (Ctx *ctx, int width, int height)
-{
-#if CTX_EVENTS
-  if (ctx->events.width != width || ctx->events.height != height)
-  {
-    ctx->events.width = width;
-    ctx->events.height = height;
-    _ctx_resized (ctx, width, height, 0);
-  }
+      case CTX_LINE_TO:
+        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_MOVE_TO:
+        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_CURVE_TO:
+        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                 c->c.x1, c->c.y1,
+                                 c->c.x2, c->c.y2);
+        break;
+      case CTX_REL_CURVE_TO:
+        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                     c->c.x1, c->c.y1,
+                                     c->c.x2, c->c.y2);
+        break;
+      case CTX_QUAD_TO:
+        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_REL_QUAD_TO:
+        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_ARC:
+        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                  c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                        c->rectangle.width, c->rectangle.height,
+                                        c->rectangle.radius);
+        break;
+      case CTX_SET_PIXEL:
+        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
+                                  c->set_pixel.rgba[0],
+                                  c->set_pixel.rgba[1],
+                                  c->set_pixel.rgba[2],
+                                  c->set_pixel.rgba[3]);
+        break;
+      case CTX_PRESERVE:
+        rasterizer->preserve = 1;
+        break;
+      case CTX_ROTATE:
+      case CTX_SCALE:
+      case CTX_TRANSLATE:
+      case CTX_SAVE:
+      case CTX_RESTORE:
+        rasterizer->uses_transforms = 1;
+        ctx_interpret_transforms (rasterizer->state, entry, NULL);
+
+        
+        break;
+      case CTX_FONT:
+        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
+        break;
+      case CTX_BEGIN_PATH:
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_CLIP:
+        // should perhaps modify a global state to include
+        // in hash?
+        ctx_rasterizer_clip (rasterizer);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_rasterizer_finish_shape (rasterizer);
+        break;
+      case CTX_DEFINE_TEXTURE:
+        {
+        ctx_sha1_init (&hasher->sha1_fill);
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->define_texture.eid, strlen 
(c->define_texture.eid));
+        ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+
+        rasterizer->comp_op = NULL; // why?
+        }
+        break;
+      case CTX_TEXTURE:
+        ctx_sha1_init (&hasher->sha1_fill);
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->texture.eid, strlen (c->texture.eid));
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+        rasterizer->comp_op = NULL; // why?
+        break;
+      case CTX_COLOR:
+        {
+          uint32_t color;
+          if (((int)(ctx_arg_float(0))&512))
+          {
+            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
+            ctx_sha1_init (&hasher->sha1_stroke);
+            ctx_sha1_process(&hasher->sha1_stroke, (unsigned char*)&color, 4);
+          }
+          else
+          {
+            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
+            ctx_sha1_init (&hasher->sha1_fill);
+            ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)&color, 4);
+          }
+        }
+        break;
+      case CTX_LINEAR_GRADIENT:
+          ctx_sha1_init (&hasher->sha1_fill);
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*)c, sizeof (c->linear_gradient));
+          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
+        break;
+      case CTX_RADIAL_GRADIENT:
+          ctx_sha1_init (&hasher->sha1_fill);
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*)c, sizeof (c->radial_gradient));
+          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
+        //ctx_state_gradient_clear_stops (rasterizer->state);
+        break;
+#if CTX_GRADIENTS
+      case CTX_GRADIENT_STOP:
+        {
+          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
+                         };
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*) &rgba[0], sizeof(rgba));
+        }
+        break;
 #endif
+    }
+  if (command->code == CTX_LINE_WIDTH)
+    {
+      float x = state->gstate.line_width;
+      /* normalize line width according to scaling factor
+       */
+      x = x * ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
+                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
+                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
+                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
+      state->gstate.line_width = x;
+    }
 }
 
-#if CTX_EVENTS
-typedef struct CtxIdleCb {
-  int (*cb) (Ctx *ctx, void *idle_data);
-  void *idle_data;
+static CtxRasterizer *
+ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width, int height, int cols, int 
rows)
+{
+  CtxHasher *hasher = (CtxHasher*)rasterizer;
+  ctx_memset (rasterizer, 0, sizeof (CtxHasher) );
+  rasterizer->vfuncs.process = ctx_hasher_process;
+  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
+  // XXX need own destructor to not leak ->hashes
+  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
+  rasterizer->state       = state;
+  rasterizer->ctx         = ctx;
+  ctx_state_init (rasterizer->state);
+  rasterizer->blit_x      = 0;
+  rasterizer->blit_y      = 0;
+  rasterizer->blit_width  = width;
+  rasterizer->blit_height = height;
+  rasterizer->state->gstate.clip_min_x  = 0;
+  rasterizer->state->gstate.clip_min_y  = 0;
+  rasterizer->state->gstate.clip_max_x  = width - 1;
+  rasterizer->state->gstate.clip_max_y  = height - 1;
+  rasterizer->scan_min    = 5000;
+  rasterizer->scan_max    = -5000;
+  rasterizer->aa          = 5;
+  rasterizer->force_aa    = 0;
 
-  void (*destroy_notify)(void *destroy_data);
-  void *destroy_data;
+  hasher->rows = rows;
+  hasher->cols = cols;
 
-  int   ticks_full;
-  int   ticks_remaining;
-  int   is_idle;
-  int   id;
-} CtxIdleCb;
+  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
+  ctx_sha1_init (&hasher->sha1_fill);
+  ctx_sha1_init (&hasher->sha1_stroke);
 
-void _ctx_events_init (Ctx *ctx)
+  return rasterizer;
+}
+
+Ctx *ctx_hasher_new (int width, int height, int cols, int rows)
 {
-  CtxEvents *events = &ctx->events;
-  _ctx_init_ticks ();
-  events->tap_delay_min  = 40;
-  events->tap_delay_max  = 800;
-  events->tap_delay_max  = 8000000; /* quick reflexes needed making it hard for some is an argument against 
very short values  */
+  Ctx *ctx           = ctx_new ();
+  CtxState    *state = &ctx->state;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_calloc (sizeof (CtxHasher), 1);
+  ctx_hasher_init (rasterizer, ctx, state, width, height, cols, rows);
+  ctx_set_renderer (ctx, (void*)rasterizer);
+  return ctx;
+}
+uint8_t *ctx_hasher_get_hash (Ctx *ctx, int col, int row)
+{
+  CtxHasher *hasher = (CtxHasher*)ctx->renderer;
+  if (row < 0) row =0;
+  if (col < 0) col =0;
+  if (row >= hasher->rows) row = hasher->rows-1;
+  if (col >= hasher->cols) col = hasher->cols-1;
 
-  events->tap_delay_hold = 1000;
-  events->tap_hysteresis = 32;  /* XXX: should be ppi dependent */
+  return &hasher->hashes[(row*hasher->cols+col)*20];
 }
 
+#endif
+#if CTX_EVENTS
 
-void _ctx_idle_iteration (Ctx *ctx)
-{
-  static unsigned long prev_ticks = 0;
-  CtxList *l;
-  CtxList *to_remove = NULL;
-  unsigned long ticks = _ctx_ticks ();
-  unsigned long tick_delta = (prev_ticks == 0) ? 0 : ticks - prev_ticks;
-  prev_ticks = ticks;
+#include <termios.h>
 
-  if (!ctx->events.idles)
-  {
-    return;
-  }
-  for (l = ctx->events.idles; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
+#include <fcntl.h>
+#include <sys/ioctl.h>
 
-    if (item->ticks_remaining >= 0)
-      item->ticks_remaining -= tick_delta;
+int ctx_terminal_width (void)
+{
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[14t");
+  //tcflush(STDIN_FILENO, 1);
+  tcdrain(STDIN_FILENO);
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
+  
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
 
-    if (item->ticks_remaining < 0)
-    {
-      if (item->cb (ctx, item->idle_data) == 0)
-        ctx_list_prepend (&to_remove, item);
-      else
-        item->ticks_remaining = item->ticks_full;
-    }
-  }
-  for (l = to_remove; l; l = l->next)
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
   {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
+    length += read (STDIN_FILENO, &buf[length], 1);
   }
-}
-
-
-void ctx_add_key_binding_full (Ctx *ctx,
-                           const char *key,
-                           const char *action,
-                           const char *label,
-                           CtxCb       cb,
-                           void       *cb_data,
-                           CtxDestroyNotify destroy_notify,
-                           void       *destroy_data)
-{
-  CtxEvents *events = &ctx->events;
-  if (events->n_bindings +1 >= CTX_MAX_KEYBINDINGS)
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
   {
-    fprintf (stderr, "warning: binding overflow\n");
-    return;
+    return 0;
   }
-  events->bindings[events->n_bindings].nick = strdup (key);
-  strcpy (events->bindings[events->n_bindings].nick, key);
-
-  if (action)
-    events->bindings[events->n_bindings].command = action ? strdup (action) : NULL;
-  if (label)
-    events->bindings[events->n_bindings].label = label ? strdup (label) : NULL;
-  events->bindings[events->n_bindings].cb = cb;
-  events->bindings[events->n_bindings].cb_data = cb_data;
-  events->bindings[events->n_bindings].destroy_notify = destroy_notify;
-  events->bindings[events->n_bindings].destroy_data = destroy_data;
-  events->n_bindings++;
-}
-
-void ctx_add_key_binding (Ctx *ctx,
-                          const char *key,
-                          const char *action,
-                          const char *label,
-                          CtxCb       cb,
-                          void       *cb_data)
-{
-  ctx_add_key_binding_full (ctx, key, action, label, cb, cb_data, NULL, NULL);
-}
-
-void ctx_clear_bindings (Ctx *ctx)
-{
-  CtxEvents *events = &ctx->events;
-  int i;
-  for (i = 0; events->bindings[i].nick; i ++)
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi) {semi++; semi = strchr (semi, ';');}
+  if (semi)
   {
-    if (events->bindings[i].destroy_notify)
-      events->bindings[i].destroy_notify (events->bindings[i].destroy_data);
-    free (events->bindings[i].nick);
-    if (events->bindings[i].command)
-      free (events->bindings[i].command);
-    if (events->bindings[i].label)
-      free (events->bindings[i].label);
+    return atoi(semi + 1);
   }
-  memset (&events->bindings, 0, sizeof (events->bindings));
-  events->n_bindings = 0;
-}
-
-static void _ctx_bindings_key_down (CtxEvent *event, void *data1, void *data2)
-{
-  Ctx *ctx = event->ctx;
-  CtxEvents *events = &ctx->events;
-  int i;
-  int handled = 0;
-
-  for (i = events->n_bindings-1; i>=0; i--)
-    if (!strcmp (events->bindings[i].nick, event->string))
-    {
-      if (events->bindings[i].cb)
-      {
-        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
-        if (event->stop_propagate)
-          return;
-        handled = 1;
-      }
-    }
-  if (!handled)
-  for (i = events->n_bindings-1; i>=0; i--)
-    if (!strcmp (events->bindings[i].nick, "unhandled"))
-    {
-      if (events->bindings[i].cb)
-      {
-        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
-        if (event->stop_propagate)
-          return;
-      }
-    }
-}
-
-CtxBinding *ctx_get_bindings (Ctx *ctx)
-{
-  return &ctx->events.bindings[0];
+  return 0;
 }
 
-void ctx_remove_idle (Ctx *ctx, int handle)
+int ctx_terminal_height (void)
 {
-  CtxList *l;
-  CtxList *to_remove = NULL;
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[14t");
+  //tcflush(STDIN_FILENO, 1);
+  tcdrain(STDIN_FILENO);
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
+  
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
 
-  if (!ctx->events.idles)
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
   {
-    return;
+    length += read (STDIN_FILENO, &buf[length], 1);
   }
-  for (l = ctx->events.idles; l; l = l->next)
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
   {
-    CtxIdleCb *item = l->data;
-    if (item->id == handle)
-      ctx_list_prepend (&to_remove, item);
+    return 0;
   }
-  for (l = to_remove; l; l = l->next)
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi)
   {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
+    return atoi(semi + 1);
   }
+  return 0;
 }
 
-int ctx_add_timeout_full (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
-                          void (*destroy_notify)(void *destroy_data), void *destroy_data)
+int ctx_terminal_cols (void)
 {
-  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
-  item->cb              = idle_cb;
-  item->idle_data       = idle_data;
-  item->id              = ++ctx->events.idle_id;
-  item->ticks_full      = 
-  item->ticks_remaining = ms * 1000;
-  item->destroy_notify  = destroy_notify;
-  item->destroy_data    = destroy_data;
-  ctx_list_append (&ctx->events.idles, item);
-  return item->id;
-}
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 80;
+  return ws.ws_col;
+} 
 
-int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+int ctx_terminal_rows (void)
 {
-  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 25;
+  return ws.ws_row;
 }
 
-int ctx_add_idle_full (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
-                                 void (*destroy_notify)(void *destroy_data), void *destroy_data)
-{
-  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
-  item->cb = idle_cb;
-  item->idle_data = idle_data;
-  item->id = ++ctx->events.idle_id;
-  item->ticks_full =
-  item->ticks_remaining = -1;
-  item->is_idle = 1;
-  item->destroy_notify = destroy_notify;
-  item->destroy_data = destroy_data;
-  ctx_list_append (&ctx->events.idles, item);
-  return item->id;
-}
 
-int ctx_add_idle (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
-{
-  return ctx_add_idle_full (ctx, idle_cb, idle_data, NULL, NULL);
-}
 
+
+
+#define DECTCEM_CURSOR_SHOW      "\033[?25h"
+#define DECTCEM_CURSOR_HIDE      "\033[?25l"
+#define TERMINAL_MOUSE_OFF       "\033[?1000l\033[?1003l"
+#define TERMINAL_MOUSE_ON_BASIC  "\033[?1000h"
+#define TERMINAL_MOUSE_ON_DRAG   "\033[?1000h\033[?1003h" /* +ON_BASIC for wider */
+#define TERMINAL_MOUSE_ON_FULL   "\033[?1000h\033[?1004h" /* compatibility */
+#define XTERM_ALTSCREEN_ON       "\033[?47h"
+#define XTERM_ALTSCREEN_OFF      "\033[?47l"
+
+/*************************** input handling *************************/
+
+#include <termios.h>
+#include <errno.h>
+#include <signal.h>
+
+#define DELAY_MS  100  
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
 #endif
-/* using bigger primes would be a good idea, this falls apart due to rounding
- * when zoomed in close
- */
-static inline double ctx_path_hash (void *path)
-{
-  double ret = 0;
-#if 0
-  int i;
-  cairo_path_data_t *data;
-  if (!path)
-    return 0.99999;
-  for (i = 0; i <path->num_data; i += path->data[i].header.length)
-  {
-    data = &path->data[i];
-    switch (data->header.type) {
-      case CAIRO_PATH_MOVE_TO:
-        ret *= 17;
-        ret += data[1].point.x;
-        ret *= 113;
-        ret += data[1].point.y;
-        break;
-      case CAIRO_PATH_LINE_TO:
-        ret *= 121;
-        ret += data[1].point.x;
-        ret *= 1021;
-        ret += data[1].point.y;
-        break;
-      case CAIRO_PATH_CURVE_TO:
-        ret *= 3111;
-        ret += data[1].point.x;
-        ret *= 23;
-        ret += data[1].point.y;
-        ret *= 107;
-        ret += data[2].point.x;
-        ret *= 739;
-        ret += data[2].point.y;
-        ret *= 3;
-        ret += data[3].point.x;
-        ret *= 51;
-        ret += data[3].point.y;
-        break;
-      case CAIRO_PATH_CLOSE_PATH:
-        ret *= 51;
-        break;
-    }
-  }
-#endif
-  return ret;
-}
 
-#if CTX_EVENTS
-void _ctx_item_ref (CtxItem *item)
-{
-  if (item->ref_count < 0)
-  {
-    fprintf (stderr, "EEEEK!\n");
-  }
-  item->ref_count++;
-}
+static int  size_changed = 0;       /* XXX: global state */
+static int  signal_installed = 0;   /* XXX: global state */
 
+static const char *mouse_modes[]=
+{TERMINAL_MOUSE_OFF,
+ TERMINAL_MOUSE_ON_BASIC,
+ TERMINAL_MOUSE_ON_DRAG,
+ TERMINAL_MOUSE_ON_FULL,
+ NULL};
 
-void _ctx_item_unref (CtxItem *item)
-{
-  if (item->ref_count <= 0)
-  {
-    fprintf (stderr, "EEEEK!\n");
-    return;
-  }
-  item->ref_count--;
-  if (item->ref_count <=0)
-  {
-    {
-      int i;
-      for (i = 0; i < item->cb_count; i++)
-      {
-        if (item->cb[i].finalize)
-          item->cb[i].finalize (item->cb[i].data1, item->cb[i].data2,
-                                   item->cb[i].finalize_data);
-      }
-    }
-    if (item->path)
-    {
-      //cairo_path_destroy (item->path);
-    }
-    free (item);
-  }
-}
+/* note that a nick can have multiple occurences, the labels
+ * should be kept the same for all occurences of a combination. */
+typedef struct NcKeyCode {
+  const char *nick;          /* programmers name for key (combo) */
+  const char *label;         /* utf8 label for key */
+  const char  sequence[10];  /* terminal sequence */
+} NcKeyCode;
+static const NcKeyCode keycodes[]={  
 
+  {"up",                  "↑",     "\033[A"},
+  {"down",                "↓",     "\033[B"},
+  {"right",               "→",     "\033[C"},
+  {"left",                "←",     "\033[D"},
 
-static int
-path_equal (void *path,
-            void *path2)
-{
-  //  XXX
-  return 0;
-}
+  {"shift-up",            "⇧↑",    "\033[1;2A"},
+  {"shift-down",          "⇧↓",    "\033[1;2B"},
+  {"shift-right",         "⇧→",    "\033[1;2C"},
+  {"shift-left",          "⇧←",    "\033[1;2D"},
 
-void ctx_listen_set_cursor (Ctx      *ctx,
-                            CtxCursor cursor)
-{
-  if (ctx->events.last_item)
-  {
-    ctx->events.last_item->cursor = cursor;
-  }
-}
+  {"alt-up",              "^↑",    "\033[1;3A"},
+  {"alt-down",            "^↓",    "\033[1;3B"},
+  {"alt-right",           "^→",    "\033[1;3C"},
+  {"alt-left",            "^←",    "\033[1;3D"},
 
-void ctx_listen_full (Ctx     *ctx,
-                      float    x,
-                      float    y,
-                      float    width,
-                      float    height,
-                      CtxEventType  types,
-                      CtxCb    cb,
-                      void    *data1,
-                      void    *data2,
-                      void   (*finalize)(void *listen_data,
-                                         void *listen_data2,
-                                         void *finalize_data),
-                      void    *finalize_data)
-{
-  if (!ctx->events.frozen)
-  {
-    CtxItem *item;
+  {"alt-shift-up",        "alt-s↑", "\033[1;4A"},
+  {"alt-shift-down",      "alt-s↓", "\033[1;4B"},
+  {"alt-shift-right",     "alt-s→", "\033[1;4C"},
+  {"alt-shift-left",      "alt-s←", "\033[1;4D"},
 
-    /* early bail for listeners outside screen  */
-    /* XXX: fixme respect clipping */
-    {
-      float tx = x;
-      float ty = y;
-      float tw = width;
-      float th = height;
-      _ctx_user_to_device (&ctx->state, &tx, &ty);
-      _ctx_user_to_device_distance (&ctx->state, &tw, &th);
-      if (ty > ctx->events.height * 2 ||
-          tx > ctx->events.width * 2 ||
-          tx + tw < 0 ||
-          ty + th < 0)
-      {
-        if (finalize)
-          finalize (data1, data2, finalize_data);
-        return;
-      }
-    }
+  {"control-up",          "^↑",    "\033[1;5A"},
+  {"control-down",        "^↓",    "\033[1;5B"},
+  {"control-right",       "^→",    "\033[1;5C"},
+  {"control-left",        "^←",    "\033[1;5D"},
 
-    item = calloc (sizeof (CtxItem), 1);
-    item->x0 = x;
-    item->y0 = y;
-    item->x1 = x + width;
-    item->y1 = y + height;
-    item->cb[0].types = types;
-    item->cb[0].cb = cb;
-    item->cb[0].data1 = data1;
-    item->cb[0].data2 = data2;
-    item->cb[0].finalize = finalize;
-    item->cb[0].finalize_data = finalize_data;
-    item->cb_count = 1;
-    item->types = types;
-    //item->path = cairo_copy_path (cr); // XXX
-    item->path_hash = ctx_path_hash (item->path);
-    ctx_get_matrix (ctx, &item->inv_matrix);
-    ctx_matrix_invert (&item->inv_matrix);
+  /* putty */
+  {"control-up",          "^↑",    "\033OA"},
+  {"control-down",        "^↓",    "\033OB"},
+  {"control-right",       "^→",    "\033OC"},
+  {"control-left",        "^←",    "\033OD"},
 
-    if (ctx->events.items)
-    {
-      CtxList *l;
-      for (l = ctx->events.items; l; l = l->next)
-      {
-        CtxItem *item2 = l->data;
+  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
+  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
+  {"control-shift-right", "^⇧→",   "\033[1;6C"},
+  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
 
-        /* store multiple callbacks for one entry when the paths
-         * are exact matches, reducing per event traversal checks at the
-         * cost of a little paint-hit (XXX: is this the right tradeoff,
-         * perhaps it is better to spend more time during event processing
-         * than during paint?)
-         */
-        if (item->path_hash == item2->path_hash &&
-            path_equal (item->path, item2->path))
-        {
-          /* found an item, copy over cb data  */
-          item2->cb[item2->cb_count] = item->cb[0];
-          free (item);
-          item2->cb_count++;
-          item2->types |= types;
-          return;
-        }
-      }
-    }
-    item->ref_count       = 1;
-    ctx->events.last_item = item;
-    ctx_list_prepend_full (&ctx->events.items, item, (void*)_ctx_item_unref, NULL);
-  }
-}
+  {"control-up",          "^↑",    "\033Oa"},
+  {"control-down",        "^↓",    "\033Ob"},
+  {"control-right",       "^→",    "\033Oc"},
+  {"control-left",        "^←",    "\033Od"},
 
-void ctx_event_stop_propagate (CtxEvent *event)
-{
-  if (event)
-    event->stop_propagate = 1;
-}
+  {"shift-up",            "⇧↑",    "\033[a"},
+  {"shift-down",          "⇧↓",    "\033[b"},
+  {"shift-right",         "⇧→",    "\033[c"},
+  {"shift-left",          "⇧←",    "\033[d"},
 
-void ctx_listen (Ctx          *ctx,
-                 CtxEventType  types,
-                 CtxCb         cb,
-                 void*         data1,
-                 void*         data2)
-{
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
-  if (types & CTX_KEY)
-  {
-    x = 0;
-    y = 0;
-    width = 0;
-    height = 0;
-  }
-  else
-  {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
-  }
-
-  if (types == CTX_DRAG_MOTION)
-    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
-  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL);
-}
-
-void  ctx_listen_with_finalize (Ctx          *ctx,
-                                CtxEventType  types,
-                                CtxCb         cb,
-                                void*         data1,
-                                void*         data2,
-                      void   (*finalize)(void *listen_data, void *listen_data2,
-                                         void *finalize_data),
-                      void    *finalize_data)
-{
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
-  if (types & CTX_KEY)
-  {
-    x = 0;
-    y = 0;
-    width = 0;
-    height = 0;
-  }
-  else
-  {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
-  }
+  {"insert",              "ins",   "\033[2~"},
+  {"delete",              "del",   "\033[3~"},
+  {"page-up",             "PgUp",  "\033[5~"},
+  {"page-down",           "PdDn",  "\033[6~"},
+  {"home",                "Home",  "\033OH"},
+  {"end",                 "End",   "\033OF"},
+  {"home",                "Home",  "\033[H"},
+  {"end",                 "End",   "\033[F"},
+  {"control-delete",      "^del",  "\033[3;5~"},
+  {"shift-delete",        "⇧del",  "\033[3;2~"},
+  {"control-shift-delete","^⇧del", "\033[3;6~"},
 
-  if (types == CTX_DRAG_MOTION)
-    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
-  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data);
-}
+  {"F1",        "F1",  "\033[11~"},
+  {"F2",        "F2",  "\033[12~"},
+  {"F3",        "F3",  "\033[13~"},
+  {"F4",        "F4",  "\033[14~"},
+  {"F1",        "F1",  "\033OP"},
+  {"F2",        "F2",  "\033OQ"},
+  {"F3",        "F3",  "\033OR"},
+  {"F4",        "F4",  "\033OS"},
+  {"F5",        "F5",  "\033[15~"},
+  {"F6",        "F6",  "\033[16~"},
+  {"F7",        "F7",  "\033[17~"},
+  {"F8",        "F8",  "\033[18~"},
+  {"F9",        "F9",  "\033[19~"},
+  {"F9",        "F9",  "\033[20~"},
+  {"F10",       "F10", "\033[21~"},
+  {"F11",       "F11", "\033[22~"},
+  {"F12",       "F12", "\033[23~"},
+  {"tab",       "↹",     {9, '\0'}},
+  {"shift-tab", "shift+↹",  "\033[Z"},
+  {"backspace", "⌫",  {127, '\0'}},
+  {"space",     "␣",   " "},
+  {"esc",        "␛",  "\033"},
+  {"return",    "⏎",  {10,0}},
+  {"return",    "⏎",  {13,0}},
+  /* this section could be autogenerated by code */
+  {"control-a", "^A",  {1,0}},
+  {"control-b", "^B",  {2,0}},
+  {"control-c", "^C",  {3,0}},
+  {"control-d", "^D",  {4,0}},
+  {"control-e", "^E",  {5,0}},
+  {"control-f", "^F",  {6,0}},
+  {"control-g", "^G",  {7,0}},
+  {"control-h", "^H",  {8,0}}, /* backspace? */
+  {"control-i", "^I",  {9,0}}, /* tab */
+  {"control-j", "^J",  {10,0}},
+  {"control-k", "^K",  {11,0}},
+  {"control-l", "^L",  {12,0}},
+  {"control-n", "^N",  {14,0}},
+  {"control-o", "^O",  {15,0}},
+  {"control-p", "^P",  {16,0}},
+  {"control-q", "^Q",  {17,0}},
+  {"control-r", "^R",  {18,0}},
+  {"control-s", "^S",  {19,0}},
+  {"control-t", "^T",  {20,0}},
+  {"control-u", "^U",  {21,0}},
+  {"control-v", "^V",  {22,0}},
+  {"control-w", "^W",  {23,0}},
+  {"control-x", "^X",  {24,0}},
+  {"control-y", "^Y",  {25,0}},
+  {"control-z", "^Z",  {26,0}},
+  {"alt-0",     "%0",  "\0330"},
+  {"alt-1",     "%1",  "\0331"},
+  {"alt-2",     "%2",  "\0332"},
+  {"alt-3",     "%3",  "\0333"},
+  {"alt-4",     "%4",  "\0334"},
+  {"alt-5",     "%5",  "\0335"},
+  {"alt-6",     "%6",  "\0336"},
+  {"alt-7",     "%7",  "\0337"}, /* backspace? */
+  {"alt-8",     "%8",  "\0338"},
+  {"alt-9",     "%9",  "\0339"},
+  {"alt-+",     "%+",  "\033+"},
+  {"alt--",     "%-",  "\033-"},
+  {"alt-/",     "%/",  "\033/"},
+  {"alt-a",     "%A",  "\033a"},
+  {"alt-b",     "%B",  "\033b"},
+  {"alt-c",     "%C",  "\033c"},
+  {"alt-d",     "%D",  "\033d"},
+  {"alt-e",     "%E",  "\033e"},
+  {"alt-f",     "%F",  "\033f"},
+  {"alt-g",     "%G",  "\033g"},
+  {"alt-h",     "%H",  "\033h"}, /* backspace? */
+  {"alt-i",     "%I",  "\033i"},
+  {"alt-j",     "%J",  "\033j"},
+  {"alt-k",     "%K",  "\033k"},
+  {"alt-l",     "%L",  "\033l"},
+  {"alt-n",     "%N",  "\033m"},
+  {"alt-n",     "%N",  "\033n"},
+  {"alt-o",     "%O",  "\033o"},
+  {"alt-p",     "%P",  "\033p"},
+  {"alt-q",     "%Q",  "\033q"},
+  {"alt-r",     "%R",  "\033r"},
+  {"alt-s",     "%S",  "\033s"},
+  {"alt-t",     "%T",  "\033t"},
+  {"alt-u",     "%U",  "\033u"},
+  {"alt-v",     "%V",  "\033v"},
+  {"alt-w",     "%W",  "\033w"},
+  {"alt-x",     "%X",  "\033x"},
+  {"alt-y",     "%Y",  "\033y"},
+  {"alt-z",     "%Z",  "\033z"},
+  {"shift-tab", "shift-↹", {27, 9, 0}},
+  /* Linux Console  */
+  {"home",      "Home", "\033[1~"},
+  {"end",       "End",  "\033[4~"},
+  {"F1",        "F1",   "\033[[A"},
+  {"F2",        "F2",   "\033[[B"},
+  {"F3",        "F3",   "\033[[C"},
+  {"F4",        "F4",   "\033[[D"},
+  {"F5",        "F5",   "\033[[E"},
+  {"F6",        "F6",   "\033[[F"},
+  {"F7",        "F7",   "\033[[G"},
+  {"F8",        "F8",   "\033[[H"},
+  {"F9",        "F9",   "\033[[I"},
+  {"F10",       "F10",  "\033[[J"},
+  {"F11",       "F11",  "\033[[K"},
+  {"F12",       "F12",  "\033[[L"}, 
+  {"ok",        "",     "\033[0n"},
+  {NULL, }
+};
 
+static struct termios orig_attr;    /* in order to restore at exit */
+static int    nc_is_raw = 0;
+static int    atexit_registered = 0;
+static int    mouse_mode = NC_MOUSE_NONE;
 
-static void ctx_report_hit_region (CtxEvent *event,
-                       void     *data,
-                       void     *data2)
+static void _nc_noraw (void)
 {
-  const char *id = data;
-
-  fprintf (stderr, "hit region %s\n", id);
-  // XXX: NYI
+  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
+    nc_is_raw = 0;
 }
 
-void ctx_add_hit_region (Ctx *ctx, const char *id)
+void
+nc_at_exit (void)
 {
-  char *id_copy = strdup (id);
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
-  {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
-  }
-  
-  return ctx_listen_full (ctx, x, y, width, height, CTX_POINTER, ctx_report_hit_region, id_copy, NULL, 
(void*)free, NULL);
+  printf (TERMINAL_MOUSE_OFF);
+  printf (XTERM_ALTSCREEN_OFF);
+  _nc_noraw();
+  fprintf (stdout, "\e[?25h");
+  //if (ctx_native_events)
+  fprintf (stdout, "\e[?201l");
+  fprintf (stdout, "\e[?1049l");
 }
 
-typedef struct _CtxGrab CtxGrab;
-
-struct _CtxGrab
+static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
 {
-  CtxItem *item;
-  int      device_no;
-  int      timeout_id;
-  int      start_time;
-  float    x; // for tap and hold
-  float    y;
-  CtxEventType  type;
-};
+  static int prev_state = 0;
+  const char *ret = "mouse-motion";
+  float relx, rely;
+  signed char buf[3];
+  read (n->mouse_fd, buf, 3);
+  relx = buf[1];
+  rely = -buf[2];
 
-static void grab_free (Ctx *ctx, CtxGrab *grab)
-{
-  if (grab->timeout_id)
-  {
-    ctx_remove_idle (ctx, grab->timeout_id);
-    grab->timeout_id = 0;
-  }
-  _ctx_item_unref (grab->item);
-  free (grab);
-}
+  n->mouse_x += relx * 0.1;
+  n->mouse_y += rely * 0.1;
 
-static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
-{
-  ctx_list_remove (&ctx->events.grabs, grab);
-  grab_free (ctx, grab);
-}
+  if (n->mouse_x < 1) n->mouse_x = 1;
+  if (n->mouse_y < 1) n->mouse_y = 1;
+  if (n->mouse_x >= n->events.width)  n->mouse_x = n->events.width;
+  if (n->mouse_y >= n->events.height) n->mouse_y = n->events.height;
 
-static CtxGrab *device_add_grab (Ctx *ctx, int device_no, CtxItem *item, CtxEventType type)
-{
-  CtxGrab *grab = calloc (1, sizeof (CtxGrab));
-  grab->item = item;
-  grab->type = type;
-  _ctx_item_ref (item);
-  grab->device_no = device_no;
-  ctx_list_append (&ctx->events.grabs, grab);
-  return grab;
-}
+  if (x) *x = n->mouse_x;
+  if (y) *y = n->mouse_y;
 
-CtxList *device_get_grabs (Ctx *ctx, int device_no)
-{
-  CtxList *ret = NULL;
-  CtxList *l;
-  for (l = ctx->events.grabs; l; l = l->next)
-  {
-    CtxGrab *grab = l->data;
-    if (grab->device_no == device_no)
-      ctx_list_append (&ret, grab);
-  }
-  return ret;
-}
-
-static void _mrg_restore_path (Ctx *ctx, void *path)  //XXX
-{
-  //int i;
-  //cairo_path_data_t *data;
-  //cairo_new_path (cr);
-  //cairo_append_path (cr, path);
-}
-
-CtxList *_ctx_detect_list (Ctx *ctx, float x, float y, CtxEventType type)
-{
-  CtxList *a;
-  CtxList *ret = NULL;
-
-  if (type == CTX_KEY_DOWN ||
-      type == CTX_KEY_UP ||
-      type == CTX_MESSAGE ||
-      type == (CTX_KEY_DOWN|CTX_MESSAGE) ||
-      type == (CTX_KEY_DOWN|CTX_KEY_UP) ||
-      type == (CTX_KEY_DOWN|CTX_KEY_UP|CTX_MESSAGE))
-  {
-    for (a = ctx->events.items; a; a = a->next)
+  if ((prev_state & 1) != (buf[0] & 1))
     {
-      CtxItem *item = a->data;
-      if (item->types & type)
-      {
-        ctx_list_prepend (&ret, item);
-        return ret;
-      }
+      if (buf[0] & 1) ret = "mouse-press";
     }
-    return NULL;
-  }
+  else if (buf[0] & 1)
+    ret = "mouse-drag";
 
-  for (a = ctx->events.items; a; a = a->next)
-  {
-    CtxItem *item= a->data;
-  
-    float u, v;
-    u = x;
-    v = y;
-    ctx_matrix_apply_transform (&item->inv_matrix, &u, &v);
+  if ((prev_state & 2) != (buf[0] & 2))
+    {
+      if (buf[0] & 2) ret = "mouse2-press";
+    }
+  else if (buf[0] & 2)
+    ret = "mouse2-drag";
 
-    if (u >= item->x0 && v >= item->y0 &&
-        u <  item->x1 && v <  item->y1 && 
-        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
-        item->cursor)))
+  if ((prev_state & 4) != (buf[0] & 4))
     {
-      if (item->path)
-      {
-        _mrg_restore_path (ctx, item->path);
-        if (ctx_in_fill (ctx, u, v))
-        {
-          ctx_begin_path (ctx);
-          ctx_list_prepend (&ret, item);
-        }
-        ctx_begin_path (ctx);
-      }
-      else
-      {
-        ctx_list_prepend (&ret, item);
-      }
+      if (buf[0] & 4) ret = "mouse1-press";
     }
-  }
+  else if (buf[0] & 4)
+    ret = "mouse1-drag";
+
+  prev_state = buf[0];
   return ret;
 }
 
-CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
+static const char *mev_type = NULL;
+static int         mev_x = 0;
+static int         mev_y = 0;
+static int         mev_q = 0;
+
+static const char *mouse_get_event (Ctx  *n, int *x, int *y)
 {
-  CtxList *l = _ctx_detect_list (ctx, x, y, type);
-  if (l)
-  {
-    ctx_list_reverse (&l);
-    CtxItem *ret = l->data;
-    ctx_list_free (&l);
-    return ret;
-  }
-  return NULL;
+  if (!mev_q)
+    return NULL;
+  *x = mev_x;
+  *y = mev_y;
+  mev_q = 0;
+  return mev_type;
 }
 
-static int
-_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
+static int mouse_has_event (Ctx *n)
 {
-  static CtxEvent s_event;
-  CtxEvent transformed_event;
-  int i;
+  struct timeval tv;
+  int retval;
+
+  if (mouse_mode == NC_MOUSE_NONE)
+    return 0;
 
+  if (mev_q)
+    return 1;
 
-  if (!event)
-  {
-    event = &s_event;
-    event->type = type;
-    event->x = x;
-    event->y = y;
-  }
-  event->ctx = ctx;
-  transformed_event = *event;
-  transformed_event.device_x = event->x;
-  transformed_event.device_y = event->y;
+  if (n->mouse_fd == 0)
+    return 0;
+  return 0;
 
   {
-    float tx, ty;
-    tx = transformed_event.x;
-    ty = transformed_event.y;
-    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-    transformed_event.x = tx;
-    transformed_event.y = ty;
+    fd_set rfds;
+    FD_ZERO (&rfds);
+    FD_SET(n->mouse_fd, &rfds);
+    tv.tv_sec = 0; tv.tv_usec = 0;
+    retval = select (n->mouse_fd+1, &rfds, NULL, NULL, &tv);
+  }
 
-    if ((type & CTX_DRAG_PRESS) ||
-        (type & CTX_DRAG_MOTION) ||
-        (type & CTX_MOTION))   /* probably a worthwhile check for the performance 
-                                  benefit
-                                */
+  if (retval != 0)
     {
-      tx = transformed_event.start_x;
-      ty = transformed_event.start_y;
-      ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-      transformed_event.start_x = tx;
-      transformed_event.start_y = ty;
-    }
+      int nx = 0, ny = 0;
+      const char *type = mouse_get_event_int (n, &nx, &ny);
 
+      if ((mouse_mode < NC_MOUSE_DRAG && mev_type && !strcmp (mev_type, "drag")) ||
+          (mouse_mode < NC_MOUSE_ALL && mev_type && !strcmp (mev_type, "motion")))
+        {
+          mev_q = 0;
+          return mouse_has_event (n);
+        }
 
-    tx = transformed_event.delta_x;
-    ty = transformed_event.delta_y;
-    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-    transformed_event.delta_x = tx;
-    transformed_event.delta_y = ty;
-  }
+      if ((mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse-motion")) ||
+         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse1-drag")) ||
+         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse2-drag")))
+        {
+          if (nx == mev_x && ny == mev_y)
+          {
+            mev_q = 0;
+            return mouse_has_event (n);
+          }
+        }
+      mev_x = nx;
+      mev_y = ny;
+      mev_type = type;
+      mev_q = 1;
+    }
+  return retval != 0;
+}
 
-  transformed_event.state = ctx->events.modifier_state;
-  transformed_event.type = type;
 
-  for (i = item->cb_count-1; i >= 0; i--)
-  {
-    if (item->cb[i].types & type)
+static int _nc_raw (void)
+{
+  struct termios raw;
+  if (!isatty (STDIN_FILENO))
+    return -1;
+  if (!atexit_registered)
     {
-      item->cb[i].cb (&transformed_event, item->cb[i].data1, item->cb[i].data2);
-      event->stop_propagate = transformed_event.stop_propagate; /* copy back the response */
-      if (event->stop_propagate)
-        return event->stop_propagate;
+      atexit (nc_at_exit);
+      atexit_registered = 1;
     }
-  }
+  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+    return -1;
+  raw = orig_attr;  /* modify the original mode */
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return -1;
+  nc_is_raw = 1;
+  tcdrain(STDIN_FILENO);
+  tcflush(STDIN_FILENO, 1);
   return 0;
 }
-#endif
-
-#if CTX_EVENTS
-
-#include <stdatomic.h>
 
-int ctx_native_events = 0;
-#if CTX_SDL
-int ctx_sdl_events = 0;
-int ctx_sdl_consume_events (Ctx *ctx);
-#endif
+static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
+{
+  int i;
+  int matches = 0;
 
-#if CTX_FB
-int ctx_fb_events = 0;
-int ctx_fb_consume_events (Ctx *ctx);
-#endif
+  if (!strncmp (buf, "\033[M", MIN(length,3)))
+    {
+      if (length >= 6)
+        return 9001;
+      return 2342;
+    }
+  for (i = 0; keycodes[i].nick; i++)
+    if (!strncmp (buf, keycodes[i].sequence, length))
+      {
+        matches ++;
+        if ((int)strlen (keycodes[i].sequence) == length && ret)
+          {
+            *ret = &keycodes[i];
+            return 1;
+          }
+      }
+  if (matches != 1 && ret)
+    *ret = NULL;
+  return matches==1?2:matches;
+}
 
+static void nc_resize_term (int  dummy)
+{
+  size_changed = 1;
+}
 
-int ctx_nct_consume_events (Ctx *ctx);
-int ctx_ctx_consume_events (Ctx *ctx);
-#if CTX_SDL
-#endif
-
-void ctx_consume_events (Ctx *ctx)
+int ctx_has_event (Ctx  *n, int delay_ms)
 {
-#if CTX_SDL
-  if (ctx_sdl_events)
-    ctx_sdl_consume_events (ctx);
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-    ctx_fb_consume_events (ctx);
-  else
-#endif
-  if (ctx_native_events)
-    ctx_ctx_consume_events (ctx);
-  else
-    ctx_nct_consume_events (ctx);
+  struct timeval tv;
+  int retval;
+  fd_set rfds;
+
+  if (size_changed)
+    return 1;
+  FD_ZERO (&rfds);
+  FD_SET (STDIN_FILENO, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = delay_ms * 1000; 
+  retval = select (1, &rfds, NULL, NULL, &tv);
+  if (size_changed)
+    return 1;
+  return retval == 1 && retval != -1;
 }
 
-CtxEvent *ctx_get_event (Ctx *ctx)
+const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
 {
-  static CtxEvent event_copy;
-  _ctx_idle_iteration (ctx);
-  if (!ctx->events.ctx_get_event_enabled)
-    ctx->events.ctx_get_event_enabled = 1;
+  unsigned char buf[20];
+  int length;
 
-  ctx_consume_events (ctx);
 
-  if (ctx->events.events)
+  if (x) *x = -1;
+  if (y) *y = -1;
+
+  if (!signal_installed)
     {
-      event_copy = *((CtxEvent*)(ctx->events.events->data));
-      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
-      return &event_copy;
+      _nc_raw ();
+      signal_installed = 1;
+      signal (SIGWINCH, nc_resize_term);
     }
-  return NULL;
-}
+  if (mouse_mode) // XXX too often to do it all the time!
+    printf("%s", mouse_modes[mouse_mode]);
 
-static int
-_ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y)
-{
-  CtxList *l;
-  event->stop_propagate = 0;
-  for (l = items; l; l = l->next)
   {
-    _ctx_emit_cb_item (ctx, l->data, event, type, x, y);
-    if (event->stop_propagate)
-      return event->stop_propagate;
+    int elapsed = 0;
+    int got_event = 0;
+
+    do {
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      got_event = mouse_has_event (n);
+      if (!got_event)
+        got_event = ctx_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      /* only do this if the client has asked for idle events,
+       * and perhaps programmed the ms timer?
+       */
+      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
+      if (!got_event && timeoutms && elapsed >= timeoutms)
+        return "idle";
+    } while (!got_event);
   }
-  return 0;
+
+  if (mouse_has_event (n))
+    return mouse_get_event (n, x, y);
+
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const NcKeyCode *match = NULL;
+
+        /* special case ESC, so that we can use it alone in keybindings */
+        if (length == 0 && buf[0] == 27)
+          {
+            struct timeval tv;
+            fd_set rfds;
+            FD_ZERO (&rfds);
+            FD_SET (STDIN_FILENO, &rfds);
+            tv.tv_sec = 0;
+            tv.tv_usec = 1000 * DELAY_MS;
+            if (select (1, &rfds, NULL, NULL, &tv) == 0)
+              return "esc";
+          }
+
+        switch (match_keycode ((const char*)buf, length + 1, &match))
+          {
+            case 1: /* unique match */
+              if (!match)
+                return NULL;
+              if (!strcmp(match->nick, "ok"))
+              {
+                ctx_frame_ack = 1;
+                return NULL;
+              }
+              return match->nick;
+              break;
+            case 9001: /* mouse event */
+              if (x) *x = ((unsigned char)buf[4]-32)*1.0;
+              if (y) *y = ((unsigned char)buf[5]-32)*1.0;
+              switch (buf[3])
+                {
+                        /* XXX : todo reduce this to less string constants */
+                  case 32:  return "mouse-press";
+                  case 33:  return "mouse1-press";
+                  case 34:  return "mouse2-press";
+                  case 40:  return "alt-mouse-press";
+                  case 41:  return "alt-mouse1-press";
+                  case 42:  return "alt-mouse2-press";
+                  case 48:  return "control-mouse-press";
+                  case 49:  return "control-mouse1-press";
+                  case 50:  return "control-mouse2-press";
+                  case 56:  return "alt-control-mouse-press";
+                  case 57:  return "alt-control-mouse1-press";
+                  case 58:  return "alt-control-mouse2-press";
+                  case 64:  return "mouse-drag";
+                  case 65:  return "mouse1-drag";
+                  case 66:  return "mouse2-drag";
+                  case 71:  return "mouse-motion"; /* shift+motion */
+                  case 72:  return "alt-mouse-drag";
+                  case 73:  return "alt-mouse1-drag";
+                  case 74:  return "alt-mouse2-drag";
+                  case 75:  return "mouse-motion"; /* alt+motion */
+                  case 80:  return "control-mouse-drag";
+                  case 81:  return "control-mouse1-drag";
+                  case 82:  return "control-mouse2-drag";
+                  case 83:  return "mouse-motion"; /* ctrl+motion */
+                  case 91:  return "mouse-motion"; /* ctrl+alt+motion */
+                  case 95:  return "mouse-motion"; /* ctrl+alt+shift+motion */
+                  case 96:  return "scroll-up";
+                  case 97:  return "scroll-down";
+                  case 100: return "shift-scroll-up";
+                  case 101: return "shift-scroll-down";
+                  case 104: return "alt-scroll-up";
+                  case 105: return "alt-scroll-down";
+                  case 112: return "control-scroll-up";
+                  case 113: return "control-scroll-down";
+                  case 116: return "control-shift-scroll-up";
+                  case 117: return "control-shift-scroll-down";
+                  case 35: /* (or release) */
+                  case 51: /* (or ctrl-release) */
+                  case 43: /* (or alt-release) */
+                  case 67: return "mouse-motion";
+                           /* have a separate mouse-drag ? */
+                  default: {
+                             static char rbuf[100];
+                             sprintf (rbuf, "mouse (unhandled state: %i)", buf[3]);
+                             return rbuf;
+                           }
+                }
+            case 0: /* no matches, bail*/
+              { 
+                static char ret[256];
+                if (length == 0 && ctx_utf8_len (buf[0])>1) /* single unicode
+                                                               char */
+                  {
+                    int n_read = 
+                    read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
+                    if (n_read)
+                    {
+                      buf[ctx_utf8_len(buf[0])]=0;
+                      strcpy (ret, (const char*)buf);
+                    }
+                    return ret;
+                  }
+                if (length == 0) /* ascii */
+                  {
+                    buf[1]=0;
+                    strcpy (ret, (const char*)buf);
+                    return ret;
+                  }
+                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
+                  length>=0? buf[0]: 0, length>=0? buf[0]>31?buf[0]:'?': ' ', 
+                  length>=1? buf[1]: 0, length>=1? buf[1]>31?buf[1]:'?': ' ', 
+                  length>=2? buf[2]: 0, length>=2? buf[2]>31?buf[2]:'?': ' ', 
+                  length>=3? buf[3]: 0, length>=3? buf[3]>31?buf[3]:'?': ' ',
+                  length>=4? buf[4]: 0, length>=4? buf[4]>31?buf[4]:'?': ' ',
+                  length>=5? buf[5]: 0, length>=5? buf[5]>31?buf[5]:'?': ' ',
+                  length>=6? buf[6]: 0, length>=6? buf[6]>31?buf[6]:'?': ' ');
+                return ret;
+              }
+              return NULL;
+            default: /* continue */
+              break;
+          }
+      }
+    else
+      return "key read eek";
+  return "fail";
 }
 
-/*
- * update what is the currently hovered item and returns it.. and the list of hits
- * a well.
- *
- */
-static CtxItem *_ctx_update_item (Ctx *ctx, int device_no, float x, float y, CtxEventType type, CtxList 
**hitlist)
+int ctx_nct_consume_events (Ctx *ctx)
 {
-  CtxItem *current = NULL;
-
-  CtxList *l = _ctx_detect_list (ctx, x, y, type);
-  if (l)
-  {
-    ctx_list_reverse (&l);
-    current = l->data;
-  }
-  if (hitlist)
-    *hitlist = l;
-  else
-    ctx_list_free (&l);
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
 
-  if (ctx->events.prev[device_no] == NULL || current == NULL || (current->path_hash != 
ctx->events.prev[device_no]->path_hash))
   {
-// enter/leave should snapshot chain to root
-// and compare with previous snapshotted chain to root
-// and emit/enter/leave as appropriate..
-//
-// leave might be registered for emission on enter..emission?
-
+    float x, y;
+    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
 
-    //int focus_radius = 2;
-    if (current)
-      _ctx_item_ref (current);
+    x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
+    y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
 
-    if (ctx->events.prev[device_no])
+    if (!strcmp (event, "mouse-press"))
     {
-      {
-#if 0
-        CtxRectangle rect = {floor(ctx->events.prev[device_no]->x0-focus_radius),
-                             floor(ctx->events.prev[device_no]->y0-focus_radius),
-                             ceil(ctx->events.prev[device_no]->x1)-floor(ctx->events.prev[device_no]->x0) + 
focus_radius * 2,
-                             ceil(ctx->events.prev[device_no]->y1)-floor(ctx->events.prev[device_no]->y0) + 
focus_radius * 2};
-        mrg_queue_draw (mrg, &rect);
-#endif 
+      ctx_pointer_press (ctx, x, y, 0, 0);
+      ctxctx->was_down = 1;
+    } else if (!strcmp (event, "mouse-release"))
+    {
+      ctx_pointer_release (ctx, x, y, 0, 0);
+      ctxctx->was_down = 0;
+    } else if (!strcmp (event, "mouse-motion"))
+    {
+      //nct_set_cursor_pos (backend->term, ix, iy);
+      //nct_flush (backend->term);
+      if (ctxctx->was_down)
+      {
+        ctx_pointer_release (ctx, x, y, 0, 0);
+        ctxctx->was_down = 0;
       }
+      ctx_pointer_motion (ctx, x, y, 0, 0);
+    } else if (!strcmp (event, "mouse-drag"))
+    {
+      ctx_pointer_motion (ctx, x, y, 0, 0);
+    } else if (!strcmp (event, "size-changed"))
+    {
+#if 0
+      int width = nct_sys_terminal_width ();
+      int height = nct_sys_terminal_height ();
+      nct_set_size (backend->term, width, height);
+      width *= CPX;
+      height *= CPX;
+      free (mrg->glyphs);
+      free (mrg->styles);
+      free (backend->nct_pixels);
+      backend->nct_pixels = calloc (width * height * 4, 1);
+      mrg->glyphs = calloc ((width/CPX) * (height/CPX) * 4, 1);
+      mrg->styles = calloc ((width/CPX) * (height/CPX) * 1, 1);
+      mrg_set_size (mrg, width, height);
+      mrg_queue_draw (mrg, NULL);
+#endif
 
-      _ctx_emit_cb_item (ctx, ctx->events.prev[device_no], NULL, CTX_LEAVE, x, y);
-      _ctx_item_unref (ctx->events.prev[device_no]);
-      ctx->events.prev[device_no] = NULL;
     }
-    if (current)
+    else
     {
-#if 0
+      if (!strcmp (event, "esc"))
+        ctx_key_press (ctx, 0, "escape", 0);
+      else if (!strcmp (event, "space"))
+        ctx_key_press (ctx, 0, "space", 0);
+      else if (!strcmp (event, "enter"))
+        ctx_key_press (ctx, 0, "\n", 0);
+      else if (!strcmp (event, "return"))
+        ctx_key_press (ctx, 0, "return", 0);
+      else if (!strcmp (event, "idle"))
       {
-        CtxRectangle rect = {floor(current->x0-focus_radius),
-                             floor(current->y0-focus_radius),
-                             ceil(current->x1)-floor(current->x0) + focus_radius * 2,
-                             ceil(current->y1)-floor(current->y0) + focus_radius * 2};
-        mrg_queue_draw (mrg, &rect);
       }
-#endif
-      _ctx_emit_cb_item (ctx, current, NULL, CTX_ENTER, x, y);
-      ctx->events.prev[device_no] = current;
+      else
+      ctx_key_press (ctx, 0, event, 0);
     }
   }
-  current = _ctx_detect (ctx, x, y, type);
-  //fprintf (stderr, "%p\n", current);
-  return current;
+
+  return 1;
 }
 
-static int tap_and_hold_fire (Ctx *ctx, void *data)
+const char *ctx_native_get_event (Ctx *n, int timeoutms)
 {
-  CtxGrab *grab = data;
-  CtxList *list = NULL;
-  ctx_list_prepend (&list, grab->item);
-  CtxEvent event = {0, };
-
-  event.ctx = ctx;
-  event.time = ctx_ms (ctx);
-
-  event.device_x = 
-  event.x = ctx->events.pointer_x[grab->device_no];
-  event.device_y = 
-  event.y = ctx->events.pointer_y[grab->device_no];
-
-  // XXX: x and y coordinates
-  int ret = _ctx_emit_cb (ctx, list, &event, CTX_TAP_AND_HOLD,
-      ctx->events.pointer_x[grab->device_no], ctx->events.pointer_y[grab->device_no]);
+  static unsigned char buf[256];
+  int length;
 
-  ctx_list_free (&list);
+  if (!signal_installed)
+    {
+      _nc_raw ();
+      signal_installed = 1;
+      signal (SIGWINCH, nc_resize_term);
+    }
+//if (mouse_mode) // XXX too often to do it all the time!
+//  printf("%s", mouse_modes[mouse_mode]);
 
-  grab->timeout_id = 0;
+    int got_event = 0;
+  {
+    int elapsed = 0;
 
-  return 0;
+    do {
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      got_event = ctx_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      /* only do this if the client has asked for idle events,
+       * and perhaps programmed the ms timer?
+       */
+      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
+      if (!got_event && timeoutms && elapsed >= timeoutms)
+      {
+        return "idle";
+      }
+    } while (!got_event);
+  }
 
-  return ret;
+  for (length = 0; got_event && length < 200; length ++)
+  {
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+         buf[length+1] = 0;
+         if (!strcmp ((char*)buf, "\e[0n"))
+         {
+           ctx_frame_ack = 1;
+           return NULL;
+         }
+         else if (buf[length]=='\n')
+         {
+           buf[length]=0;
+           return (const char*)buf;
+         }
+      }
+      got_event = ctx_has_event (n, 5);
+    }
+  return NULL;
 }
 
-int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
-                      char *string)
+const char *ctx_key_get_label (Ctx  *n, const char *nick)
 {
-  CtxList *l;
-  CtxList *hitlist = NULL;
+  int j;
+  int found = -1;
+  for (j = 0; keycodes[j].nick; j++)
+    if (found == -1 && !strcmp (keycodes[j].nick, nick))
+      return keycodes[j].label;
+  return NULL;
+}
 
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
-  if (device_no <= 3)
+void _ctx_mouse (Ctx *term, int mode)
+{
+  //if (term->is_st && mode > 1)
+  //  mode = 1;
+  if (mode != mouse_mode)
   {
-    ctx->events.pointer_x[0] = x;
-    ctx->events.pointer_y[0] = y;
+    printf ("%s", mouse_modes[mode]);
+    fflush (stdout);
   }
+  mouse_mode = mode;
+}
 
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &ctx->events.drag_event[device_no];
-
-  if (time == 0)
-    time = ctx_ms (ctx);
 
-  event->ctx = ctx;
-  event->x = x;
-  event->y = y;
+#endif
 
-  event->delta_x = event->delta_y = 0;
+#include <sys/time.h>
 
-  event->device_no = device_no;
-  event->string    = string;
-  event->time      = time;
-  event->stop_propagate = 0;
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_DROP, &hitlist);
+#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
 
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
-    _ctx_emit_cb_item (ctx, item, event, CTX_DROP, x, y);
+#if CTX_EVENTS
+#include <threads.h>
+static struct timeval start_time;
 
-    if (event->stop_propagate)
-    {
-      ctx_list_free (&hitlist);
-      return 0;
-    }
-  }
+static void
+_ctx_init_ticks (void)
+{
+  static int done = 0;
+  if (done)
+    return;
+  done = 1;
+  gettimeofday (&start_time, NULL);
+}
 
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
+static inline unsigned long
+_ctx_ticks (void)
+{
+  struct timeval measure_time;
+  gettimeofday (&measure_time, NULL);
+  return usecs (measure_time) - usecs (start_time);
+}
 
-  return 0;
+unsigned long
+ctx_ticks (void)
+{
+  _ctx_init_ticks ();
+  return _ctx_ticks ();
 }
 
-int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+uint32_t ctx_ms (Ctx *ctx)
 {
-  CtxEvents *events = &ctx->events;
-  CtxList *hitlist = NULL;
-  events->pointer_x[device_no] = x;
-  events->pointer_y[device_no] = y;
-  if (device_no <= 3)
-  {
-    events->pointer_x[0] = x;
-    events->pointer_y[0] = y;
-  }
+  return _ctx_ticks () / 1000;
+}
 
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &events->drag_event[device_no];
 
-  if (time == 0)
-    time = ctx_ms (ctx);
+typedef enum _CtxFlags CtxFlags;
 
-  event->x = event->start_x = event->prev_x = x;
-  event->y = event->start_y = event->prev_y = y;
+enum _CtxFlags {
+   CTX_FLAG_DIRECT = (1<<0),
+};
 
-  event->delta_x = event->delta_y = 0;
 
-  event->device_no = device_no;
-  event->time      = time;
-  event->stop_propagate = 0;
+int _ctx_max_threads = 1;
+int _ctx_enable_hash_cache = 1;
 
-  if (events->pointer_down[device_no] == 1)
-  {
-    fprintf (stderr, "events thought device %i was already down\n", device_no);
-  }
-  /* doing just one of these two should be enough? */
-  events->pointer_down[device_no] = 1;
-  switch (device_no)
-  {
-    case 1:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON1;
-      break;
-    case 2:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON2;
-      break;
-    case 3:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON3;
-      break;
-    default:
-      break;
-  }
-
-  CtxGrab *grab = NULL;
-  CtxList *l;
+static mtx_t _ctx_texture_mtx;
 
-  _ctx_update_item (ctx, device_no, x, y, 
-      CTX_PRESS | CTX_DRAG_PRESS | CTX_TAP | CTX_TAP_AND_HOLD, &hitlist);
+void _ctx_texture_lock (void)
+{
+  mtx_lock (&_ctx_texture_mtx);
+}
 
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
-    if (item &&
-        ((item->types & CTX_DRAG)||
-         (item->types & CTX_TAP) ||
-         (item->types & CTX_TAP_AND_HOLD)))
-    {
-      grab = device_add_grab (ctx, device_no, item, item->types);
-      grab->start_time = time;
+void _ctx_texture_unlock (void)
+{
+  mtx_unlock (&_ctx_texture_mtx);
+}
 
-      if (item->types & CTX_TAP_AND_HOLD)
-      {
-         grab->timeout_id = ctx_add_timeout (ctx, events->tap_delay_hold, tap_and_hold_fire, grab);
-      }
-    }
-    _ctx_emit_cb_item (ctx, item, event, CTX_PRESS, x, y);
-    if (!event->stop_propagate)
-      _ctx_emit_cb_item (ctx, item, event, CTX_DRAG_PRESS, x, y);
 
-    if (event->stop_propagate)
+void
+ctx_init (int *argc, char ***argv)
+{
+#if 0
+  if (!getenv ("CTX_VERSION"))
+  {
+    int i;
+    char *new_argv[*argc+3];
+    new_argv[0] = "ctx";
+    for (i = 0; i < *argc; i++)
     {
-      ctx_list_free (&hitlist);
-      return 0;
+      new_argv[i+1] = *argv[i];
     }
+    new_argv[i+1] = NULL;
+    execvp (new_argv[0], new_argv);
+    // if this fails .. we continue normal startup
+    // and end up in self-hosted braille
   }
-
-  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-  return 0;
+#endif
 }
 
-void _ctx_resized (Ctx *ctx, int width, int height, long time)
+int ctx_count (Ctx *ctx)
 {
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_DOWN);
-  CtxEvent event = {0, };
+  return ctx->drawlist.count;
+}
 
-  if (!time)
-    time = ctx_ms (ctx);
+
+static int is_in_ctx (void)
+{
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[?200$p");
+  //tcflush(STDIN_FILENO, 1);
+  tcdrain(STDIN_FILENO);
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
   
-  event.ctx = ctx;
-  event.time = time;
-  event.string = "resize-event"; /* gets delivered to clients as a key_down event, maybe message shouldbe 
used instead?
-   */
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
 
-  if (item)
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
   {
-    event.stop_propagate = 0;
-    _ctx_emit_cb_item (ctx, item, &event, CTX_KEY_DOWN, 0, 0);
+    length += read (STDIN_FILENO, &buf[length], 1);
   }
-
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
+  {
+    return 0;
+  }
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi &&  semi[1] == '2')
+  {
+    return 1;
+  }
+  return 0;
 }
 
-int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+Ctx *ctx_new_ui (int width, int height)
 {
-  CtxEvents *events = &ctx->events;
-  if (time == 0)
-    time = ctx_ms (ctx);
-
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &events->drag_event[device_no];
+  if (getenv ("CTX_HASH_CACHE"))
+  {
+    const char * val = getenv ("CTX_HASH_CACHE");
+    if (!strcmp (val, "0"))
+      _ctx_enable_hash_cache = 0;
+    if (!strcmp (val, "off"))
+      _ctx_enable_hash_cache = 0;
+  }
 
-  event->time = time;
-  event->x = x;
-  event->ctx = ctx;
-  event->y = y;
-  event->device_no = device_no;
-  event->stop_propagate = 0;
 
-  switch (device_no)
+  if (getenv ("CTX_THREADS"))
   {
-    case 1:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON1)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON1;
-      break;
-    case 2:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON2)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON2;
-      break;
-    case 3:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON3)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON3;
-      break;
-    default:
-      break;
+    int val = atoi (getenv ("CTX_THREADS"));
+    _ctx_max_threads = val;
+  }
+  else
+  {
+    _ctx_max_threads = 2;
+#ifdef _SC_NPROCESSORS_ONLN
+    _ctx_max_threads = sysconf (_SC_NPROCESSORS_ONLN) / 2;
+#endif
   }
+  
+#if CTX_EVENTS
+  mtx_init (&_ctx_texture_mtx, mtx_plain);
+#endif
 
-  //events_queue_draw (mrg, NULL); /* in case of style change */
+  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
+  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
 
-  if (events->pointer_down[device_no] == 0)
+  //fprintf (stderr, "ctx using %i threads\n", _ctx_max_threads);
+  const char *backend = getenv ("CTX_BACKEND");
+
+  if (backend && !strcmp (backend, ""))
+    backend = NULL;
+  if (backend && !strcmp (backend, "auto"))
+    backend = NULL;
+  if (backend && !strcmp (backend, "list"))
   {
-    fprintf (stderr, "device %i already up\n", device_no);
+    fprintf (stderr, "possible values for CTX_BACKEND:\n");
+    fprintf (stderr, " ctx");
+#if CTX_SDL
+    fprintf (stderr, " SDL");
+#endif
+#if CTX_FB
+    fprintf (stderr, " fb");
+    fprintf (stderr, " drm");
+#endif
+    fprintf (stderr, " term");
+    fprintf (stderr, " termimg");
+    fprintf (stderr, "\n");
+    exit (-1);
   }
-  events->pointer_down[device_no] = 0;
 
-  events->pointer_x[device_no] = x;
-  events->pointer_y[device_no] = y;
-  if (device_no <= 3)
+  Ctx *ret = NULL;
+
+  /* we do the query on auto but not on directly set ctx
+   *
+   */
+  if ((backend && !strcmp(backend, "ctx")) ||
+      (backend == NULL && is_in_ctx ()))
   {
-    events->pointer_x[0] = x;
-    events->pointer_y[0] = y;
+    if (!backend || !strcmp (backend, "ctx"))
+    {
+      // full blown ctx protocol - in terminal or standalone
+      ret = ctx_new_ctx (width, height);
+    }
   }
-  CtxList *hitlist = NULL;
-  CtxList *grablist = NULL , *g= NULL;
-  CtxGrab *grab;
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_RELEASE | CTX_DRAG_RELEASE, &hitlist);
-  grablist = device_get_grabs (ctx, device_no);
+#if CTX_SDL
+  if (!ret && getenv ("DISPLAY"))
+  {
+    if ((backend==NULL) || (!strcmp (backend, "SDL")))
+      ret = ctx_new_sdl (width, height);
+  }
+#endif
 
-  for (g = grablist; g; g = g->next)
+#if CTX_FB
+  if (!ret && !getenv ("DISPLAY"))
   {
-    grab = g->data;
+    if ((backend==NULL) || (!strcmp (backend, "drm")))
+    ret = ctx_new_fb (width, height, 1);
 
-    if (!event->stop_propagate)
+    if (!ret)
     {
-      if (grab->item->types & CTX_TAP)
-      {
-        long delay = time - grab->start_time;
-
-        if (delay > events->tap_delay_min &&
-            delay < events->tap_delay_max &&
-            (
-              (event->start_x - x) * (event->start_x - x) +
-              (event->start_y - y) * (event->start_y - y)) < ctx_pow2(events->tap_hysteresis)
-            )
-        {
-          _ctx_emit_cb_item (ctx, grab->item, event, CTX_TAP, x, y);
-        }
-      }
-
-      if (!event->stop_propagate && grab->item->types & CTX_DRAG_RELEASE)
-      {
-        _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_RELEASE, x, y);
-      }
+      if ((backend==NULL) || (!strcmp (backend, "fb")))
+        ret = ctx_new_fb (width, height, 0);
     }
-
-    device_remove_grab (ctx, grab);
   }
+#endif
 
-  if (hitlist)
+#if CTX_RASTERIZER
+  // braille in terminal
+  if (!ret)
   {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_RELEASE, x, y);
-    ctx_list_free (&hitlist);
+    if ((backend==NULL) || (!strcmp (backend, "term")))
+    ret = ctx_new_term (width, height);
   }
-  ctx_list_free (&grablist);
-  return 0;
+  if (!ret)
+  {
+    if ((backend==NULL) || (!strcmp (backend, "termimg")))
+    ret = ctx_new_termimg (width, height);
+  }
+#endif
+  if (!ret)
+  {
+    fprintf (stderr, "no interactive ctx backend\n");
+    exit (2);
+  }
+  ctx_get_event (ret); // enables events
+  return ret;
 }
-
-/*  for multi-touch, we use a list of active grabs - thus a grab corresponds to
- *  a device id. even during drag-grabs events propagate; to stop that stop
- *  propagation.
- */
-int ctx_pointer_motion (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+#else
+void _ctx_texture_unlock (void)
 {
-  CtxList *hitlist = NULL;
-  CtxList *grablist = NULL, *g;
-  CtxGrab *grab;
-
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &ctx->events.drag_event[device_no];
-
-  if (time == 0)
-    time = ctx_ms (ctx);
+}
+void _ctx_texture_lock (void)
+{
+}
 
-  event->ctx       = ctx;
-  event->x         = x;
-  event->y         = y;
-  event->time      = time;
-  event->device_no = device_no;
-  event->stop_propagate = 0;
-  
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
+#endif
+void _ctx_resized (Ctx *ctx, int width, int height, long time);
 
-  if (device_no <= 3)
+void ctx_set_size (Ctx *ctx, int width, int height)
+{
+#if CTX_EVENTS
+  if (ctx->events.width != width || ctx->events.height != height)
   {
-    ctx->events.pointer_x[0] = x;
-    ctx->events.pointer_y[0] = y;
+    ctx->events.width = width;
+    ctx->events.height = height;
+    _ctx_resized (ctx, width, height, 0);
   }
+#endif
+}
 
-  grablist = device_get_grabs (ctx, device_no);
-  _ctx_update_item (ctx, device_no, x, y, CTX_MOTION, &hitlist);
+#if CTX_EVENTS
+typedef struct CtxIdleCb {
+  int (*cb) (Ctx *ctx, void *idle_data);
+  void *idle_data;
 
-  {
-    CtxItem  *cursor_item = _ctx_detect (ctx, x, y, CTX_SET_CURSOR);
-    if (cursor_item)
-    {
-      ctx_set_cursor (ctx, cursor_item->cursor);
-    }
-    else
-    {
-      ctx_set_cursor (ctx, CTX_CURSOR_ARROW);
-    }
-    CtxItem  *hovered_item = _ctx_detect (ctx, x, y, CTX_ANY);
-    static CtxItem *prev_hovered_item = NULL;
-    if (prev_hovered_item != hovered_item)
-    {
-      ctx_set_dirty (ctx, 1);
-    }
-    prev_hovered_item = hovered_item;
-  }
+  void (*destroy_notify)(void *destroy_data);
+  void *destroy_data;
 
-  event->delta_x = x - event->prev_x;
-  event->delta_y = y - event->prev_y;
-  event->prev_x  = x;
-  event->prev_y  = y;
+  int   ticks_full;
+  int   ticks_remaining;
+  int   is_idle;
+  int   id;
+} CtxIdleCb;
 
-  CtxList *remove_grabs = NULL;
+void _ctx_events_init (Ctx *ctx)
+{
+  CtxEvents *events = &ctx->events;
+  _ctx_init_ticks ();
+  events->tap_delay_min  = 40;
+  events->tap_delay_max  = 800;
+  events->tap_delay_max  = 8000000; /* quick reflexes needed making it hard for some is an argument against 
very short values  */
 
-  for (g = grablist; g; g = g->next)
+  events->tap_delay_hold = 1000;
+  events->tap_hysteresis = 32;  /* XXX: should be ppi dependent */
+}
+
+
+void _ctx_idle_iteration (Ctx *ctx)
+{
+  static unsigned long prev_ticks = 0;
+  CtxList *l;
+  CtxList *to_remove = NULL;
+  unsigned long ticks = _ctx_ticks ();
+  unsigned long tick_delta = (prev_ticks == 0) ? 0 : ticks - prev_ticks;
+  prev_ticks = ticks;
+
+  if (!ctx->events.idles)
   {
-    grab = g->data;
+    return;
+  }
+  for (l = ctx->events.idles; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
 
-    if ((grab->type & CTX_TAP) ||
-        (grab->type & CTX_TAP_AND_HOLD))
-    {
-      if (
-          (
-            (event->start_x - x) * (event->start_x - x) +
-            (event->start_y - y) * (event->start_y - y)) >
-              ctx_pow2(ctx->events.tap_hysteresis)
-         )
-      {
-        //fprintf (stderr, "-");
-        ctx_list_prepend (&remove_grabs, grab);
-      }
-      else
-      {
-        //fprintf (stderr, ":");
-      }
-    }
+    if (item->ticks_remaining >= 0)
+      item->ticks_remaining -= tick_delta;
 
-    if (grab->type & CTX_DRAG_MOTION)
+    if (item->ticks_remaining < 0)
     {
-      _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_MOTION, x, y);
-      if (event->stop_propagate)
-        break;
+      if (item->cb (ctx, item->idle_data) == 0)
+        ctx_list_prepend (&to_remove, item);
+      else
+        item->ticks_remaining = item->ticks_full;
     }
   }
-  if (remove_grabs)
+  for (l = to_remove; l; l = l->next)
   {
-    for (g = remove_grabs; g; g = g->next)
-      device_remove_grab (ctx, g->data);
-    ctx_list_free (&remove_grabs);
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
   }
-  if (hitlist)
+}
+
+
+void ctx_add_key_binding_full (Ctx *ctx,
+                           const char *key,
+                           const char *action,
+                           const char *label,
+                           CtxCb       cb,
+                           void       *cb_data,
+                           CtxDestroyNotify destroy_notify,
+                           void       *destroy_data)
+{
+  CtxEvents *events = &ctx->events;
+  if (events->n_bindings +1 >= CTX_MAX_KEYBINDINGS)
   {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_MOTION, x, y);
-    ctx_list_free (&hitlist);
+    fprintf (stderr, "warning: binding overflow\n");
+    return;
   }
-  ctx_list_free (&grablist);
-  return 0;
+  events->bindings[events->n_bindings].nick = strdup (key);
+  strcpy (events->bindings[events->n_bindings].nick, key);
+
+  if (action)
+    events->bindings[events->n_bindings].command = action ? strdup (action) : NULL;
+  if (label)
+    events->bindings[events->n_bindings].label = label ? strdup (label) : NULL;
+  events->bindings[events->n_bindings].cb = cb;
+  events->bindings[events->n_bindings].cb_data = cb_data;
+  events->bindings[events->n_bindings].destroy_notify = destroy_notify;
+  events->bindings[events->n_bindings].destroy_data = destroy_data;
+  events->n_bindings++;
 }
 
-void ctx_incoming_message (Ctx *ctx, const char *message, long time)
+void ctx_add_key_binding (Ctx *ctx,
+                          const char *key,
+                          const char *action,
+                          const char *label,
+                          CtxCb       cb,
+                          void       *cb_data)
 {
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_MESSAGE);
-  CtxEvent event = {0, };
-
-  if (!time)
-    time = ctx_ms (ctx);
+  ctx_add_key_binding_full (ctx, key, action, label, cb, cb_data, NULL, NULL);
+}
 
-  if (item)
+void ctx_clear_bindings (Ctx *ctx)
+{
+  CtxEvents *events = &ctx->events;
+  int i;
+  for (i = 0; events->bindings[i].nick; i ++)
   {
-    int i;
-    event.ctx = ctx;
-    event.type = CTX_MESSAGE;
-    event.time = time;
-    event.string = message;
+    if (events->bindings[i].destroy_notify)
+      events->bindings[i].destroy_notify (events->bindings[i].destroy_data);
+    free (events->bindings[i].nick);
+    if (events->bindings[i].command)
+      free (events->bindings[i].command);
+    if (events->bindings[i].label)
+      free (events->bindings[i].label);
+  }
+  memset (&events->bindings, 0, sizeof (events->bindings));
+  events->n_bindings = 0;
+}
 
-    fprintf (stderr, "{%s|\n", message);
+static void
+ctx_collect_events (CtxEvent *event, void *data, void *data2);
+static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2)
+{
+  Ctx *ctx = event->ctx;
+  CtxEvents *events = &ctx->events;
+  int i;
+  int handled = 0;
 
-      for (i = 0; i < item->cb_count; i++)
+  for (i = events->n_bindings-1; i>=0; i--)
+    if (!strcmp (events->bindings[i].nick, event->string))
+    {
+      if (events->bindings[i].cb)
       {
-        if (item->cb[i].types & (CTX_MESSAGE))
-        {
-          event.state = ctx->events.modifier_state;
-          item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-          if (event.stop_propagate)
-            return;// event.stop_propagate;
-        }
+        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
+        if (event->stop_propagate)
+          return;
+        handled = 1;
       }
-  }
+    }
+  if (!handled)
+  for (i = events->n_bindings-1; i>=0; i--)
+    if (!strcmp (events->bindings[i].nick, "unhandled"))
+    {
+      if (events->bindings[i].cb)
+      {
+        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
+        if (event->stop_propagate)
+          return;
+      }
+    }
+  ctx_collect_events (event, data1, data2);
 }
 
-int ctx_scrolled (Ctx *ctx, float x, float y, CtxScrollDirection scroll_direction, uint32_t time)
+CtxBinding *ctx_get_bindings (Ctx *ctx)
 {
-  CtxList *hitlist = NULL;
-  CtxList *l;
+  return &ctx->events.bindings[0];
+}
 
-  int device_no = 0;
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
+void ctx_remove_idle (Ctx *ctx, int handle)
+{
+  CtxList *l;
+  CtxList *to_remove = NULL;
 
-  CtxEvent *event = &ctx->events.drag_event[device_no];  /* XXX: might
-                                       conflict with other code
-                                       create a sibling member
-                                       of drag_event?*/
-  if (time == 0)
-    time = ctx_ms (ctx);
-
-  event->x         = event->start_x = event->prev_x = x;
-  event->y         = event->start_y = event->prev_y = y;
-  event->delta_x   = event->delta_y = 0;
-  event->device_no = device_no;
-  event->time      = time;
-  event->stop_propagate = 0;
-  event->scroll_direction = scroll_direction;
-
-  _ctx_update_item (ctx, device_no, x, y, CTX_SCROLL, &hitlist);
-
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
-
-    _ctx_emit_cb_item (ctx, item, event, CTX_SCROLL, x, y);
-
-    if (event->stop_propagate)
-      l = NULL;
-  }
-
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-  return 0;
-}
-
-int ctx_key_press (Ctx *ctx, unsigned int keyval,
-                   const char *string, uint32_t time)
-{
-  char event_type[128]="";
-  float x, y; int b;
-  sscanf (string, "%s %f %f %i", event_type, &x, &y, &b);
-  if (!strcmp (event_type, "mouse-motion") ||
-      !strcmp (event_type, "mouse-drag"))
-  {
-    ctx_pointer_motion (ctx, x, y, b, 0);
-    return 0;
-  }
-  else if (!strcmp (event_type, "mouse-press"))
+  if (!ctx->events.idles)
   {
-    ctx_pointer_press (ctx, x, y, b, 0);
-    return 0;
+    return;
   }
-  else if (!strcmp (event_type, "mouse-release"))
+  for (l = ctx->events.idles; l; l = l->next)
   {
-    ctx_pointer_release (ctx, x, y, b, 0);
-    return 0;
+    CtxIdleCb *item = l->data;
+    if (item->id == handle)
+      ctx_list_prepend (&to_remove, item);
   }
-
-
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_DOWN);
-  CtxEvent event = {0,};
-
-  if (time == 0)
-    time = ctx_ms (ctx);
-  if (item)
+  for (l = to_remove; l; l = l->next)
   {
-    int i;
-    event.ctx = ctx;
-    event.type = CTX_KEY_DOWN;
-    event.unicode = keyval; 
-    event.string = strdup(string);
-    event.stop_propagate = 0;
-    event.time = time;
-
-    for (i = 0; i < item->cb_count; i++)
-    {
-      if (item->cb[i].types & (CTX_KEY_DOWN))
-      {
-        event.state = ctx->events.modifier_state;
-        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-#if 0
-        char buf[256];
-        ctx_set (ctx, ctx_strhash ("title", 0), buf, strlen(buf));
-        ctx_flush (ctx);
-#endif
-        if (event.stop_propagate)
-        {
-          free ((void*)event.string);
-          return event.stop_propagate;
-        }
-      }
-    }
-    free ((void*)event.string);
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
   }
-  return 0;
 }
 
-void ctx_freeze           (Ctx *ctx)
+int ctx_add_timeout_full (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
+                          void (*destroy_notify)(void *destroy_data), void *destroy_data)
 {
-  ctx->events.frozen ++;
+  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
+  item->cb              = idle_cb;
+  item->idle_data       = idle_data;
+  item->id              = ++ctx->events.idle_id;
+  item->ticks_full      = 
+  item->ticks_remaining = ms * 1000;
+  item->destroy_notify  = destroy_notify;
+  item->destroy_data    = destroy_data;
+  ctx_list_append (&ctx->events.idles, item);
+  return item->id;
 }
 
-void ctx_thaw             (Ctx *ctx)
-{
-  ctx->events.frozen --;
-}
-int ctx_events_frozen (Ctx *ctx)
-{
-  return ctx && ctx->events.frozen;
-}
-void ctx_events_clear_items (Ctx *ctx)
-{
-  ctx_list_free (&ctx->events.items);
-}
-int ctx_events_width (Ctx *ctx)
-{
-  return ctx->events.width;
-}
-int ctx_events_height (Ctx *ctx)
+int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
 {
-  return ctx->events.height;
+  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
 }
 
-float ctx_pointer_x (Ctx *ctx)
+int ctx_add_idle_full (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
+                                 void (*destroy_notify)(void *destroy_data), void *destroy_data)
 {
-  return ctx->events.pointer_x[0];
+  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
+  item->cb = idle_cb;
+  item->idle_data = idle_data;
+  item->id = ++ctx->events.idle_id;
+  item->ticks_full =
+  item->ticks_remaining = -1;
+  item->is_idle = 1;
+  item->destroy_notify = destroy_notify;
+  item->destroy_data = destroy_data;
+  ctx_list_append (&ctx->events.idles, item);
+  return item->id;
 }
 
-float ctx_pointer_y (Ctx *ctx)
+int ctx_add_idle (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
 {
-  return ctx->events.pointer_y[0];
+  return ctx_add_idle_full (ctx, idle_cb, idle_data, NULL, NULL);
 }
 
-int ctx_pointer_is_down (Ctx *ctx, int no)
+#endif
+/* using bigger primes would be a good idea, this falls apart due to rounding
+ * when zoomed in close
+ */
+static inline double ctx_path_hash (void *path)
 {
-  if (no < 0 || no > CTX_MAX_DEVICES) return 0;
-  return ctx->events.pointer_down[no];
+  double ret = 0;
+#if 0
+  int i;
+  cairo_path_data_t *data;
+  if (!path)
+    return 0.99999;
+  for (i = 0; i <path->num_data; i += path->data[i].header.length)
+  {
+    data = &path->data[i];
+    switch (data->header.type) {
+      case CAIRO_PATH_MOVE_TO:
+        ret *= 17;
+        ret += data[1].point.x;
+        ret *= 113;
+        ret += data[1].point.y;
+        break;
+      case CAIRO_PATH_LINE_TO:
+        ret *= 121;
+        ret += data[1].point.x;
+        ret *= 1021;
+        ret += data[1].point.y;
+        break;
+      case CAIRO_PATH_CURVE_TO:
+        ret *= 3111;
+        ret += data[1].point.x;
+        ret *= 23;
+        ret += data[1].point.y;
+        ret *= 107;
+        ret += data[2].point.x;
+        ret *= 739;
+        ret += data[2].point.y;
+        ret *= 3;
+        ret += data[3].point.x;
+        ret *= 51;
+        ret += data[3].point.y;
+        break;
+      case CAIRO_PATH_CLOSE_PATH:
+        ret *= 51;
+        break;
+    }
+  }
+#endif
+  return ret;
 }
 
-void _ctx_debug_overlays (Ctx *ctx)
+#if CTX_EVENTS
+void _ctx_item_ref (CtxItem *item)
 {
-  CtxList *a;
-  ctx_save (ctx);
-
-  ctx_line_width (ctx, 2);
-  ctx_rgba (ctx, 0,0,0.8,0.5);
-  for (a = ctx->events.items; a; a = a->next)
+  if (item->ref_count < 0)
   {
-    float current_x = ctx_pointer_x (ctx);
-    float current_y = ctx_pointer_y (ctx);
-    CtxItem *item = a->data;
-    CtxMatrix matrix = item->inv_matrix;
+    fprintf (stderr, "EEEEK!\n");
+  }
+  item->ref_count++;
+}
 
-    ctx_matrix_apply_transform (&matrix, &current_x, &current_y);
 
-    if (current_x >= item->x0 && current_x < item->x1 &&
-        current_y >= item->y0 && current_y < item->y1)
+void _ctx_item_unref (CtxItem *item)
+{
+  if (item->ref_count <= 0)
+  {
+    fprintf (stderr, "EEEEK!\n");
+    return;
+  }
+  item->ref_count--;
+  if (item->ref_count <=0)
+  {
     {
-      ctx_matrix_invert (&matrix);
-      ctx_set_matrix (ctx, &matrix);
-      _mrg_restore_path (ctx, item->path);
-      ctx_stroke (ctx);
+      int i;
+      for (i = 0; i < item->cb_count; i++)
+      {
+        if (item->cb[i].finalize)
+          item->cb[i].finalize (item->cb[i].data1, item->cb[i].data2,
+                                   item->cb[i].finalize_data);
+      }
+    }
+    if (item->path)
+    {
+      //cairo_path_destroy (item->path);
     }
+    free (item);
   }
-  ctx_restore (ctx);
 }
 
-void ctx_set_render_threads   (Ctx *ctx, int n_threads)
+
+static int
+path_equal (void *path,
+            void *path2)
 {
-  // XXX
+  //  XXX
+  return 0;
 }
-int ctx_get_render_threads   (Ctx *ctx)
+
+void ctx_listen_set_cursor (Ctx      *ctx,
+                            CtxCursor cursor)
 {
-  return _ctx_max_threads;
-}
-void ctx_set_hash_cache (Ctx *ctx, int enable_hash_cache)
-{
-  _ctx_enable_hash_cache = enable_hash_cache;
-}
-int ctx_get_hash_cache (Ctx *ctx)
-{
-  return _ctx_enable_hash_cache;
+  if (ctx->events.last_item)
+  {
+    ctx->events.last_item->cursor = cursor;
+  }
 }
 
-int ctx_is_dirty (Ctx *ctx)
-{
-  return ctx->dirty;
-}
-void ctx_set_dirty (Ctx *ctx, int dirty)
+void ctx_listen_full (Ctx     *ctx,
+                      float    x,
+                      float    y,
+                      float    width,
+                      float    height,
+                      CtxEventType  types,
+                      CtxCb    cb,
+                      void    *data1,
+                      void    *data2,
+                      void   (*finalize)(void *listen_data,
+                                         void *listen_data2,
+                                         void *finalize_data),
+                      void    *finalize_data)
 {
-  ctx->dirty = dirty;
-}
+  if (!ctx->events.frozen)
+  {
+    CtxItem *item;
 
-/*
- * centralized global API for managing file descriptors that
- * wake us up, this to remove sleeping and polling
- */
+    /* early bail for listeners outside screen  */
+    /* XXX: fixme respect clipping */
+    {
+      float tx = x;
+      float ty = y;
+      float tw = width;
+      float th = height;
+      _ctx_user_to_device (&ctx->state, &tx, &ty);
+      _ctx_user_to_device_distance (&ctx->state, &tw, &th);
+      if (ty > ctx->events.height * 2 ||
+          tx > ctx->events.width * 2 ||
+          tx + tw < 0 ||
+          ty + th < 0)
+      {
+        if (finalize)
+          finalize (data1, data2, finalize_data);
+        return;
+      }
+    }
 
-#define CTX_MAX_LISTEN_FDS 8
-static int _ctx_listen_fd[CTX_MAX_LISTEN_FDS];
-static int _ctx_listen_fds = 0;
-static int _ctx_listen_max_fd = 0;
+    item = calloc (sizeof (CtxItem), 1);
+    item->x0 = x;
+    item->y0 = y;
+    item->x1 = x + width;
+    item->y1 = y + height;
+    item->cb[0].types = types;
+    item->cb[0].cb = cb;
+    item->cb[0].data1 = data1;
+    item->cb[0].data2 = data2;
+    item->cb[0].finalize = finalize;
+    item->cb[0].finalize_data = finalize_data;
+    item->cb_count = 1;
+    item->types = types;
+    //item->path = cairo_copy_path (cr); // XXX
+    item->path_hash = ctx_path_hash (item->path);
+    ctx_get_matrix (ctx, &item->inv_matrix);
+    ctx_matrix_invert (&item->inv_matrix);
 
-void _ctx_add_listen_fd (int fd)
+    if (ctx->events.items)
+    {
+      CtxList *l;
+      for (l = ctx->events.items; l; l = l->next)
+      {
+        CtxItem *item2 = l->data;
+
+        /* store multiple callbacks for one entry when the paths
+         * are exact matches, reducing per event traversal checks at the
+         * cost of a little paint-hit (XXX: is this the right tradeoff,
+         * perhaps it is better to spend more time during event processing
+         * than during paint?)
+         */
+        if (item->path_hash == item2->path_hash &&
+            path_equal (item->path, item2->path))
+        {
+          /* found an item, copy over cb data  */
+          item2->cb[item2->cb_count] = item->cb[0];
+          free (item);
+          item2->cb_count++;
+          item2->types |= types;
+          return;
+        }
+      }
+    }
+    item->ref_count       = 1;
+    ctx->events.last_item = item;
+    ctx_list_prepend_full (&ctx->events.items, item, (void*)_ctx_item_unref, NULL);
+  }
+}
+
+void ctx_event_stop_propagate (CtxEvent *event)
 {
-  _ctx_listen_fd[_ctx_listen_fds++]=fd;
-  if (fd > _ctx_listen_max_fd)
-    _ctx_listen_max_fd = fd;
+  if (event)
+    event->stop_propagate = 1;
 }
 
-void _ctx_remove_listen_fd (int fd)
+void ctx_listen (Ctx          *ctx,
+                 CtxEventType  types,
+                 CtxCb         cb,
+                 void*         data1,
+                 void*         data2)
 {
-  for (int i = 0; i < _ctx_listen_fds; i++)
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  if (types & CTX_KEY)
   {
-    if (_ctx_listen_fd[i] == fd)
-    {
-      _ctx_listen_fd[i] = _ctx_listen_fd[_ctx_listen_fds-1];
-      _ctx_listen_fds--;
-      return;
-    }
+    x = 0;
+    y = 0;
+    width = 0;
+    height = 0;
+  }
+  else
+  {
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
   }
+
+  if (types == CTX_DRAG_MOTION)
+    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
+  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL);
 }
 
-int _ctx_data_pending (int timeout)
+void  ctx_listen_with_finalize (Ctx          *ctx,
+                                CtxEventType  types,
+                                CtxCb         cb,
+                                void*         data1,
+                                void*         data2,
+                      void   (*finalize)(void *listen_data, void *listen_data2,
+                                         void *finalize_data),
+                      void    *finalize_data)
 {
-  struct timeval tv;
-  fd_set fdset;
-  FD_ZERO (&fdset);
-  for (int i = 0; i < _ctx_listen_fds; i++)
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  if (types & CTX_KEY)
   {
-    FD_SET (_ctx_listen_fd[i], &fdset);
+    x = 0;
+    y = 0;
+    width = 0;
+    height = 0;
   }
-  tv.tv_sec = 0;
-  tv.tv_usec = timeout;
-  tv.tv_sec = timeout / 1000000;
-  tv.tv_usec = timeout % 1000000;
-  int retval = select (_ctx_listen_max_fd, &fdset, NULL, NULL, &tv);
-  if (retval == -1)
+  else
   {
-    perror ("select");
-    return 0;
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
   }
-  return retval;
+
+  if (types == CTX_DRAG_MOTION)
+    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
+  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data);
 }
 
-#endif
-/* the parser comes in the end, nothing in ctx knows about the parser  */
 
-#if CTX_PARSER
+static void ctx_report_hit_region (CtxEvent *event,
+                       void     *data,
+                       void     *data2)
+{
+  const char *id = data;
 
-/* ctx parser, */
+  fprintf (stderr, "hit region %s\n", id);
+  // XXX: NYI
+}
 
-struct
-  _CtxParser
+void ctx_add_hit_region (Ctx *ctx, const char *id)
 {
-  int        t_args; // total number of arguments seen for current command
-  Ctx       *ctx;
-  int        state;
-  uint8_t    holding[CTX_PARSER_MAXLEN]; /*  */
-  int        line; /*  for error reporting */
-  int        col;  /*  for error reporting */
-  int        pos;
-  float      numbers[CTX_PARSER_MAX_ARGS+1];
-  int        n_numbers;
-  int        decimal;
-  CtxCode    command;
-  int        expected_args; /* low digits are literal higher values
-                               carry special meaning */
-  int        n_args;
-  uint32_t   set_key_hash;
-  float      pcx;
-  float      pcy;
-  int        color_components;
-  int        color_model; // 1 gray 3 rgb 4 cmyk
-  float      left_margin; // set by last user provided move_to
-  int        width;       // <- maybe should be float
-  int        height;
-  float      cell_width;
-  float      cell_height;
-  int        cursor_x;    // <- leaking in from terminal
-  int        cursor_y;
-
-  int        color_space_slot;
+  char *id_copy = strdup (id);
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  {
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
+  }
+  
+  return ctx_listen_full (ctx, x, y, width, height,
+                          CTX_POINTER, ctx_report_hit_region,
+                          id_copy, NULL, (void*)free, NULL);
+}
 
-  void (*exit) (void *exit_data);
-  void *exit_data;
-  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len);
-  int   (*get_prop)(void *prop_data, const char *key, char **data, int *len);
-  void *prop_data;
-};
+typedef struct _CtxGrab CtxGrab;
 
-void
-ctx_parser_set_size (CtxParser *parser,
-                 int        width,
-                 int        height,
-                 float      cell_width,
-                 float      cell_height)
+struct _CtxGrab
 {
-  if (cell_width > 0)
-    parser->cell_width       = cell_width;
-  if (cell_height > 0)
-    parser->cell_height      = cell_height;
-  if (width > 0)
-    parser->width            = width;
-  if (height > 0)
-    parser->height           = height;
-}
+  CtxItem *item;
+  int      device_no;
+  int      timeout_id;
+  int      start_time;
+  float    x; // for tap and hold
+  float    y;
+  CtxEventType  type;
+};
 
-static CtxParser *
-ctx_parser_init (CtxParser *parser,
-                 Ctx       *ctx,
-                 int        width,
-                 int        height,
-                 float      cell_width,
-                 float      cell_height,
-                 int        cursor_x,
-                 int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
-  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
-                 void  *prop_data,
-                 void (*exit) (void *exit_data),
-                 void *exit_data
-                )
+static void grab_free (Ctx *ctx, CtxGrab *grab)
 {
-  ctx_memset (parser, 0, sizeof (CtxParser) );
-  parser->line             = 1;
-  parser->ctx              = ctx;
-  parser->cell_width       = cell_width;
-  parser->cell_height      = cell_height;
-  parser->cursor_x         = cursor_x;
-  parser->cursor_y         = cursor_y;
-  parser->width            = width;
-  parser->height           = height;
-  parser->exit             = exit;
-  parser->exit_data        = exit_data;
-  parser->color_model      = CTX_RGBA;
-  parser->color_components = 4;
-  parser->command          = CTX_MOVE_TO;
-  parser->set_prop         = set_prop;
-  parser->get_prop         = get_prop;
-  parser->prop_data        = prop_data;
-  return parser;
+  if (grab->timeout_id)
+  {
+    ctx_remove_idle (ctx, grab->timeout_id);
+    grab->timeout_id = 0;
+  }
+  _ctx_item_unref (grab->item);
+  free (grab);
 }
 
-CtxParser *ctx_parser_new (
-  Ctx       *ctx,
-  int        width,
-  int        height,
-  float      cell_width,
-  float      cell_height,
-  int        cursor_x,
-  int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
-  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
-  void  *prop_data,
-  void (*exit) (void *exit_data),
-  void *exit_data)
+static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
 {
-  return ctx_parser_init ( (CtxParser *) ctx_calloc (sizeof (CtxParser), 1),
-                           ctx,
-                           width, height,
-                           cell_width, cell_height,
-                           cursor_x, cursor_y, set_prop, get_prop, prop_data,
-                           exit, exit_data);
+  ctx_list_remove (&ctx->events.grabs, grab);
+  grab_free (ctx, grab);
 }
 
-void ctx_parser_free (CtxParser *parser)
+static CtxGrab *device_add_grab (Ctx *ctx, int device_no, CtxItem *item, CtxEventType type)
 {
-  free (parser);
+  CtxGrab *grab = calloc (1, sizeof (CtxGrab));
+  grab->item = item;
+  grab->type = type;
+  _ctx_item_ref (item);
+  grab->device_no = device_no;
+  ctx_list_append (&ctx->events.grabs, grab);
+  return grab;
 }
 
-#define CTX_ARG_COLLECT_NUMBERS             50
-#define CTX_ARG_STRING_OR_NUMBER            100
-#define CTX_ARG_NUMBER_OF_COMPONENTS        200
-#define CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1 201
-
-static int ctx_arguments_for_code (CtxCode code)
+CtxList *device_get_grabs (Ctx *ctx, int device_no)
 {
-  switch (code)
-    {
-      case CTX_SAVE:
-      case CTX_START_GROUP:
-      case CTX_END_GROUP:
-      case CTX_IDENTITY:
-      case CTX_CLOSE_PATH:
-      case CTX_BEGIN_PATH:
-      case CTX_RESET:
-      case CTX_FLUSH:
-      case CTX_RESTORE:
-      case CTX_STROKE:
-      case CTX_FILL:
-      case CTX_NEW_PAGE:
-      case CTX_CLIP:
-      case CTX_EXIT:
-        return 0;
-      case CTX_GLOBAL_ALPHA:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-      case CTX_FONT_SIZE:
-      case CTX_LINE_JOIN:
-      case CTX_LINE_CAP:
-      case CTX_LINE_WIDTH:
-      case CTX_SHADOW_BLUR:
-      case CTX_SHADOW_OFFSET_X:
-      case CTX_SHADOW_OFFSET_Y:
-      case CTX_FILL_RULE:
-      case CTX_TEXT_ALIGN:
-      case CTX_TEXT_BASELINE:
-      case CTX_TEXT_DIRECTION:
-      case CTX_MITER_LIMIT:
-      case CTX_REL_VER_LINE_TO:
-      case CTX_REL_HOR_LINE_TO:
-      case CTX_HOR_LINE_TO:
-      case CTX_VER_LINE_TO:
-      case CTX_FONT:
-      case CTX_ROTATE:
-      case CTX_GLYPH:
-        return 1;
-      case CTX_TRANSLATE:
-      case CTX_REL_SMOOTHQ_TO:
-      case CTX_LINE_TO:
-      case CTX_MOVE_TO:
-      case CTX_SCALE:
-      case CTX_REL_LINE_TO:
-      case CTX_REL_MOVE_TO:
-      case CTX_SMOOTHQ_TO:
-        return 2;
-      case CTX_LINEAR_GRADIENT:
-      case CTX_REL_QUAD_TO:
-      case CTX_QUAD_TO:
-      case CTX_RECTANGLE:
-      case CTX_REL_SMOOTH_TO:
-      case CTX_VIEW_BOX:
-      case CTX_SMOOTH_TO:
-        return 4;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-      case CTX_ROUND_RECTANGLE:
-        return 5;
-      case CTX_ARC:
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_APPLY_TRANSFORM:
-      case CTX_RADIAL_GRADIENT:
-        return 6;
-      case CTX_TEXT_STROKE:
-      case CTX_TEXT: // special case
-      case CTX_COLOR_SPACE:
-      case CTX_DEFINE_GLYPH:
-      case CTX_KERNING_PAIR:
-        return CTX_ARG_STRING_OR_NUMBER;
-      case CTX_LINE_DASH: /* append to current dashes for each argument encountered */
-        return CTX_ARG_COLLECT_NUMBERS;
-      //case CTX_SET_KEY:
-      case CTX_COLOR:
-      case CTX_SHADOW_COLOR:
-        return CTX_ARG_NUMBER_OF_COMPONENTS;
-      case CTX_GRADIENT_STOP:
-        return CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1;
+  CtxList *ret = NULL;
+  CtxList *l;
+  for (l = ctx->events.grabs; l; l = l->next)
+  {
+    CtxGrab *grab = l->data;
+    if (grab->device_no == device_no)
+      ctx_list_append (&ret, grab);
+  }
+  return ret;
+}
 
-        default:
-#if 1
-      case CTX_TEXTURE:
-        case CTX_SET_RGBA_U8:
-        case CTX_BITPIX:
-        case CTX_NOP:
-        case CTX_NEW_EDGE:
-        case CTX_EDGE:
-        case CTX_EDGE_FLIPPED:
-        case CTX_CONT:
-        case CTX_DATA:
-        case CTX_DATA_REV:
-        case CTX_SET_PIXEL:
-        case CTX_REL_LINE_TO_X4:
-        case CTX_REL_LINE_TO_REL_CURVE_TO:
-        case CTX_REL_CURVE_TO_REL_LINE_TO:
-        case CTX_REL_CURVE_TO_REL_MOVE_TO:
-        case CTX_REL_LINE_TO_X2:
-        case CTX_MOVE_TO_REL_LINE_TO:
-        case CTX_REL_LINE_TO_REL_MOVE_TO:
-        case CTX_FILL_MOVE_TO:
-        case CTX_REL_QUAD_TO_REL_QUAD_TO:
-        case CTX_REL_QUAD_TO_S16:
-#endif
-        return 0;
-    }
+static void _mrg_restore_path (Ctx *ctx, void *path)  //XXX
+{
+  //int i;
+  //cairo_path_data_t *data;
+  //cairo_new_path (cr);
+  //cairo_append_path (cr, path);
 }
 
-static int ctx_parser_set_command (CtxParser *parser, CtxCode code)
+CtxList *_ctx_detect_list (Ctx *ctx, float x, float y, CtxEventType type)
 {
-  if (code < 150 && code >= 32)
+  CtxList *a;
+  CtxList *ret = NULL;
+
+  if (type == CTX_KEY_DOWN ||
+      type == CTX_KEY_UP ||
+      type == CTX_KEY_PRESS ||
+      type == CTX_MESSAGE ||
+      type == (CTX_KEY_DOWN|CTX_MESSAGE) ||
+      type == (CTX_KEY_DOWN|CTX_KEY_UP) ||
+      type == (CTX_KEY_DOWN|CTX_KEY_UP|CTX_MESSAGE))
   {
-  parser->expected_args = ctx_arguments_for_code (code);
-  parser->n_args = 0;
-  if (parser->expected_args >= CTX_ARG_NUMBER_OF_COMPONENTS)
+    for (a = ctx->events.items; a; a = a->next)
     {
-      parser->expected_args = (parser->expected_args % 100) + parser->color_components;
+      CtxItem *item = a->data;
+      if (item->types & type)
+      {
+        ctx_list_prepend (&ret, item);
+        return ret;
+      }
     }
+    return NULL;
   }
-  return code;
-}
-
-static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model);
-
-static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str)
-{
-  uint32_t ret = str[0]; /* if it is single char it already is the CtxCode */
 
-  /* this is handled outside the hashing to make it possible to be case insensitive
-   * with the rest.
-   */
-  if (str[0] == CTX_SET_KEY && str[1] && str[2] == 0)
+  for (a = ctx->events.items; a; a = a->next)
   {
-    switch (str[1])
-    {
-      case 'm': return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
-      case 'B': return ctx_parser_set_command (parser, CTX_BLEND_MODE);
-      case 'l': return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
-      case 't': return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
-      case 'b': return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
-      case 'd': return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
-      case 'j': return ctx_parser_set_command (parser, CTX_LINE_JOIN);
-      case 'c': return ctx_parser_set_command (parser, CTX_LINE_CAP);
-      case 'w': return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
-      case 'C': return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
-      case 's': return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
-      case 'x': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
-      case 'y': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
-      case 'a': return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
-      case 'f': return ctx_parser_set_command (parser, CTX_FONT_SIZE);
-      case 'r': return ctx_parser_set_command (parser, CTX_FILL_RULE);
-    }
-  }
+    CtxItem *item= a->data;
+  
+    float u, v;
+    u = x;
+    v = y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &u, &v);
 
-  if (str[0] && str[1])
+    if (u >= item->x0 && v >= item->y0 &&
+        u <  item->x1 && v <  item->y1 && 
+        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
+        item->cursor)))
     {
-      uint32_t str_hash;
-      /* trim ctx_ and CTX_ prefix */
-      if ( (str[0] == 'c' && str[1] == 't' && str[2] == 'x' && str[3] == '_') ||
-           (str[0] == 'C' && str[1] == 'T' && str[2] == 'X' && str[3] == '_') )
-        {
-          str += 4;
-        }
-      if ( (str[0] == 's' && str[1] == 'e' && str[2] == 't' && str[3] == '_') )
-        { str += 4; }
-      str_hash = ctx_strhash ( (char *) str, 0);
-      switch (str_hash)
+      if (item->path)
+      {
+        _mrg_restore_path (ctx, item->path);
+        if (ctx_in_fill (ctx, u, v))
         {
-          /* first a list of mappings to one_char hashes, handled in a
-           * separate fast path switch without hashing
-           */
-          case CTX_arcTo:          ret = CTX_ARC_TO; break;
-          case CTX_arc:            ret = CTX_ARC; break;
-          case CTX_curveTo:        ret = CTX_CURVE_TO; break;
-          case CTX_restore:        ret = CTX_RESTORE; break;
-          case CTX_stroke:         ret = CTX_STROKE; break;
-          case CTX_fill:           ret = CTX_FILL; break;
-          case CTX_flush:          ret = CTX_FLUSH; break;
-          case CTX_horLineTo:      ret = CTX_HOR_LINE_TO; break;
-          case CTX_rotate:         ret = CTX_ROTATE; break;
-          case CTX_color:          ret = CTX_COLOR; break;
-          case CTX_lineTo:         ret = CTX_LINE_TO; break;
-          case CTX_moveTo:         ret = CTX_MOVE_TO; break;
-          case CTX_scale:          ret = CTX_SCALE; break;
-          case CTX_newPage:        ret = CTX_NEW_PAGE; break;
-          case CTX_quadTo:         ret = CTX_QUAD_TO; break;
-          case CTX_viewBox:        ret = CTX_VIEW_BOX; break;
-          case CTX_smooth_to:      ret = CTX_SMOOTH_TO; break;
-          case CTX_smooth_quad_to: ret = CTX_SMOOTHQ_TO; break;
-          case CTX_clear:          ret = CTX_COMPOSITE_CLEAR; break;
-          case CTX_copy:           ret = CTX_COMPOSITE_COPY; break;
-          case CTX_destinationOver: ret = CTX_COMPOSITE_DESTINATION_OVER; break;
-          case CTX_destinationIn:    ret = CTX_COMPOSITE_DESTINATION_IN; break;
-          case CTX_destinationOut:   ret = CTX_COMPOSITE_DESTINATION_OUT; break;
-          case CTX_sourceOver:       ret = CTX_COMPOSITE_SOURCE_OVER; break;
-          case CTX_sourceAtop:       ret = CTX_COMPOSITE_SOURCE_ATOP; break;
-          case CTX_destinationAtop:  ret = CTX_COMPOSITE_DESTINATION_ATOP; break;
-          case CTX_sourceOut:        ret = CTX_COMPOSITE_SOURCE_OUT; break;
-          case CTX_sourceIn:         ret = CTX_COMPOSITE_SOURCE_IN; break;
-          case CTX_xor:              ret = CTX_COMPOSITE_XOR; break;
-          case CTX_darken:           ret = CTX_BLEND_DARKEN; break;
-          case CTX_lighten:          ret = CTX_BLEND_LIGHTEN; break;
-          //case CTX_color:          ret = CTX_BLEND_COLOR; break;
-          //
-          //  XXX check that he special casing for color works
-          //      it is the first collision and it is due to our own
-          //      color, not w3c for now unique use of it
-          //
-          case CTX_hue:            ret = CTX_BLEND_HUE; break;
-          case CTX_multiply:       ret = CTX_BLEND_MULTIPLY; break;
-          case CTX_normal:         ret = CTX_BLEND_NORMAL;break;
-          case CTX_screen:         ret = CTX_BLEND_SCREEN;break;
-          case CTX_difference:     ret = CTX_BLEND_DIFFERENCE; break;
-          case CTX_reset:          ret = CTX_RESET; break;
-          case CTX_verLineTo:  ret = CTX_VER_LINE_TO; break;
-          case CTX_exit:
-          case CTX_done:           ret = CTX_EXIT; break;
-          case CTX_closePath:      ret = CTX_CLOSE_PATH; break;
-          case CTX_beginPath:
-          case CTX_newPath:        ret = CTX_BEGIN_PATH; break;
-          case CTX_relArcTo:       ret = CTX_REL_ARC_TO; break;
-          case CTX_clip:           ret = CTX_CLIP; break;
-          case CTX_relCurveTo:     ret = CTX_REL_CURVE_TO; break;
-          case CTX_startGroup:     ret = CTX_START_GROUP; break;
-          case CTX_endGroup:       ret = CTX_END_GROUP; break;
-          case CTX_save:           ret = CTX_SAVE; break;
-          case CTX_translate:      ret = CTX_TRANSLATE; break;
-          case CTX_linearGradient: ret = CTX_LINEAR_GRADIENT; break;
-          case CTX_relHorLineTo:   ret = CTX_REL_HOR_LINE_TO; break;
-          case CTX_relLineTo:      ret = CTX_REL_LINE_TO; break;
-          case CTX_relMoveTo:      ret = CTX_REL_MOVE_TO; break;
-          case CTX_font:           ret = CTX_FONT; break;
-          case CTX_radialGradient:ret = CTX_RADIAL_GRADIENT; break;
-          case CTX_gradientAddStop:
-          case CTX_addStop:        ret = CTX_GRADIENT_STOP; break;
-          case CTX_relQuadTo:      ret = CTX_REL_QUAD_TO; break;
-          case CTX_rectangle:
-          case CTX_rect:           ret = CTX_RECTANGLE; break;
-          case CTX_roundRectangle: ret = CTX_ROUND_RECTANGLE; break;
-          case CTX_relSmoothTo:    ret = CTX_REL_SMOOTH_TO; break;
-          case CTX_relSmoothqTo:   ret = CTX_REL_SMOOTHQ_TO; break;
-          case CTX_textStroke:     ret = CTX_TEXT_STROKE; break;
-          case CTX_relVerLineTo:   ret = CTX_REL_VER_LINE_TO; break;
-          case CTX_text:           ret = CTX_TEXT; break;
-          case CTX_identity:       ret = CTX_IDENTITY; break;
-          case CTX_transform:      ret = CTX_APPLY_TRANSFORM; break;
-          case CTX_texture:        ret = CTX_TEXTURE; break;
-#if 0
-          case CTX_rgbSpace:
-            return ctx_parser_set_command (parser, CTX_SET_RGB_SPACE);
-          case CTX_cmykSpace:
-            return ctx_parser_set_command (parser, CTX_SET_CMYK_SPACE);
-          case CTX_drgbSpace:
-            return ctx_parser_set_command (parser, CTX_SET_DRGB_SPACE);
-#endif
-          case CTX_defineGlyph:
-            return ctx_parser_set_command (parser, CTX_DEFINE_GLYPH);
-          case CTX_kerningPair:
-            return ctx_parser_set_command (parser, CTX_KERNING_PAIR);
-
-          case CTX_colorSpace:
-            return ctx_parser_set_command (parser, CTX_COLOR_SPACE);
-          case CTX_fillRule:
-            return ctx_parser_set_command (parser, CTX_FILL_RULE);
-          case CTX_fontSize:
-          case CTX_setFontSize:
-            return ctx_parser_set_command (parser, CTX_FONT_SIZE);
-          case CTX_compositingMode:
-            return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
-
-          case CTX_blend:
-          case CTX_blending:
-          case CTX_blendMode:
-            return ctx_parser_set_command (parser, CTX_BLEND_MODE);
-
-          case CTX_miterLimit:
-            return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
-          case CTX_textAlign:
-            return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
-          case CTX_textBaseline:
-            return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
-          case CTX_textDirection:
-            return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
-          case CTX_join:
-          case CTX_lineJoin:
-          case CTX_setLineJoin:
-            return ctx_parser_set_command (parser, CTX_LINE_JOIN);
-          case CTX_glyph:
-            return ctx_parser_set_command (parser, CTX_GLYPH);
-          case CTX_cap:
-          case CTX_lineCap:
-          case CTX_setLineCap:
-            return ctx_parser_set_command (parser, CTX_LINE_CAP);
-          case CTX_lineDash:
-            return ctx_parser_set_command (parser, CTX_LINE_DASH);
-          case CTX_lineWidth:
-          case CTX_setLineWidth:
-            return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
-          case CTX_shadowColor:
-            return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
-          case CTX_shadowBlur:
-            return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
-          case CTX_shadowOffsetX:
-            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
-          case CTX_shadowOffsetY:
-            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
-          case CTX_globalAlpha:
-            return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
-          /* strings are handled directly here,
-           * instead of in the one-char handler, using return instead of break
-           */
-          case CTX_gray:
-            ctx_parser_set_color_model (parser, CTX_GRAY);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_graya:
-            ctx_parser_set_color_model (parser, CTX_GRAYA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgb:
-            ctx_parser_set_color_model (parser, CTX_RGB);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgb:
-            ctx_parser_set_color_model (parser, CTX_DRGB);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgba:
-            ctx_parser_set_color_model (parser, CTX_RGBA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgba:
-            ctx_parser_set_color_model (parser, CTX_DRGBA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmyk:
-            ctx_parser_set_color_model (parser, CTX_CMYK);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmyka:
-            ctx_parser_set_color_model (parser, CTX_CMYKA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lab:
-            ctx_parser_set_color_model (parser, CTX_LAB);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_laba:
-            ctx_parser_set_color_model (parser, CTX_LABA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lch:
-            ctx_parser_set_color_model (parser, CTX_LCH);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lcha:
-            ctx_parser_set_color_model (parser, CTX_LCHA);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          /* words that correspond to low integer constants
-          */
-          case CTX_winding:     return CTX_FILL_RULE_WINDING;
-          case CTX_evenOdd:
-          case CTX_even_odd:    return CTX_FILL_RULE_EVEN_ODD;
-          case CTX_bevel:       return CTX_JOIN_BEVEL;
-          case CTX_round:       return CTX_JOIN_ROUND;
-          case CTX_miter:       return CTX_JOIN_MITER;
-          case CTX_none:        return CTX_CAP_NONE;
-          case CTX_square:      return CTX_CAP_SQUARE;
-          case CTX_start:       return CTX_TEXT_ALIGN_START;
-          case CTX_end:         return CTX_TEXT_ALIGN_END;
-          case CTX_left:        return CTX_TEXT_ALIGN_LEFT;
-          case CTX_right:       return CTX_TEXT_ALIGN_RIGHT;
-          case CTX_center:      return CTX_TEXT_ALIGN_CENTER;
-          case CTX_top:         return CTX_TEXT_BASELINE_TOP;
-          case CTX_bottom :     return CTX_TEXT_BASELINE_BOTTOM;
-          case CTX_middle:      return CTX_TEXT_BASELINE_MIDDLE;
-          case CTX_alphabetic:  return CTX_TEXT_BASELINE_ALPHABETIC;
-          case CTX_hanging:     return CTX_TEXT_BASELINE_HANGING;
-          case CTX_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC;
-
-          case CTX_userRGB:     return CTX_COLOR_SPACE_USER_RGB;
-          case CTX_deviceRGB:   return CTX_COLOR_SPACE_DEVICE_RGB;
-          case CTX_userCMYK:    return CTX_COLOR_SPACE_USER_CMYK;
-          case CTX_deviceCMYK:  return CTX_COLOR_SPACE_DEVICE_CMYK;
-#undef STR
-#undef LOWER
-          default:
-            ret = str_hash;
+          ctx_begin_path (ctx);
+          ctx_list_prepend (&ret, item);
         }
+        ctx_begin_path (ctx);
+      }
+      else
+      {
+        ctx_list_prepend (&ret, item);
+      }
     }
-  if (ret == CTX_CLOSE_PATH2)
-    { ret = CTX_CLOSE_PATH; }
-  /* handling single char, and ret = foo; break;  in cases above*/
-  return ctx_parser_set_command (parser, (CtxCode) ret);
+  }
+  return ret;
 }
 
-enum
+CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
 {
-  CTX_PARSER_NEUTRAL = 0,
-  CTX_PARSER_NUMBER,
-  CTX_PARSER_NEGATIVE_NUMBER,
-  CTX_PARSER_WORD,
-  CTX_PARSER_COMMENT,
-  CTX_PARSER_STRING_APOS,
-  CTX_PARSER_STRING_QUOT,
-  CTX_PARSER_STRING_APOS_ESCAPED,
-  CTX_PARSER_STRING_QUOT_ESCAPED,
-  CTX_PARSER_STRING_A85,
-} CTX_STATE;
+  CtxList *l = _ctx_detect_list (ctx, x, y, type);
+  if (l)
+  {
+    ctx_list_reverse (&l);
+    CtxItem *ret = l->data;
+    ctx_list_free (&l);
+    return ret;
+  }
+  return NULL;
+}
 
-static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model)
+static int
+_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
 {
-  parser->color_model      = color_model;
-  parser->color_components = ctx_color_model_get_components (color_model);
+  static CtxEvent s_event;
+  CtxEvent transformed_event;
+  int i;
+
+
+  if (!event)
+  {
+    event = &s_event;
+    event->type = type;
+    event->x = x;
+    event->y = y;
+  }
+  event->ctx = ctx;
+  transformed_event = *event;
+  transformed_event.device_x = event->x;
+  transformed_event.device_y = event->y;
+
+  {
+    float tx, ty;
+    tx = transformed_event.x;
+    ty = transformed_event.y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+    transformed_event.x = tx;
+    transformed_event.y = ty;
+
+    if ((type & CTX_DRAG_PRESS) ||
+        (type & CTX_DRAG_MOTION) ||
+        (type & CTX_MOTION))   /* probably a worthwhile check for the performance 
+                                  benefit
+                                */
+    {
+      tx = transformed_event.start_x;
+      ty = transformed_event.start_y;
+      ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+      transformed_event.start_x = tx;
+      transformed_event.start_y = ty;
+    }
+
+
+    tx = transformed_event.delta_x;
+    ty = transformed_event.delta_y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+    transformed_event.delta_x = tx;
+    transformed_event.delta_y = ty;
+  }
+
+  transformed_event.state = ctx->events.modifier_state;
+  transformed_event.type = type;
+
+  for (i = item->cb_count-1; i >= 0; i--)
+  {
+    if (item->cb[i].types & type)
+    {
+      item->cb[i].cb (&transformed_event, item->cb[i].data1, item->cb[i].data2);
+      event->stop_propagate = transformed_event.stop_propagate; /* copy back the response */
+      if (event->stop_propagate)
+        return event->stop_propagate;
+    }
+  }
+  return 0;
 }
+#endif
 
-static void ctx_parser_get_color_rgba (CtxParser *parser, int offset, float *red, float *green, float *blue, 
float *alpha)
+#if CTX_EVENTS
+
+#include <stdatomic.h>
+
+int ctx_native_events = 0;
+#if CTX_SDL
+int ctx_sdl_events = 0;
+int ctx_sdl_consume_events (Ctx *ctx);
+#endif
+
+#if CTX_FB
+int ctx_fb_events = 0;
+int ctx_fb_consume_events (Ctx *ctx);
+#endif
+
+int ctx_nct_consume_events (Ctx *ctx);
+int ctx_ctx_consume_events (Ctx *ctx);
+
+void ctx_consume_events (Ctx *ctx)
 {
-  /* XXX - this function is to be deprecated */
-  *alpha = 1.0;
-  switch (parser->color_model)
+#if CTX_SDL
+  if (ctx_sdl_events)
+    ctx_sdl_consume_events (ctx);
+  else
+#endif
+#if CTX_FB
+  if (ctx_fb_events)
+    ctx_fb_consume_events (ctx);
+  else
+#endif
+  if (ctx_native_events)
+    ctx_ctx_consume_events (ctx);
+  else
+    ctx_nct_consume_events (ctx);
+}
+
+CtxEvent *ctx_get_event (Ctx *ctx)
+{
+  static CtxEvent event_copy;
+  _ctx_idle_iteration (ctx);
+  if (!ctx->events.ctx_get_event_enabled)
+    ctx->events.ctx_get_event_enabled = 1;
+
+  ctx_consume_events (ctx);
+
+  if (ctx->events.events)
     {
-      case CTX_GRAYA:
-        *alpha = parser->numbers[offset + 1];
-        /* FALLTHROUGH */
-      case CTX_GRAY:
-        *red = *green = *blue = parser->numbers[offset + 0];
-        break;
-      default:
-      case CTX_LABA: // NYI - needs RGB profile
-      case CTX_LCHA: // NYI - needs RGB profile
-      case CTX_RGBA:
-        *alpha = parser->numbers[offset + 3];
-        /* FALLTHROUGH */
-      case CTX_LAB: // NYI
-      case CTX_LCH: // NYI
-      case CTX_RGB:
-        *red = parser->numbers[offset + 0];
-        *green = parser->numbers[offset + 1];
-        *blue = parser->numbers[offset + 2];
-        break;
-      case CTX_CMYKA:
-        *alpha = parser->numbers[offset + 4];
-        /* FALLTHROUGH */
-      case CTX_CMYK:
-        /* should use profile instead  */
-        *red = (1.0-parser->numbers[offset + 0]) *
-               (1.0 - parser->numbers[offset + 3]);
-        *green = (1.0-parser->numbers[offset + 1]) *
-                 (1.0 - parser->numbers[offset + 3]);
-        *blue = (1.0-parser->numbers[offset + 2]) *
-                (1.0 - parser->numbers[offset + 3]);
-        break;
+      event_copy = *((CtxEvent*)(ctx->events.events->data));
+      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
+      return &event_copy;
     }
+  return NULL;
 }
 
-static void ctx_parser_dispatch_command (CtxParser *parser)
+static int
+_ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y)
 {
-  CtxCode cmd = parser->command;
-  Ctx *ctx = parser->ctx;
-  if (parser->expected_args != CTX_ARG_STRING_OR_NUMBER &&
-      parser->expected_args != CTX_ARG_COLLECT_NUMBERS &&
-      parser->expected_args != parser->n_numbers)
+  CtxList *l;
+  event->stop_propagate = 0;
+  for (l = items; l; l = l->next)
+  {
+    _ctx_emit_cb_item (ctx, l->data, event, type, x, y);
+    if (event->stop_propagate)
+      return event->stop_propagate;
+  }
+  return 0;
+}
+
+/*
+ * update what is the currently hovered item and returns it.. and the list of hits
+ * a well.
+ *
+ */
+static CtxItem *_ctx_update_item (Ctx *ctx, int device_no, float x, float y, CtxEventType type, CtxList 
**hitlist)
+{
+  CtxItem *current = NULL;
+
+  CtxList *l = _ctx_detect_list (ctx, x, y, type);
+  if (l)
+  {
+    ctx_list_reverse (&l);
+    current = l->data;
+  }
+  if (hitlist)
+    *hitlist = l;
+  else
+    ctx_list_free (&l);
+
+  if (ctx->events.prev[device_no] == NULL || current == NULL || (current->path_hash != 
ctx->events.prev[device_no]->path_hash))
+  {
+// enter/leave should snapshot chain to root
+// and compare with previous snapshotted chain to root
+// and emit/enter/leave as appropriate..
+//
+// leave might be registered for emission on enter..emission?
+
+
+    //int focus_radius = 2;
+    if (current)
+      _ctx_item_ref (current);
+
+    if (ctx->events.prev[device_no])
     {
-      ctx_log ("ctx:%i:%i %c got %i instead of %i args\n",
-               parser->line, parser->col,
-               cmd, parser->n_numbers, parser->expected_args);
+      {
+#if 0
+        CtxIntRectangle rect = {floor(ctx->events.prev[device_no]->x0-focus_radius),
+                             floor(ctx->events.prev[device_no]->y0-focus_radius),
+                             ceil(ctx->events.prev[device_no]->x1)-floor(ctx->events.prev[device_no]->x0) + 
focus_radius * 2,
+                             ceil(ctx->events.prev[device_no]->y1)-floor(ctx->events.prev[device_no]->y0) + 
focus_radius * 2};
+        mrg_queue_draw (mrg, &rect);
+#endif 
+      }
+
+      _ctx_emit_cb_item (ctx, ctx->events.prev[device_no], NULL, CTX_LEAVE, x, y);
+      _ctx_item_unref (ctx->events.prev[device_no]);
+      ctx->events.prev[device_no] = NULL;
     }
-#define arg(a)  (parser->numbers[a])
-  parser->command = CTX_NOP;
-  switch (cmd)
+    if (current)
     {
-      default:
-        break; // to silence warnings about missing ones
-      case CTX_PRESERVE:
-        ctx_preserve (ctx);
-        break;
-      case CTX_FILL:
-        ctx_fill (ctx);
-        break;
-      case CTX_SAVE:
-        ctx_save (ctx);
-        break;
-      case CTX_START_GROUP:
-        ctx_start_group (ctx);
-        break;
-      case CTX_END_GROUP:
-        ctx_end_group (ctx);
-        break;
-      case CTX_STROKE:
-        ctx_stroke (ctx);
-        break;
-      case CTX_RESTORE:
-        ctx_restore (ctx);
-        break;
-#if CTX_ENABLE_CM
-      case CTX_COLOR_SPACE:
-        if (parser->n_numbers == 1)
-        {
-          parser->color_space_slot = arg(0);
-          parser->command = CTX_COLOR_SPACE; // did this work without?
-        }
-        else
-        {
-          ctx_colorspace (ctx, parser->color_space_slot,
-                               parser->holding, parser->pos);
-        }
-        break;
+#if 0
+      {
+        CtxIntRectangle rect = {floor(current->x0-focus_radius),
+                             floor(current->y0-focus_radius),
+                             ceil(current->x1)-floor(current->x0) + focus_radius * 2,
+                             ceil(current->y1)-floor(current->y0) + focus_radius * 2};
+        mrg_queue_draw (mrg, &rect);
+      }
 #endif
-      case CTX_KERNING_PAIR:
-        switch (parser->n_args)
-        {
-          case 0:
-            parser->numbers[0] = ctx_utf8_to_unichar ((char*)parser->holding);
-            break;
-          case 1:
-            parser->numbers[1] = ctx_utf8_to_unichar ((char*)parser->holding);
-            break;
-          case 2:
-            parser->numbers[2] = ctx_utf8_to_unichar ((char*)parser->holding);
-            {
-              CtxEntry e = {CTX_KERNING_PAIR, };
-              e.data.u16[0] = parser->numbers[0];
-              e.data.u16[1] = parser->numbers[1];
-              e.data.s32[2] = parser->numbers[2] * 256;
-              ctx_process (ctx, &e);
-            }
-            break;
-        }
-        parser->command = CTX_KERNING_PAIR;
-        parser->n_args ++; // make this more generic?
-        break;             
-      case CTX_DEFINE_GLYPH:
-        /* XXX : reuse n_args logic - to enforce order */
-        if (parser->n_numbers == 1)
-        {
-          CtxEntry e = {CTX_DEFINE_GLYPH, };
-          e.data.u32[0] = parser->color_space_slot;
-          e.data.u32[1] = arg(0) * 256;
-          ctx_process (ctx, &e);
-        }
-        else
-        {
-          int unichar = ctx_utf8_to_unichar ((char*)parser->holding);
-          parser->color_space_slot = unichar;
-        }
-        parser->command = CTX_DEFINE_GLYPH;
-        break;             
+      _ctx_emit_cb_item (ctx, current, NULL, CTX_ENTER, x, y);
+      ctx->events.prev[device_no] = current;
+    }
+  }
+  current = _ctx_detect (ctx, x, y, type);
+  //fprintf (stderr, "%p\n", current);
+  return current;
+}
 
-      case CTX_COLOR:
-        {
-          switch (parser->color_model)
-            {
-              case CTX_GRAY:
-                ctx_gray (ctx, arg (0) );
-                break;
-              case CTX_GRAYA:
-                ctx_rgba (ctx, arg (0), arg (0), arg (0), arg (1) );
-                break;
-              case CTX_RGB:
-                ctx_rgb (ctx, arg (0), arg (1), arg (2) );
-                break;
-#if CTX_ENABLE_CMYK
-              case CTX_CMYK:
-                ctx_cmyk (ctx, arg (0), arg (1), arg (2), arg (3) );
-                break;
-              case CTX_CMYKA:
-                ctx_cmyka (ctx, arg (0), arg (1), arg (2), arg (3), arg (4) );
-                break;
-#else
-              /* when there is no cmyk support at all in rasterizer
-               * do a naive mapping to RGB on input.
-               */
-              case CTX_CMYK:
-              case CTX_CMYKA:
-                {
-                  float r,g,b,a = 1.0f;
-                  ctx_cmyk_to_rgb (arg (0), arg (1), arg (2), arg (3), &r, &g, &b);
-                  if (parser->color_model == CTX_CMYKA)
-                    { a = arg (4); }
-                  ctx_rgba (ctx, r, g, b, a);
-                }
-                break;
-#endif
-              case CTX_RGBA:
-                ctx_rgba (ctx, arg (0), arg (1), arg (2), arg (3) );
-                break;
-              case CTX_DRGB:
-                ctx_drgba (ctx, arg (0), arg (1), arg (2), 1.0);
-                break;
-              case CTX_DRGBA:
-                ctx_drgba (ctx, arg (0), arg (1), arg (2), arg (3) );
-                break;
-            }
-        }
-        break;
-      case CTX_LINE_DASH:
-        if (parser->n_numbers)
-        {
-          ctx_line_dash (ctx, parser->numbers, parser->n_numbers);
-        }
-        else
-        {
-          ctx_line_dash (ctx, NULL, 0);
-        }
-        //append_dash_val (ctx, arg(0));
-        break;
-      case CTX_ARC_TO:
-        ctx_arc_to (ctx, arg (0), arg (1), arg (2), arg (3), arg (4) );
-        break;
-      case CTX_REL_ARC_TO:
-        ctx_rel_arc_to (ctx, arg (0), arg (1), arg (2), arg (3), arg (4) );
-        break;
-      case CTX_REL_SMOOTH_TO:
-        {
-          float cx = parser->pcx;
-          float cy = parser->pcy;
-          float ax = 2 * ctx_x (ctx) - cx;
-          float ay = 2 * ctx_y (ctx) - cy;
-          ctx_curve_to (ctx, ax, ay, arg (0) +  cx, arg (1) + cy,
-                        arg (2) + cx, arg (3) + cy);
-          parser->pcx = arg (0) + cx;
-          parser->pcy = arg (1) + cy;
-        }
-        break;
-      case CTX_SMOOTH_TO:
-        {
-          float ax = 2 * ctx_x (ctx) - parser->pcx;
-          float ay = 2 * ctx_y (ctx) - parser->pcy;
-          ctx_curve_to (ctx, ax, ay, arg (0), arg (1),
-                        arg (2), arg (3) );
-          parser->pcx = arg (0);
-          parser->pcx = arg (1);
-        }
-        break;
-      case CTX_SMOOTHQ_TO:
-        ctx_quad_to (ctx, parser->pcx, parser->pcy, arg (0), arg (1) );
-        break;
-      case CTX_REL_SMOOTHQ_TO:
-        {
-          float cx = parser->pcx;
-          float cy = parser->pcy;
-          parser->pcx = 2 * ctx_x (ctx) - parser->pcx;
-          parser->pcy = 2 * ctx_y (ctx) - parser->pcy;
-          ctx_quad_to (ctx, parser->pcx, parser->pcy, arg (0) +  cx, arg (1) + cy);
-        }
-        break;
-      case CTX_VER_LINE_TO:
-        ctx_line_to (ctx, ctx_x (ctx), arg (0) );
-        parser->command = CTX_VER_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_HOR_LINE_TO:
-        ctx_line_to (ctx, arg (0), ctx_y (ctx) );
-        parser->command = CTX_HOR_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_REL_HOR_LINE_TO:
-        ctx_rel_line_to (ctx, arg (0), 0.0f);
-        parser->command = CTX_REL_HOR_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_REL_VER_LINE_TO:
-        ctx_rel_line_to (ctx, 0.0f, arg (0) );
-        parser->command = CTX_REL_VER_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_ARC:
-        ctx_arc (ctx, arg (0), arg (1), arg (2), arg (3), arg (4), arg (5) );
-        break;
-      case CTX_APPLY_TRANSFORM:
-        ctx_apply_transform (ctx, arg (0), arg (1), arg (2), arg (3), arg (4), arg (5) );
-        break;
-      case CTX_CURVE_TO:
-        ctx_curve_to (ctx, arg (0), arg (1), arg (2), arg (3), arg (4), arg (5) );
-        parser->pcx = arg (2);
-        parser->pcy = arg (3);
-        parser->command = CTX_CURVE_TO;
-        break;
-      case CTX_REL_CURVE_TO:
-        parser->pcx = arg (2) + ctx_x (ctx);
-        parser->pcy = arg (3) + ctx_y (ctx);
-        ctx_rel_curve_to (ctx, arg (0), arg (1), arg (2), arg (3), arg (4), arg (5) );
-        parser->command = CTX_REL_CURVE_TO;
-        break;
-      case CTX_LINE_TO:
-        ctx_line_to (ctx, arg (0), arg (1) );
-        parser->command = CTX_LINE_TO;
-        parser->pcx = arg (0);
-        parser->pcy = arg (1);
-        break;
-      case CTX_MOVE_TO:
-        ctx_move_to (ctx, arg (0), arg (1) );
-        parser->command = CTX_LINE_TO;
-        parser->pcx = arg (0);
-        parser->pcy = arg (1);
-        parser->left_margin = parser->pcx;
-        break;
-      case CTX_FONT_SIZE:
-        ctx_font_size (ctx, arg (0) );
-        break;
-      case CTX_MITER_LIMIT:
-        ctx_miter_limit (ctx, arg (0) );
-        break;
-      case CTX_SCALE:
-        ctx_scale (ctx, arg (0), arg (1) );
-        break;
-      case CTX_QUAD_TO:
-        parser->pcx = arg (0);
-        parser->pcy = arg (1);
-        ctx_quad_to (ctx, arg (0), arg (1), arg (2), arg (3) );
-        parser->command = CTX_QUAD_TO;
-        break;
-      case CTX_REL_QUAD_TO:
-        parser->pcx = arg (0) + ctx_x (ctx);
-        parser->pcy = arg (1) + ctx_y (ctx);
-        ctx_rel_quad_to (ctx, arg (0), arg (1), arg (2), arg (3) );
-        parser->command = CTX_REL_QUAD_TO;
-        break;
-      case CTX_CLIP:
-        ctx_clip (ctx);
-        break;
-      case CTX_TRANSLATE:
-        ctx_translate (ctx, arg (0), arg (1) );
-        break;
-      case CTX_ROTATE:
-        ctx_rotate (ctx, arg (0) );
-        break;
-      case CTX_FONT:
-        ctx_font (ctx, (char *) parser->holding);
-        break;
+static int tap_and_hold_fire (Ctx *ctx, void *data)
+{
+  CtxGrab *grab = data;
+  CtxList *list = NULL;
+  ctx_list_prepend (&list, grab->item);
+  CtxEvent event = {0, };
 
-      case CTX_TEXT_STROKE:
-      case CTX_TEXT:
-        if (parser->n_numbers == 1)
-          { ctx_rel_move_to (ctx, -parser->numbers[0], 0.0); }  //  XXX : scale by font(size)
-        else
-          {
-            for (char *c = (char *) parser->holding; c; )
-              {
-                char *next_nl = ctx_strchr (c, '\n');
-                if (next_nl)
-                  { *next_nl = 0; }
-                /* do our own layouting on a per-word basis?, to get justified
-                 * margins? then we'd want explict margins rather than the
-                 * implicit ones from move_to's .. making move_to work within
-                 * margins.
-                 */
-                if (cmd == CTX_TEXT_STROKE)
-                  { ctx_text_stroke (ctx, c); }
-                else
-                  { ctx_text (ctx, c); }
-                if (next_nl)
-                  {
-                    *next_nl = '\n'; // swap it newline back in
-                    ctx_move_to (ctx, parser->left_margin, ctx_y (ctx) +
-                                 ctx_get_font_size (ctx) );
-                    c = next_nl + 1;
-                    if (c[0] == 0)
-                      { c = NULL; }
-                  }
-                else
-                  {
-                    c = NULL;
-                  }
-              }
-          }
-        if (cmd == CTX_TEXT_STROKE)
-          { parser->command = CTX_TEXT_STROKE; }
-        else
-          { parser->command = CTX_TEXT; }
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rel_line_to (ctx, arg(0), arg(1) );
-        parser->pcx += arg(0);
-        parser->pcy += arg(1);
-        break;
-      case CTX_REL_MOVE_TO:
-        ctx_rel_move_to (ctx, arg(0), arg(1) );
-        parser->pcx += arg(0);
-        parser->pcy += arg(1);
-        parser->left_margin = ctx_x (ctx);
-        break;
-      case CTX_LINE_WIDTH:
-        ctx_line_width (ctx, arg(0) );
-        break;
-      case CTX_SHADOW_COLOR:
-        ctx_shadow_rgba (ctx, arg(0), arg(1), arg(2), arg(3));
-        break;
-      case CTX_SHADOW_BLUR:
-        ctx_shadow_blur (ctx, arg(0) );
-        break;
-      case CTX_SHADOW_OFFSET_X:
-        ctx_shadow_offset_x (ctx, arg(0) );
-        break;
-      case CTX_SHADOW_OFFSET_Y:
-        ctx_shadow_offset_y (ctx, arg(0) );
-        break;
-      case CTX_LINE_JOIN:
-        ctx_line_join (ctx, (CtxLineJoin) arg(0) );
-        break;
-      case CTX_LINE_CAP:
-        ctx_line_cap (ctx, (CtxLineCap) arg(0) );
-        break;
-      case CTX_COMPOSITING_MODE:
-        ctx_compositing_mode (ctx, (CtxCompositingMode) arg(0) );
-        break;
-      case CTX_BLEND_MODE:
-        {
-          int blend_mode = arg(0);
-          if (blend_mode == CTX_COLOR) blend_mode = CTX_BLEND_COLOR;
-          ctx_blend_mode (ctx, (CtxBlend)blend_mode);
-        }
-        break;
-      case CTX_FILL_RULE:
-        ctx_fill_rule (ctx, (CtxFillRule) arg(0) );
-        break;
-      case CTX_TEXT_ALIGN:
-        ctx_text_align (ctx, (CtxTextAlign) arg(0) );
-        break;
-      case CTX_TEXT_BASELINE:
-        ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) );
-        break;
-      case CTX_TEXT_DIRECTION:
-        ctx_text_direction (ctx, (CtxTextDirection) arg(0) );
-        break;
-      case CTX_IDENTITY:
-        ctx_identity (ctx);
-        break;
-      case CTX_RECTANGLE:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_round_rectangle (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
-        break;
-      case CTX_VIEW_BOX:
-        ctx_view_box (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_LINEAR_GRADIENT:
-        ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_RADIAL_GRADIENT:
-        ctx_radial_gradient (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        break;
-      case CTX_GRADIENT_STOP:
-        {
-          float red, green, blue, alpha;
-          ctx_parser_get_color_rgba (parser, 1, &red, &green, &blue, &alpha);
-          ctx_gradient_add_stop (ctx, arg(0), red, green, blue, alpha);
-        }
-        break;
-      case CTX_GLOBAL_ALPHA:
-        ctx_global_alpha (ctx, arg(0) );
-        break;
-      case CTX_TEXTURE:
-        ctx_texture (ctx, arg(0), arg(1), arg(2));
-        break;
-      case CTX_BEGIN_PATH:
-        ctx_begin_path (ctx);
-        break;
-      case CTX_GLYPH:
-        ctx_glyph (ctx, arg(0), 0);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_close_path (ctx);
-        break;
-      case CTX_EXIT:
-        if (parser->exit)
-          { parser->exit (parser->exit_data);
-            return;
-          }
-        break;
-      case CTX_FLUSH:
-        //ctx_flush (ctx);
-        break;
-      case CTX_RESET:
-        ctx_reset (ctx);
-        ctx_translate (ctx,
-                       (parser->cursor_x-1) * parser->cell_width * 1.0,
-                       (parser->cursor_y-1) * parser->cell_height * 1.0);
-        break;
-    }
-#undef arg
-  parser->n_numbers = 0;
-}
+  event.ctx = ctx;
+  event.time = ctx_ms (ctx);
 
-static void ctx_parser_holding_append (CtxParser *parser, int byte)
-{
-  parser->holding[parser->pos++]=byte;
-  if (parser->pos > (int) sizeof (parser->holding)-2)
-    { parser->pos = sizeof (parser->holding)-2; }
-  parser->holding[parser->pos]=0;
+  event.device_x = 
+  event.x = ctx->events.pointer_x[grab->device_no];
+  event.device_y = 
+  event.y = ctx->events.pointer_y[grab->device_no];
+
+  // XXX: x and y coordinates
+  int ret = _ctx_emit_cb (ctx, list, &event, CTX_TAP_AND_HOLD,
+      ctx->events.pointer_x[grab->device_no], ctx->events.pointer_y[grab->device_no]);
+
+  ctx_list_free (&list);
+
+  grab->timeout_id = 0;
+
+  return 0;
+
+  return ret;
 }
 
-static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int arg_no, float *value)
+int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
+                      char *string)
 {
-  int big   = parser->width;
-  int small = parser->height;
-  if (big < small)
-    {
-      small = parser->width;
-      big   = parser->height;
-    }
-  switch (code)
+  CtxList *l;
+  CtxList *hitlist = NULL;
+
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+  if (device_no <= 3)
+  {
+    ctx->events.pointer_x[0] = x;
+    ctx->events.pointer_y[0] = y;
+  }
+
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &ctx->events.drag_event[device_no];
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  event->ctx = ctx;
+  event->x = x;
+  event->y = y;
+
+  event->delta_x = event->delta_y = 0;
+
+  event->device_no = device_no;
+  event->string    = string;
+  event->time      = time;
+  event->stop_propagate = 0;
+
+  _ctx_update_item (ctx, device_no, x, y, CTX_DROP, &hitlist);
+
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+    _ctx_emit_cb_item (ctx, item, event, CTX_DROP, x, y);
+
+    if (event->stop_propagate)
     {
-      case CTX_RADIAL_GRADIENT:
-      case CTX_ARC:
-        switch (arg_no)
-          {
-            case 0:
-            case 3:
-              *value *= (parser->width/100.0);
-              break;
-            case 1:
-            case 4:
-              *value *= (parser->height/100.0);
-              break;
-            case 2:
-            case 5:
-              *value *= small/100.0;
-              break;
-          }
-        break;
-      case CTX_FONT_SIZE:
-      case CTX_MITER_LIMIT:
-      case CTX_LINE_WIDTH:
-        {
-          *value *= (small/100.0);
-        }
-        break;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-        if (arg_no > 3)
-          {
-            *value *= (small/100.0);
-          }
-        else
-          {
-            if (arg_no % 2 == 0)
-              { *value  *= ( (parser->width) /100.0); }
-            else
-              { *value *= ( (parser->height) /100.0); }
-          }
-        break;
-      case CTX_ROUND_RECTANGLE:
-        if (arg_no == 4)
-        {
-          { *value *= ((parser->height)/100.0); }
-          return;
-        }
-        /* FALLTHROUGH */
-      default: // even means x coord
-        if (arg_no % 2 == 0)
-          { *value  *= ((parser->width)/100.0); }
-        else
-          { *value *= ((parser->height)/100.0); }
-        break;
+      ctx_list_free (&hitlist);
+      return 0;
     }
-}
+  }
 
-static void ctx_parser_transform_percent_height (CtxParser *parser, CtxCode code, int arg_no, float *value)
-{
-  *value *= (parser->height/100.0);
-}
+  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
 
-static void ctx_parser_transform_percent_width (CtxParser *parser, CtxCode code, int arg_no, float *value)
-{
-  *value *= (parser->height/100.0);
+  return 0;
 }
 
-static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_no, float *value)
+int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
 {
-  float small = parser->cell_width;
-  if (small > parser->cell_height)
-    { small = parser->cell_height; }
-  switch (code)
-    {
-      case CTX_RADIAL_GRADIENT:
-      case CTX_ARC:
-        switch (arg_no)
-          {
-            case 0:
-            case 3:
-              *value *= parser->cell_width;
-              break;
-            case 1:
-            case 4:
-              *value *= parser->cell_height;
-              break;
-            case 2:
-            case 5:
-              *value *= small; // use height?
-              break;
-          }
-        break;
-      case CTX_MITER_LIMIT:
-      case CTX_FONT_SIZE:
-      case CTX_LINE_WIDTH:
-        {
-          *value *= parser->cell_height;
-        }
-        break;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-        if (arg_no > 3)
-          {
-            *value *= small;
-          }
-        else
-          {
-            *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
-          }
-        break;
-      case CTX_RECTANGLE:
-        if (arg_no % 2 == 0)
-          { *value *= parser->cell_width; }
-        else
-          {
-            if (! (arg_no > 1) )
-              { (*value) -= 1.0f; }
-            *value *= parser->cell_height;
-          }
-        break;
-      default: // even means x coord odd means y coord
-        *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
-        break;
-    }
-}
+  CtxEvents *events = &ctx->events;
+  CtxList *hitlist = NULL;
+  events->pointer_x[device_no] = x;
+  events->pointer_y[device_no] = y;
+  if (device_no <= 3)
+  {
+    events->pointer_x[0] = x;
+    events->pointer_y[0] = y;
+  }
 
-// %h %v %m %M
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &events->drag_event[device_no];
 
-static void ctx_parser_word_done (CtxParser *parser)
-{
-  parser->holding[parser->pos]=0;
-  int old_args = parser->expected_args;
-  int command = ctx_parser_resolve_command (parser, parser->holding);
-  if ((command >= 0 && command < 32)
-      || (command > 150) || (command < 0)
-      )  // special case low enum values
-    {                   // and enum values too high to be
-                        // commands - permitting passing words
-                        // for strings in some cases
-      parser->numbers[parser->n_numbers] = command;
+  if (time == 0)
+    time = ctx_ms (ctx);
 
-      // trigger transition from number
-      parser->state = CTX_PARSER_NUMBER;
-      ctx_parser_feed_byte (parser, ',');
-    }
-  else if (command > 0)
+  event->x = event->start_x = event->prev_x = x;
+  event->y = event->start_y = event->prev_y = y;
+
+  event->delta_x = event->delta_y = 0;
+
+  event->device_no = device_no;
+  event->time      = time;
+  event->stop_propagate = 0;
+
+  if (events->pointer_down[device_no] == 1)
+  {
+    fprintf (stderr, "events thought device %i was already down\n", device_no);
+  }
+  /* doing just one of these two should be enough? */
+  events->pointer_down[device_no] = 1;
+  switch (device_no)
+  {
+    case 1:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON1;
+      break;
+    case 2:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON2;
+      break;
+    case 3:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON3;
+      break;
+    default:
+      break;
+  }
+
+  CtxGrab *grab = NULL;
+  CtxList *l;
+
+  _ctx_update_item (ctx, device_no, x, y, 
+      CTX_PRESS | CTX_DRAG_PRESS | CTX_TAP | CTX_TAP_AND_HOLD, &hitlist);
+
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+    if (item &&
+        ((item->types & CTX_DRAG)||
+         (item->types & CTX_TAP) ||
+         (item->types & CTX_TAP_AND_HOLD)))
     {
-      if (old_args == CTX_ARG_COLLECT_NUMBERS)
+      grab = device_add_grab (ctx, device_no, item, item->types);
+      grab->start_time = time;
+
+      if (item->types & CTX_TAP_AND_HOLD)
       {
-        int tmp1 = parser->command;
-        int tmp2 = parser->expected_args;
-        ctx_parser_dispatch_command (parser);
-        parser->command = (CtxCode)tmp1;
-        parser->expected_args = tmp2;
+         grab->timeout_id = ctx_add_timeout (ctx, events->tap_delay_hold, tap_and_hold_fire, grab);
       }
-
-      parser->command = (CtxCode) command;
-      if (parser->expected_args == 0)
-        {
-          ctx_parser_dispatch_command (parser);
-        }
     }
-  else
+    _ctx_emit_cb_item (ctx, item, event, CTX_PRESS, x, y);
+    if (!event->stop_propagate)
+      _ctx_emit_cb_item (ctx, item, event, CTX_DRAG_PRESS, x, y);
+
+    if (event->stop_propagate)
     {
-      /* interpret char by char */
-      uint8_t buf[16]=" ";
-      for (int i = 0; parser->pos && parser->holding[i] > ' '; i++)
-        {
-          buf[0] = parser->holding[i];
-          parser->command = (CtxCode) ctx_parser_resolve_command (parser, buf);
-          if (parser->command > 0)
-            {
-              if (parser->expected_args == 0)
-                {
-                  ctx_parser_dispatch_command (parser);
-                }
-            }
-          else
-            {
-              ctx_log ("unhandled command '%c'\n", buf[0]);
-            }
-        }
+      ctx_list_free (&hitlist);
+      return 0;
     }
-  parser->n_numbers = 0;
-  parser->n_args = 0;
+  }
+
+  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+  return 0;
 }
 
-static void ctx_parser_string_done (CtxParser *parser)
+void _ctx_resized (Ctx *ctx, int width, int height, long time)
 {
-  ctx_parser_dispatch_command (parser);
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
+  CtxEvent event = {0, };
+
+  if (!time)
+    time = ctx_ms (ctx);
+  
+  event.ctx = ctx;
+  event.time = time;
+  event.string = "resize-event"; /* gets delivered to clients as a key_down event, maybe message shouldbe 
used instead?
+   */
+
+  if (item)
+  {
+    event.stop_propagate = 0;
+    _ctx_emit_cb_item (ctx, item, &event, CTX_KEY_PRESS, 0, 0);
+  }
+
 }
 
-void ctx_parser_feed_byte (CtxParser *parser, int byte)
+int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
 {
-  switch (byte)
-    {
-      case '\n':
-        parser->col=0;
-        parser->line++;
-        break;
-      default:
-        parser->col++;
-    }
-  switch (parser->state)
+  CtxEvents *events = &ctx->events;
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &events->drag_event[device_no];
+
+  event->time = time;
+  event->x = x;
+  event->ctx = ctx;
+  event->y = y;
+  event->device_no = device_no;
+  event->stop_propagate = 0;
+
+  switch (device_no)
+  {
+    case 1:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON1)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON1;
+      break;
+    case 2:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON2)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON2;
+      break;
+    case 3:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON3)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON3;
+      break;
+    default:
+      break;
+  }
+
+  //events_queue_draw (mrg, NULL); /* in case of style change */
+
+  if (events->pointer_down[device_no] == 0)
+  {
+    fprintf (stderr, "device %i already up\n", device_no);
+  }
+  events->pointer_down[device_no] = 0;
+
+  events->pointer_x[device_no] = x;
+  events->pointer_y[device_no] = y;
+  if (device_no <= 3)
+  {
+    events->pointer_x[0] = x;
+    events->pointer_y[0] = y;
+  }
+  CtxList *hitlist = NULL;
+  CtxList *grablist = NULL , *g= NULL;
+  CtxGrab *grab;
+
+  _ctx_update_item (ctx, device_no, x, y, CTX_RELEASE | CTX_DRAG_RELEASE, &hitlist);
+  grablist = device_get_grabs (ctx, device_no);
+
+  for (g = grablist; g; g = g->next)
+  {
+    grab = g->data;
+
+    if (!event->stop_propagate)
     {
-      case CTX_PARSER_NEUTRAL:
-        switch (byte)
-          {
-            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 11: case 12: case 
14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 
26: case 27: case 28: case 29: case 30: case 31:
-              break;
-            case ' ': case '\t': case '\r': case '\n':
-            case ';': case ',':
-            case '(': case ')':
-            case '{': case '}':
-            case '=':
+      if (grab->item->types & CTX_TAP)
+      {
+        long delay = time - grab->start_time;
+
+        if (delay > events->tap_delay_min &&
+            delay < events->tap_delay_max &&
+            (
+              (event->start_x - x) * (event->start_x - x) +
+              (event->start_y - y) * (event->start_y - y)) < ctx_pow2(events->tap_hysteresis)
+            )
+        {
+          _ctx_emit_cb_item (ctx, grab->item, event, CTX_TAP, x, y);
+        }
+      }
+
+      if (!event->stop_propagate && grab->item->types & CTX_DRAG_RELEASE)
+      {
+        _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_RELEASE, x, y);
+      }
+    }
+
+    device_remove_grab (ctx, grab);
+  }
+
+  if (hitlist)
+  {
+    if (!event->stop_propagate)
+      _ctx_emit_cb (ctx, hitlist, event, CTX_RELEASE, x, y);
+    ctx_list_free (&hitlist);
+  }
+  ctx_list_free (&grablist);
+  return 0;
+}
+
+/*  for multi-touch, we use a list of active grabs - thus a grab corresponds to
+ *  a device id. even during drag-grabs events propagate; to stop that stop
+ *  propagation.
+ */
+int ctx_pointer_motion (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+{
+  CtxList *hitlist = NULL;
+  CtxList *grablist = NULL, *g;
+  CtxGrab *grab;
+
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &ctx->events.drag_event[device_no];
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  event->ctx       = ctx;
+  event->x         = x;
+  event->y         = y;
+  event->time      = time;
+  event->device_no = device_no;
+  event->stop_propagate = 0;
+  
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+
+  if (device_no <= 3)
+  {
+    ctx->events.pointer_x[0] = x;
+    ctx->events.pointer_y[0] = y;
+  }
+
+  grablist = device_get_grabs (ctx, device_no);
+  _ctx_update_item (ctx, device_no, x, y, CTX_MOTION, &hitlist);
+
+  {
+    CtxItem  *cursor_item = _ctx_detect (ctx, x, y, CTX_SET_CURSOR);
+    if (cursor_item)
+    {
+      ctx_set_cursor (ctx, cursor_item->cursor);
+    }
+    else
+    {
+      ctx_set_cursor (ctx, CTX_CURSOR_ARROW);
+    }
+    CtxItem  *hovered_item = _ctx_detect (ctx, x, y, CTX_ANY);
+    static CtxItem *prev_hovered_item = NULL;
+    if (prev_hovered_item != hovered_item)
+    {
+      ctx_set_dirty (ctx, 1);
+    }
+    prev_hovered_item = hovered_item;
+  }
+
+  event->delta_x = x - event->prev_x;
+  event->delta_y = y - event->prev_y;
+  event->prev_x  = x;
+  event->prev_y  = y;
+
+  CtxList *remove_grabs = NULL;
+
+  for (g = grablist; g; g = g->next)
+  {
+    grab = g->data;
+
+    if ((grab->type & CTX_TAP) ||
+        (grab->type & CTX_TAP_AND_HOLD))
+    {
+      if (
+          (
+            (event->start_x - x) * (event->start_x - x) +
+            (event->start_y - y) * (event->start_y - y)) >
+              ctx_pow2(ctx->events.tap_hysteresis)
+         )
+      {
+        //fprintf (stderr, "-");
+        ctx_list_prepend (&remove_grabs, grab);
+      }
+      else
+      {
+        //fprintf (stderr, ":");
+      }
+    }
+
+    if (grab->type & CTX_DRAG_MOTION)
+    {
+      _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_MOTION, x, y);
+      if (event->stop_propagate)
+        break;
+    }
+  }
+  if (remove_grabs)
+  {
+    for (g = remove_grabs; g; g = g->next)
+      device_remove_grab (ctx, g->data);
+    ctx_list_free (&remove_grabs);
+  }
+  if (hitlist)
+  {
+    if (!event->stop_propagate)
+      _ctx_emit_cb (ctx, hitlist, event, CTX_MOTION, x, y);
+    ctx_list_free (&hitlist);
+  }
+  ctx_list_free (&grablist);
+  return 0;
+}
+
+void ctx_incoming_message (Ctx *ctx, const char *message, long time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_MESSAGE);
+  CtxEvent event = {0, };
+
+  if (!time)
+    time = ctx_ms (ctx);
+
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_MESSAGE;
+    event.time = time;
+    event.string = message;
+
+    fprintf (stderr, "{%s|\n", message);
+
+      for (i = 0; i < item->cb_count; i++)
+      {
+        if (item->cb[i].types & (CTX_MESSAGE))
+        {
+          event.state = ctx->events.modifier_state;
+          item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+          if (event.stop_propagate)
+            return;// event.stop_propagate;
+        }
+      }
+  }
+}
+
+int ctx_scrolled (Ctx *ctx, float x, float y, CtxScrollDirection scroll_direction, uint32_t time)
+{
+  CtxList *hitlist = NULL;
+  CtxList *l;
+
+  int device_no = 0;
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+
+  CtxEvent *event = &ctx->events.drag_event[device_no];  /* XXX: might
+                                       conflict with other code
+                                       create a sibling member
+                                       of drag_event?*/
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  event->x         = event->start_x = event->prev_x = x;
+  event->y         = event->start_y = event->prev_y = y;
+  event->delta_x   = event->delta_y = 0;
+  event->device_no = device_no;
+  event->time      = time;
+  event->stop_propagate = 0;
+  event->scroll_direction = scroll_direction;
+
+  _ctx_update_item (ctx, device_no, x, y, CTX_SCROLL, &hitlist);
+
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+
+    _ctx_emit_cb_item (ctx, item, event, CTX_SCROLL, x, y);
+
+    if (event->stop_propagate)
+      l = NULL;
+  }
+
+  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+  return 0;
+}
+
+static int ctx_str_has_prefix (const char *string, const char *prefix)
+{
+  for (int i = 0; prefix[i]; i++)
+  {
+    if (!string[i]) return 0;
+    if (string[i] != prefix[i]) return 0;
+  }
+  return 0;
+}
+
+int ctx_key_press (Ctx *ctx, unsigned int keyval,
+                   const char *string, uint32_t time)
+{
+  char event_type[128]="";
+  float x, y; int b;
+  sscanf (string, "%s %f %f %i", event_type, &x, &y, &b);
+  if (!strcmp (event_type, "mouse-motion") ||
+      !strcmp (event_type, "mouse-drag"))
+    return ctx_pointer_motion (ctx, x, y, b, 0);
+  else if (!strcmp (event_type, "mouse-press"))
+    return ctx_pointer_press (ctx, x, y, b, 0);
+  else if (!strcmp (event_type, "mouse-release"))
+    return ctx_pointer_release (ctx, x, y, b, 0);
+  //else if (!strcmp (event_type, "keydown"))
+  //  return ctx_key_down (ctx, keyval, string + 8, time);
+  //else if (!strcmp (event_type, "keyup"))
+  //  return ctx_key_up (ctx, keyval, string + 6, time);
+
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
+  CtxEvent event = {0,};
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_KEY_PRESS;
+    event.unicode = keyval; 
+    event.string = strdup(string);
+    event.stop_propagate = 0;
+    event.time = time;
+
+    for (i = 0; i < item->cb_count; i++)
+    {
+      if (item->cb[i].types & (CTX_KEY_PRESS))
+      {
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
+      }
+    }
+    free ((void*)event.string);
+  }
+  return 0;
+}
+
+int ctx_key_down (Ctx *ctx, unsigned int keyval,
+                  const char *string, uint32_t time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_DOWN);
+  CtxEvent event = {0,};
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx     = ctx;
+    event.type    = CTX_KEY_DOWN;
+    event.unicode = keyval; 
+    event.string  = strdup(string);
+    event.stop_propagate = 0;
+    event.time    = time;
+
+    for (i = 0; i < item->cb_count; i++)
+    {
+      if (item->cb[i].types & (CTX_KEY_DOWN))
+      {
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
+      }
+    }
+    free ((void*)event.string);
+  }
+  return 0;
+}
+
+int ctx_key_up (Ctx *ctx, unsigned int keyval,
+                const char *string, uint32_t time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_UP);
+  CtxEvent event = {0,};
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_KEY_UP;
+    event.unicode = keyval; 
+    event.string = strdup(string);
+    event.stop_propagate = 0;
+    event.time = time;
+
+    for (i = 0; i < item->cb_count; i++)
+    {
+      if (item->cb[i].types & (CTX_KEY_UP))
+      {
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
+      }
+    }
+    free ((void*)event.string);
+  }
+  return 0;
+
+  return 0;
+}
+
+void ctx_freeze           (Ctx *ctx)
+{
+  ctx->events.frozen ++;
+}
+
+void ctx_thaw             (Ctx *ctx)
+{
+  ctx->events.frozen --;
+}
+int ctx_events_frozen (Ctx *ctx)
+{
+  return ctx && ctx->events.frozen;
+}
+void ctx_events_clear_items (Ctx *ctx)
+{
+  ctx_list_free (&ctx->events.items);
+}
+int ctx_events_width (Ctx *ctx)
+{
+  return ctx->events.width;
+}
+int ctx_events_height (Ctx *ctx)
+{
+  return ctx->events.height;
+}
+
+float ctx_pointer_x (Ctx *ctx)
+{
+  return ctx->events.pointer_x[0];
+}
+
+float ctx_pointer_y (Ctx *ctx)
+{
+  return ctx->events.pointer_y[0];
+}
+
+int ctx_pointer_is_down (Ctx *ctx, int no)
+{
+  if (no < 0 || no > CTX_MAX_DEVICES) return 0;
+  return ctx->events.pointer_down[no];
+}
+
+void _ctx_debug_overlays (Ctx *ctx)
+{
+  CtxList *a;
+  ctx_save (ctx);
+
+  ctx_line_width (ctx, 2);
+  ctx_rgba (ctx, 0,0,0.8,0.5);
+  for (a = ctx->events.items; a; a = a->next)
+  {
+    float current_x = ctx_pointer_x (ctx);
+    float current_y = ctx_pointer_y (ctx);
+    CtxItem *item = a->data;
+    CtxMatrix matrix = item->inv_matrix;
+
+    ctx_matrix_apply_transform (&matrix, &current_x, &current_y);
+
+    if (current_x >= item->x0 && current_x < item->x1 &&
+        current_y >= item->y0 && current_y < item->y1)
+    {
+      ctx_matrix_invert (&matrix);
+      ctx_set_matrix (ctx, &matrix);
+      _mrg_restore_path (ctx, item->path);
+      ctx_stroke (ctx);
+    }
+  }
+  ctx_restore (ctx);
+}
+
+void ctx_set_render_threads   (Ctx *ctx, int n_threads)
+{
+  // XXX
+}
+int ctx_get_render_threads   (Ctx *ctx)
+{
+  return _ctx_max_threads;
+}
+void ctx_set_hash_cache (Ctx *ctx, int enable_hash_cache)
+{
+  _ctx_enable_hash_cache = enable_hash_cache;
+}
+int ctx_get_hash_cache (Ctx *ctx)
+{
+  return _ctx_enable_hash_cache;
+}
+
+int ctx_is_dirty (Ctx *ctx)
+{
+  return ctx->dirty;
+}
+void ctx_set_dirty (Ctx *ctx, int dirty)
+{
+  ctx->dirty = dirty;
+}
+
+/*
+ * centralized global API for managing file descriptors that
+ * wake us up, this to remove sleeping and polling
+ */
+
+#define CTX_MAX_LISTEN_FDS 8
+static int _ctx_listen_fd[CTX_MAX_LISTEN_FDS];
+static int _ctx_listen_fds = 0;
+static int _ctx_listen_max_fd = 0;
+
+void _ctx_add_listen_fd (int fd)
+{
+  _ctx_listen_fd[_ctx_listen_fds++]=fd;
+  if (fd > _ctx_listen_max_fd)
+    _ctx_listen_max_fd = fd;
+}
+
+void _ctx_remove_listen_fd (int fd)
+{
+  for (int i = 0; i < _ctx_listen_fds; i++)
+  {
+    if (_ctx_listen_fd[i] == fd)
+    {
+      _ctx_listen_fd[i] = _ctx_listen_fd[_ctx_listen_fds-1];
+      _ctx_listen_fds--;
+      return;
+    }
+  }
+}
+
+int _ctx_data_pending (int timeout)
+{
+  struct timeval tv;
+  fd_set fdset;
+  FD_ZERO (&fdset);
+  for (int i = 0; i < _ctx_listen_fds; i++)
+  {
+    FD_SET (_ctx_listen_fd[i], &fdset);
+  }
+  tv.tv_sec = 0;
+  tv.tv_usec = timeout;
+  tv.tv_sec = timeout / 1000000;
+  tv.tv_usec = timeout % 1000000;
+  int retval = select (_ctx_listen_max_fd, &fdset, NULL, NULL, &tv);
+  if (retval == -1)
+  {
+    perror ("select");
+    return 0;
+  }
+  return retval;
+}
+
+void ctx_sdl_set_title (void *self, const char *new_title);
+void ctx_set_title (Ctx *ctx, const char *title)
+{
+#if CTX_SDL
+     // XXX also check we're first/only client?
+   if (ctx_renderer_is_sdl (ctx))
+     ctx_sdl_set_title (ctx_get_renderer (ctx), title);
+#endif
+}
+
+#endif
+/* the parser comes in the end, nothing in ctx knows about the parser  */
+
+#if CTX_PARSER
+
+/* ctx parser, */
+
+#define CTX_ID_MAXLEN 64 // in use should not be more than 40!
+                         // to offer headroom for multiplexing
+
+
+struct
+  _CtxParser
+{
+  Ctx       *ctx;
+  int        t_args; // total number of arguments seen for current command
+  int        state;
+#if CTX_PARSER_FIXED_TEMP
+  uint8_t    holding[CTX_PARSER_MAXLEN]; /*  */
+#else
+  uint8_t   *holding;
+#endif
+  int        hold_len;
+  int        pos;
+
+
+  int        line; /*  for error reporting */
+  int        col;  /*  for error reporting */
+  float      numbers[CTX_PARSER_MAX_ARGS+1];
+  int        n_numbers;
+  int        decimal;
+  CtxCode    command;
+  int        expected_args; /* low digits are literal higher values
+                               carry special meaning */
+  int        n_args;
+  int        texture_done;
+  uint8_t    texture_id[CTX_ID_MAXLEN]; // used in defineTexture only
+  uint64_t   set_key_hash;
+  float      pcx;
+  float      pcy;
+  int        color_components;
+  int        color_stroke; // 0 is fill source  1 is stroke source
+  int        color_model; // 1 gray 3 rgb 4 cmyk
+  float      left_margin; // set by last user provided move_to
+  int        width;       // <- maybe should be float
+  int        height;
+  float      cell_width;
+  float      cell_height;
+  int        cursor_x;    // <- leaking in from terminal
+  int        cursor_y;
+
+  int        color_space_slot;
+
+  void (*exit) (void *exit_data);
+  void *exit_data;
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len);
+  int   (*get_prop)(void *prop_data, const char *key, char **data, int *len);
+  void *prop_data;
+};
+
+void
+ctx_parser_set_size (CtxParser *parser,
+                 int        width,
+                 int        height,
+                 float      cell_width,
+                 float      cell_height)
+{
+  if (cell_width > 0)
+    parser->cell_width       = cell_width;
+  if (cell_height > 0)
+    parser->cell_height      = cell_height;
+  if (width > 0)
+    parser->width            = width;
+  if (height > 0)
+    parser->height           = height;
+}
+
+static CtxParser *
+ctx_parser_init (CtxParser *parser,
+                 Ctx       *ctx,
+                 int        width,
+                 int        height,
+                 float      cell_width,
+                 float      cell_height,
+                 int        cursor_x,
+                 int        cursor_y,
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
+                 void  *prop_data,
+                 void (*exit) (void *exit_data),
+                 void *exit_data
+                )
+{
+  ctx_memset (parser, 0, sizeof (CtxParser) );
+  parser->line             = 1;
+  parser->ctx              = ctx;
+  parser->cell_width       = cell_width;
+  parser->cell_height      = cell_height;
+  parser->cursor_x         = cursor_x;
+  parser->cursor_y         = cursor_y;
+  parser->width            = width;
+  parser->height           = height;
+  parser->exit             = exit;
+  parser->exit_data        = exit_data;
+  parser->color_model      = CTX_RGBA;
+  parser->color_stroke     = 0;
+  parser->color_components = 4;
+  parser->command          = CTX_MOVE_TO;
+  parser->set_prop         = set_prop;
+  parser->get_prop         = get_prop;
+  parser->prop_data        = prop_data;
+  return parser;
+}
+
+CtxParser *ctx_parser_new (
+  Ctx       *ctx,
+  int        width,
+  int        height,
+  float      cell_width,
+  float      cell_height,
+  int        cursor_x,
+  int        cursor_y,
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
+  void  *prop_data,
+  void (*exit) (void *exit_data),
+  void *exit_data)
+{
+  return ctx_parser_init ( (CtxParser *) ctx_calloc (sizeof (CtxParser), 1),
+                           ctx,
+                           width, height,
+                           cell_width, cell_height,
+                           cursor_x, cursor_y, set_prop, get_prop, prop_data,
+                           exit, exit_data);
+}
+
+void ctx_parser_free (CtxParser *parser)
+{
+#if !CTX_PARSER_FIXED_TEMP
+  if (parser->holding)
+    free (parser->holding);
+#endif
+  free (parser);
+}
+
+#define CTX_ARG_COLLECT_NUMBERS             50
+#define CTX_ARG_STRING_OR_NUMBER            100
+#define CTX_ARG_NUMBER_OF_COMPONENTS        200
+#define CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1 201
+
+static int ctx_arguments_for_code (CtxCode code)
+{
+  switch (code)
+    {
+      case CTX_SAVE:
+      case CTX_START_GROUP:
+      case CTX_END_GROUP:
+      case CTX_IDENTITY:
+      case CTX_CLOSE_PATH:
+      case CTX_BEGIN_PATH:
+      case CTX_RESET:
+      case CTX_FLUSH:
+      case CTX_RESTORE:
+      case CTX_STROKE:
+      case CTX_FILL:
+      case CTX_NEW_PAGE:
+      case CTX_CLIP:
+      case CTX_EXIT:
+        return 0;
+      case CTX_GLOBAL_ALPHA:
+      case CTX_COMPOSITING_MODE:
+      case CTX_BLEND_MODE:
+      case CTX_FONT_SIZE:
+      case CTX_LINE_JOIN:
+      case CTX_LINE_CAP:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+      case CTX_IMAGE_SMOOTHING:
+      case CTX_SHADOW_BLUR:
+      case CTX_SHADOW_OFFSET_X:
+      case CTX_SHADOW_OFFSET_Y:
+      case CTX_FILL_RULE:
+      case CTX_TEXT_ALIGN:
+      case CTX_TEXT_BASELINE:
+      case CTX_TEXT_DIRECTION:
+      case CTX_MITER_LIMIT:
+      case CTX_REL_VER_LINE_TO:
+      case CTX_REL_HOR_LINE_TO:
+      case CTX_HOR_LINE_TO:
+      case CTX_VER_LINE_TO:
+      case CTX_FONT:
+      case CTX_ROTATE:
+      case CTX_GLYPH:
+        return 1;
+      case CTX_TRANSLATE:
+      case CTX_REL_SMOOTHQ_TO:
+      case CTX_LINE_TO:
+      case CTX_MOVE_TO:
+      case CTX_SCALE:
+      case CTX_REL_LINE_TO:
+      case CTX_REL_MOVE_TO:
+      case CTX_SMOOTHQ_TO:
+        return 2;
+      case CTX_LINEAR_GRADIENT:
+      case CTX_REL_QUAD_TO:
+      case CTX_QUAD_TO:
+      case CTX_RECTANGLE:
+      case CTX_FILL_RECT:
+      case CTX_STROKE_RECT:
+      case CTX_REL_SMOOTH_TO:
+      case CTX_VIEW_BOX:
+      case CTX_SMOOTH_TO:
+        return 4;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+      case CTX_ROUND_RECTANGLE:
+        return 5;
+      case CTX_ARC:
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_APPLY_TRANSFORM:
+      case CTX_RADIAL_GRADIENT:
+        return 6;
+      case CTX_STROKE_TEXT:
+      case CTX_TEXT:
+      case CTX_COLOR_SPACE:
+      case CTX_DEFINE_GLYPH:
+      case CTX_KERNING_PAIR:
+      case CTX_TEXTURE:
+      case CTX_DEFINE_TEXTURE:
+        return CTX_ARG_STRING_OR_NUMBER;
+      case CTX_LINE_DASH: /* append to current dashes for each argument encountered */
+        return CTX_ARG_COLLECT_NUMBERS;
+      //case CTX_SET_KEY:
+      case CTX_COLOR:
+      case CTX_SHADOW_COLOR:
+        return CTX_ARG_NUMBER_OF_COMPONENTS;
+      case CTX_GRADIENT_STOP:
+        return CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1;
+
+        default:
+#if 1
+        case CTX_SET_RGBA_U8:
+        case CTX_NOP:
+        case CTX_NEW_EDGE:
+        case CTX_EDGE:
+        case CTX_EDGE_FLIPPED:
+        case CTX_CONT:
+        case CTX_DATA:
+        case CTX_DATA_REV:
+        case CTX_SET_PIXEL:
+        case CTX_REL_LINE_TO_X4:
+        case CTX_REL_LINE_TO_REL_CURVE_TO:
+        case CTX_REL_CURVE_TO_REL_LINE_TO:
+        case CTX_REL_CURVE_TO_REL_MOVE_TO:
+        case CTX_REL_LINE_TO_X2:
+        case CTX_MOVE_TO_REL_LINE_TO:
+        case CTX_REL_LINE_TO_REL_MOVE_TO:
+        case CTX_FILL_MOVE_TO:
+        case CTX_REL_QUAD_TO_REL_QUAD_TO:
+        case CTX_REL_QUAD_TO_S16:
+#endif
+        return 0;
+    }
+}
+
+static int ctx_parser_set_command (CtxParser *parser, CtxCode code)
+{
+  if (code < 150 && code >= 32)
+  {
+  parser->expected_args = ctx_arguments_for_code (code);
+  parser->n_args = 0;
+  parser->texture_done = 0;
+  if (parser->expected_args >= CTX_ARG_NUMBER_OF_COMPONENTS)
+    {
+      parser->expected_args = (parser->expected_args % 100) + parser->color_components;
+    }
+  }
+  return code;
+}
+
+static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke);
+
+static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str)
+{
+  uint64_t ret = str[0]; /* if it is single char it already is the CtxCode */
+
+  /* this is handled outside the hashing to make it possible to be case insensitive
+   * with the rest.
+   */
+  if (str[0] == CTX_SET_KEY && str[1] && str[2] == 0)
+  {
+    switch (str[1])
+    {
+      case 'm': return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
+      case 'B': return ctx_parser_set_command (parser, CTX_BLEND_MODE);
+      case 'l': return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
+      case 't': return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
+      case 'b': return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
+      case 'd': return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
+      case 'j': return ctx_parser_set_command (parser, CTX_LINE_JOIN);
+      case 'c': return ctx_parser_set_command (parser, CTX_LINE_CAP);
+      case 'w': return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
+      case 'D': return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
+      case 'S': return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
+      case 'C': return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
+      case 's': return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
+      case 'x': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
+      case 'y': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
+      case 'a': return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
+      case 'f': return ctx_parser_set_command (parser, CTX_FONT_SIZE);
+      case 'r': return ctx_parser_set_command (parser, CTX_FILL_RULE);
+    }
+  }
+
+  if (str[0] && str[1])
+    {
+      uint64_t str_hash;
+      /* trim ctx_ and CTX_ prefix */
+      if ( (str[0] == 'c' && str[1] == 't' && str[2] == 'x' && str[3] == '_') ||
+           (str[0] == 'C' && str[1] == 'T' && str[2] == 'X' && str[3] == '_') )
+        {
+          str += 4;
+        }
+      if ( (str[0] == 's' && str[1] == 'e' && str[2] == 't' && str[3] == '_') )
+        { str += 4; }
+      str_hash = ctx_strhash ( (char *) str, 0);
+      switch (str_hash)
+        {
+          /* first a list of mappings to one_char hashes, handled in a
+           * separate fast path switch without hashing
+           */
+          case CTX_arcTo:          ret = CTX_ARC_TO; break;
+          case CTX_arc:            ret = CTX_ARC; break;
+          case CTX_curveTo:        ret = CTX_CURVE_TO; break;
+          case CTX_restore:        ret = CTX_RESTORE; break;
+          case CTX_stroke:         ret = CTX_STROKE; break;
+          case CTX_fill:           ret = CTX_FILL; break;
+          case CTX_flush:          ret = CTX_FLUSH; break;
+          case CTX_horLineTo:      ret = CTX_HOR_LINE_TO; break;
+          case CTX_rotate:         ret = CTX_ROTATE; break;
+          case CTX_color:          ret = CTX_COLOR; break;
+          case CTX_lineTo:         ret = CTX_LINE_TO; break;
+          case CTX_moveTo:         ret = CTX_MOVE_TO; break;
+          case CTX_scale:          ret = CTX_SCALE; break;
+          case CTX_newPage:        ret = CTX_NEW_PAGE; break;
+          case CTX_quadTo:         ret = CTX_QUAD_TO; break;
+          case CTX_viewBox:        ret = CTX_VIEW_BOX; break;
+          case CTX_smooth_to:      ret = CTX_SMOOTH_TO; break;
+          case CTX_smooth_quad_to: ret = CTX_SMOOTHQ_TO; break;
+          case CTX_clear:          ret = CTX_COMPOSITE_CLEAR; break;
+          case CTX_copy:           ret = CTX_COMPOSITE_COPY; break;
+          case CTX_destinationOver:  ret = CTX_COMPOSITE_DESTINATION_OVER; break;
+          case CTX_destinationIn:    ret = CTX_COMPOSITE_DESTINATION_IN; break;
+          case CTX_destinationOut:   ret = CTX_COMPOSITE_DESTINATION_OUT; break;
+          case CTX_sourceOver:       ret = CTX_COMPOSITE_SOURCE_OVER; break;
+          case CTX_sourceAtop:       ret = CTX_COMPOSITE_SOURCE_ATOP; break;
+          case CTX_destinationAtop:  ret = CTX_COMPOSITE_DESTINATION_ATOP; break;
+          case CTX_sourceOut:        ret = CTX_COMPOSITE_SOURCE_OUT; break;
+          case CTX_sourceIn:         ret = CTX_COMPOSITE_SOURCE_IN; break;
+          case CTX_xor:              ret = CTX_COMPOSITE_XOR; break;
+          case CTX_darken:           ret = CTX_BLEND_DARKEN; break;
+          case CTX_lighten:          ret = CTX_BLEND_LIGHTEN; break;
+          //case CTX_color:          ret = CTX_BLEND_COLOR; break;
+          //
+          //  XXX check that he special casing for color works
+          //      it is the first collision and it is due to our own
+          //      color, not w3c for now unique use of it
+          //
+          case CTX_hue:            ret = CTX_BLEND_HUE; break;
+          case CTX_multiply:       ret = CTX_BLEND_MULTIPLY; break;
+          case CTX_normal:         ret = CTX_BLEND_NORMAL;break;
+          case CTX_screen:         ret = CTX_BLEND_SCREEN;break;
+          case CTX_difference:     ret = CTX_BLEND_DIFFERENCE; break;
+          case CTX_reset:          ret = CTX_RESET; break;
+          case CTX_verLineTo:      ret = CTX_VER_LINE_TO; break;
+          case CTX_exit:
+          case CTX_done:           ret = CTX_EXIT; break;
+          case CTX_closePath:      ret = CTX_CLOSE_PATH; break;
+          case CTX_beginPath:
+          case CTX_newPath:        ret = CTX_BEGIN_PATH; break;
+          case CTX_relArcTo:       ret = CTX_REL_ARC_TO; break;
+          case CTX_clip:           ret = CTX_CLIP; break;
+          case CTX_relCurveTo:     ret = CTX_REL_CURVE_TO; break;
+          case CTX_startGroup:     ret = CTX_START_GROUP; break;
+          case CTX_endGroup:       ret = CTX_END_GROUP; break;
+          case CTX_save:           ret = CTX_SAVE; break;
+          case CTX_translate:      ret = CTX_TRANSLATE; break;
+          case CTX_linearGradient: ret = CTX_LINEAR_GRADIENT; break;
+          case CTX_relHorLineTo:   ret = CTX_REL_HOR_LINE_TO; break;
+          case CTX_relLineTo:      ret = CTX_REL_LINE_TO; break;
+          case CTX_relMoveTo:      ret = CTX_REL_MOVE_TO; break;
+          case CTX_font:           ret = CTX_FONT; break;
+          case CTX_radialGradient:ret = CTX_RADIAL_GRADIENT; break;
+          case CTX_gradientAddStop:
+          case CTX_addStop:        ret = CTX_GRADIENT_STOP; break;
+          case CTX_relQuadTo:      ret = CTX_REL_QUAD_TO; break;
+          case CTX_rectangle:
+          case CTX_rect:           ret = CTX_RECTANGLE; break;
+          case CTX_roundRectangle: ret = CTX_ROUND_RECTANGLE; break;
+          case CTX_relSmoothTo:    ret = CTX_REL_SMOOTH_TO; break;
+          case CTX_relSmoothqTo:   ret = CTX_REL_SMOOTHQ_TO; break;
+          case CTX_strokeText:     ret = CTX_STROKE_TEXT; break;
+          case CTX_strokeRect:     ret = CTX_STROKE_RECT; break;
+          case CTX_fillRect:       ret = CTX_FILL_RECT; break;
+          case CTX_relVerLineTo:   ret = CTX_REL_VER_LINE_TO; break;
+          case CTX_text:           ret = CTX_TEXT; break;
+          case CTX_identity:       ret = CTX_IDENTITY; break;
+          case CTX_transform:      ret = CTX_APPLY_TRANSFORM; break;
+          case CTX_texture:        ret = CTX_TEXTURE; break;
+          case CTX_defineTexture:  ret = CTX_DEFINE_TEXTURE; break;
+#if 0
+          case CTX_rgbSpace:
+            return ctx_parser_set_command (parser, CTX_SET_RGB_SPACE);
+          case CTX_cmykSpace:
+            return ctx_parser_set_command (parser, CTX_SET_CMYK_SPACE);
+          case CTX_drgbSpace:
+            return ctx_parser_set_command (parser, CTX_SET_DRGB_SPACE);
+#endif
+          case CTX_defineGlyph:
+            return ctx_parser_set_command (parser, CTX_DEFINE_GLYPH);
+          case CTX_kerningPair:
+            return ctx_parser_set_command (parser, CTX_KERNING_PAIR);
+
+          case CTX_colorSpace:
+            return ctx_parser_set_command (parser, CTX_COLOR_SPACE);
+          case CTX_fillRule:
+            return ctx_parser_set_command (parser, CTX_FILL_RULE);
+          case CTX_fontSize:
+          case CTX_setFontSize:
+            return ctx_parser_set_command (parser, CTX_FONT_SIZE);
+          case CTX_compositingMode:
+            return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
+
+          case CTX_blend:
+          case CTX_blending:
+          case CTX_blendMode:
+            return ctx_parser_set_command (parser, CTX_BLEND_MODE);
+
+          case CTX_miterLimit:
+            return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
+          case CTX_textAlign:
+            return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
+          case CTX_textBaseline:
+            return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
+          case CTX_textDirection:
+            return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
+          case CTX_join:
+          case CTX_lineJoin:
+          case CTX_setLineJoin:
+            return ctx_parser_set_command (parser, CTX_LINE_JOIN);
+          case CTX_glyph:
+            return ctx_parser_set_command (parser, CTX_GLYPH);
+          case CTX_cap:
+          case CTX_lineCap:
+          case CTX_setLineCap:
+            return ctx_parser_set_command (parser, CTX_LINE_CAP);
+          case CTX_lineDash:
+            return ctx_parser_set_command (parser, CTX_LINE_DASH);
+          case CTX_lineWidth:
+          case CTX_setLineWidth:
+            return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
+          case CTX_lineDashOffset:
+            return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
+          case CTX_imageSmoothing:
+            return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
+          case CTX_shadowColor:
+            return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
+          case CTX_shadowBlur:
+            return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
+          case CTX_shadowOffsetX:
+            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
+          case CTX_shadowOffsetY:
+            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
+          case CTX_globalAlpha:
+            return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
+
+          /* strings are handled directly here,
+           * instead of in the one-char handler, using return instead of break
+           */
+          case CTX_gray:
+            ctx_parser_set_color_model (parser, CTX_GRAY, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_graya:
+            ctx_parser_set_color_model (parser, CTX_GRAYA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgb:
+            ctx_parser_set_color_model (parser, CTX_RGB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgb:
+            ctx_parser_set_color_model (parser, CTX_DRGB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgba:
+            ctx_parser_set_color_model (parser, CTX_RGBA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgba:
+            ctx_parser_set_color_model (parser, CTX_DRGBA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmyk:
+            ctx_parser_set_color_model (parser, CTX_CMYK, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmyka:
+            ctx_parser_set_color_model (parser, CTX_CMYKA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lab:
+            ctx_parser_set_color_model (parser, CTX_LAB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_laba:
+            ctx_parser_set_color_model (parser, CTX_LABA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lch:
+            ctx_parser_set_color_model (parser, CTX_LCH, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lcha:
+            ctx_parser_set_color_model (parser, CTX_LCHA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+
+          /* and a full repeat of the above, with S for Stroke suffix */
+          case CTX_grayS:
+            ctx_parser_set_color_model (parser, CTX_GRAY, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_grayaS:
+            ctx_parser_set_color_model (parser, CTX_GRAYA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgbS:
+            ctx_parser_set_color_model (parser, CTX_RGB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgbS:
+            ctx_parser_set_color_model (parser, CTX_DRGB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgbaS:
+            ctx_parser_set_color_model (parser, CTX_RGBA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgbaS:
+            ctx_parser_set_color_model (parser, CTX_DRGBA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmykS:
+            ctx_parser_set_color_model (parser, CTX_CMYK, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmykaS:
+            ctx_parser_set_color_model (parser, CTX_CMYKA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_labS:
+            ctx_parser_set_color_model (parser, CTX_LAB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_labaS:
+            ctx_parser_set_color_model (parser, CTX_LABA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lchS:
+            ctx_parser_set_color_model (parser, CTX_LCH, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lchaS:
+            ctx_parser_set_color_model (parser, CTX_LCHA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+
+          /* words that correspond to low integer constants
+          */
+          case CTX_winding:     return CTX_FILL_RULE_WINDING;
+          case CTX_evenOdd:
+          case CTX_even_odd:    return CTX_FILL_RULE_EVEN_ODD;
+          case CTX_bevel:       return CTX_JOIN_BEVEL;
+          case CTX_round:       return CTX_JOIN_ROUND;
+          case CTX_miter:       return CTX_JOIN_MITER;
+          case CTX_none:        return CTX_CAP_NONE;
+          case CTX_square:      return CTX_CAP_SQUARE;
+          case CTX_start:       return CTX_TEXT_ALIGN_START;
+          case CTX_end:         return CTX_TEXT_ALIGN_END;
+          case CTX_left:        return CTX_TEXT_ALIGN_LEFT;
+          case CTX_right:       return CTX_TEXT_ALIGN_RIGHT;
+          case CTX_center:      return CTX_TEXT_ALIGN_CENTER;
+          case CTX_top:         return CTX_TEXT_BASELINE_TOP;
+          case CTX_bottom :     return CTX_TEXT_BASELINE_BOTTOM;
+          case CTX_middle:      return CTX_TEXT_BASELINE_MIDDLE;
+          case CTX_alphabetic:  return CTX_TEXT_BASELINE_ALPHABETIC;
+          case CTX_hanging:     return CTX_TEXT_BASELINE_HANGING;
+          case CTX_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC;
+
+          case CTX_userRGB:     return CTX_COLOR_SPACE_USER_RGB;
+          case CTX_deviceRGB:   return CTX_COLOR_SPACE_DEVICE_RGB;
+          case CTX_userCMYK:    return CTX_COLOR_SPACE_USER_CMYK;
+          case CTX_deviceCMYK:  return CTX_COLOR_SPACE_DEVICE_CMYK;
+#undef STR
+#undef LOWER
+          default:
+            ret = str_hash;
+        }
+    }
+  if (ret == CTX_CLOSE_PATH2)
+    { ret = CTX_CLOSE_PATH; }
+  /* handling single char, and ret = foo; break;  in cases above*/
+  return ctx_parser_set_command (parser, (CtxCode) ret);
+}
+
+enum
+{
+  CTX_PARSER_NEUTRAL = 0,
+  CTX_PARSER_NUMBER,
+  CTX_PARSER_NEGATIVE_NUMBER,
+  CTX_PARSER_WORD,
+  CTX_PARSER_COMMENT,
+  CTX_PARSER_STRING_APOS,
+  CTX_PARSER_STRING_QUOT,
+  CTX_PARSER_STRING_APOS_ESCAPED,
+  CTX_PARSER_STRING_QUOT_ESCAPED,
+  CTX_PARSER_STRING_A85,
+} CTX_STATE;
+
+static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke)
+{
+  parser->color_model      = color_model;
+  parser->color_stroke     = stroke;
+  parser->color_components = ctx_color_model_get_components (color_model);
+}
+
+static void ctx_parser_get_color_rgba (CtxParser *parser, int offset, float *red, float *green, float *blue, 
float *alpha)
+{
+  /* XXX - this function is to be deprecated */
+  *alpha = 1.0;
+  switch (parser->color_model)
+    {
+      case CTX_GRAYA:
+        *alpha = parser->numbers[offset + 1];
+        /* FALLTHROUGH */
+      case CTX_GRAY:
+        *red = *green = *blue = parser->numbers[offset + 0];
+        break;
+      default:
+      case CTX_LABA: // NYI - needs RGB profile
+      case CTX_LCHA: // NYI - needs RGB profile
+      case CTX_RGBA:
+        *alpha = parser->numbers[offset + 3];
+        /* FALLTHROUGH */
+      case CTX_LAB: // NYI
+      case CTX_LCH: // NYI
+      case CTX_RGB:
+        *red = parser->numbers[offset + 0];
+        *green = parser->numbers[offset + 1];
+        *blue = parser->numbers[offset + 2];
+        break;
+      case CTX_CMYKA:
+        *alpha = parser->numbers[offset + 4];
+        /* FALLTHROUGH */
+      case CTX_CMYK:
+        /* should use profile instead  */
+        *red = (1.0-parser->numbers[offset + 0]) *
+               (1.0 - parser->numbers[offset + 3]);
+        *green = (1.0-parser->numbers[offset + 1]) *
+                 (1.0 - parser->numbers[offset + 3]);
+        *blue = (1.0-parser->numbers[offset + 2]) *
+                (1.0 - parser->numbers[offset + 3]);
+        break;
+    }
+}
+
+static void ctx_parser_dispatch_command (CtxParser *parser)
+{
+  CtxCode cmd = parser->command;
+  Ctx *ctx = parser->ctx;
+#if 1
+  if (parser->expected_args != CTX_ARG_STRING_OR_NUMBER &&
+      parser->expected_args != CTX_ARG_COLLECT_NUMBERS &&
+      parser->expected_args != parser->n_numbers)
+    {
+      if (0)
+         fprintf (stderr, "ctx:%i:%i %c got %i instead of %i args\n",
+               parser->line, parser->col,
+               cmd, parser->n_numbers, parser->expected_args);
+    }
+#endif
+
+#define arg(a)  (parser->numbers[a])
+  parser->command = CTX_NOP;
+  //parser->n_args = 0;
+  switch (cmd)
+    {
+      default:
+        break; // to silence warnings about missing ones
+      case CTX_PRESERVE:
+        ctx_preserve (ctx);
+        break;
+      case CTX_FILL:
+        ctx_fill (ctx);
+        break;
+      case CTX_SAVE:
+        ctx_save (ctx);
+        break;
+      case CTX_START_GROUP:
+        ctx_start_group (ctx);
+        break;
+      case CTX_END_GROUP:
+        ctx_end_group (ctx);
+        break;
+      case CTX_STROKE:
+        ctx_stroke (ctx);
+        break;
+      case CTX_RESTORE:
+        ctx_restore (ctx);
+        break;
+#if CTX_ENABLE_CM
+      case CTX_COLOR_SPACE:
+        if (parser->n_numbers == 1)
+        {
+          parser->color_space_slot = arg(0);
+          parser->command = CTX_COLOR_SPACE; // did this work without?
+        }
+        else
+        {
+          ctx_colorspace (ctx, parser->color_space_slot,
+                               parser->holding, parser->pos);
+        }
+        break;
+#endif
+      case CTX_KERNING_PAIR:
+        switch (parser->n_args)
+        {
+          case 0:
+            parser->numbers[0] = ctx_utf8_to_unichar ((char*)parser->holding);
+            break;
+          case 1:
+            parser->numbers[1] = ctx_utf8_to_unichar ((char*)parser->holding);
+            break;
+          case 2:
+            parser->numbers[2] = strtod ((char*)parser->holding, NULL);
+            {
+              CtxEntry e = {CTX_KERNING_PAIR, };
+              e.data.u16[0] = parser->numbers[0];
+              e.data.u16[1] = parser->numbers[1];
+              e.data.s32[1] = parser->numbers[2] * 256;
+              ctx_process (ctx, &e);
+            }
+            break;
+        }
+        parser->command = CTX_KERNING_PAIR;
+        parser->n_args ++; // make this more generic?
+        break;             
+      case CTX_TEXTURE:
+        if (parser->texture_done)
+        {
+        }
+        else
+        if (parser->n_numbers == 2)
+        {
+          const char *eid = (char*)parser->holding;
+          float x0 = arg(0);
+          float x1 = arg(1);
+          ctx_texture (ctx, eid, x0, x1);
+          parser->texture_done = 1;
+        }
+        parser->command = CTX_TEXTURE;
+        //parser->n_args++;
+        break;
+      case CTX_DEFINE_TEXTURE:
+        if (parser->texture_done)
+        {
+          if (parser->texture_done++ == 1)
+          {
+             const char *eid = (char*)parser->texture_id;
+             int width  = arg(0);
+             int height = arg(1);
+             int format = arg(2);
+             int stride = ctx_pixel_format_get_stride (format, width);
+
+
+             if (parser->pos != stride * height)
+             {
+             fprintf (stderr, "unexpected datasize for define texture %s %ix%i\n size:%i != expected:%i - 
start of data: %i %i %i %i\n", eid, width, height,
+                               parser->pos,
+                               stride * height,
+                               parser->holding[0],
+                               parser->holding[1],
+                               parser->holding[2],
+                               parser->holding[3]
+                               );
+             }
+             else
+             ctx_define_texture (ctx, eid, width, height, stride, format, parser->holding, NULL);
+          }
+        }
+        else
+        {
+        switch (parser->n_numbers)
+        {
+          case 0:
+             strncpy ((char*)parser->texture_id, (char*)parser->holding, sizeof(parser->texture_id));
+             parser->texture_id[sizeof(parser->texture_id)-1]=0;
+             break;
+          case 1:
+          case 2:
+             break;
+          case 3:
+             parser->texture_done = 1;
+             break;
+          default:
+             fprintf (stderr, "!!%i\n", parser->n_numbers);
+             break;
+        }
+        }
+        parser->command = CTX_DEFINE_TEXTURE;
+        break;
+
+
+      case CTX_DEFINE_GLYPH:
+        /* XXX : reuse n_args logic - to enforce order */
+        if (parser->n_numbers == 1)
+        {
+          CtxEntry e = {CTX_DEFINE_GLYPH, };
+          e.data.u32[0] = parser->color_space_slot;
+          e.data.u32[1] = arg(0) * 256;
+          ctx_process (ctx, &e);
+        }
+        else
+        {
+          int unichar = ctx_utf8_to_unichar ((char*)parser->holding);
+          parser->color_space_slot = unichar;
+        }
+        parser->command = CTX_DEFINE_GLYPH;
+        break;             
+
+      case CTX_COLOR:
+        {
+          switch (parser->color_model)
+            {
+              case CTX_GRAY:
+              case CTX_GRAYA:
+              case CTX_RGB:
+              case CTX_RGBA:
+              case CTX_DRGB:
+              case CTX_DRGBA:
+                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
+                break;
+#if CTX_ENABLE_CMYK
+              case CTX_CMYK:
+              case CTX_CMYKA:
+                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
+                break;
+#else
+              /* when there is no cmyk support at all in rasterizer
+               * do a naive mapping to RGB on input.
+               */
+              case CTX_CMYK:
+              case CTX_CMYKA:
+                {
+                  float rgba[4] = {1,1,1,1.0f};
+
+                  ctx_cmyk_to_rgb (arg(0), arg(1), arg(2), arg(3), &rgba[0], &rgba[1], &rgba[2]);
+                  if (parser->color_model == CTX_CMYKA)
+                    { rgba[3] = arg(4); }
+                  ctx_color_raw (ctx, CTX_RGBA, rgba, parser->color_stroke);
+                }
+                break;
+#endif
+            }
+        }
+        break;
+      case CTX_LINE_DASH:
+        if (parser->n_numbers)
+        {
+          ctx_line_dash (ctx, parser->numbers, parser->n_numbers);
+        }
+        else
+        {
+          ctx_line_dash (ctx, NULL, 0);
+        }
+        //append_dash_val (ctx, arg(0));
+        break;
+      case CTX_ARC_TO:
+        ctx_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+        break;
+      case CTX_REL_ARC_TO:
+        ctx_rel_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4) );
+        break;
+      case CTX_REL_SMOOTH_TO:
+        {
+          float cx = parser->pcx;
+          float cy = parser->pcy;
+          float ax = 2 * ctx_x (ctx) - cx;
+          float ay = 2 * ctx_y (ctx) - cy;
+          ctx_curve_to (ctx, ax, ay, arg(0) +  cx, arg(1) + cy,
+                        arg(2) + cx, arg(3) + cy);
+          parser->pcx = arg(0) + cx;
+          parser->pcy = arg(1) + cy;
+        }
+        break;
+      case CTX_SMOOTH_TO:
+        {
+          float ax = 2 * ctx_x (ctx) - parser->pcx;
+          float ay = 2 * ctx_y (ctx) - parser->pcy;
+          ctx_curve_to (ctx, ax, ay, arg(0), arg(1),
+                        arg(2), arg(3) );
+          parser->pcx = arg(0);
+          parser->pcx = arg(1);
+        }
+        break;
+      case CTX_SMOOTHQ_TO:
+        ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0), arg(1) );
+        break;
+      case CTX_REL_SMOOTHQ_TO:
+        {
+          float cx = parser->pcx;
+          float cy = parser->pcy;
+          parser->pcx = 2 * ctx_x (ctx) - parser->pcx;
+          parser->pcy = 2 * ctx_y (ctx) - parser->pcy;
+          ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0) +  cx, arg(1) + cy);
+        }
+        break;
+      case CTX_VER_LINE_TO:
+        ctx_line_to (ctx, ctx_x (ctx), arg(0) );
+        parser->command = CTX_VER_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_HOR_LINE_TO:
+        ctx_line_to (ctx, arg(0), ctx_y (ctx) );
+        parser->command = CTX_HOR_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_REL_HOR_LINE_TO:
+        ctx_rel_line_to (ctx, arg(0), 0.0f);
+        parser->command = CTX_REL_HOR_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_REL_VER_LINE_TO:
+        ctx_rel_line_to (ctx, 0.0f, arg(0) );
+        parser->command = CTX_REL_VER_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_ARC:
+        ctx_arc (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_APPLY_TRANSFORM:
+        ctx_apply_transform (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_CURVE_TO:
+        ctx_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        parser->pcx = arg(2);
+        parser->pcy = arg(3);
+        parser->command = CTX_CURVE_TO;
+        break;
+      case CTX_REL_CURVE_TO:
+        parser->pcx = arg(2) + ctx_x (ctx);
+        parser->pcy = arg(3) + ctx_y (ctx);
+        ctx_rel_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        parser->command = CTX_REL_CURVE_TO;
+        break;
+      case CTX_LINE_TO:
+        ctx_line_to (ctx, arg(0), arg(1) );
+        parser->command = CTX_LINE_TO;
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        break;
+      case CTX_MOVE_TO:
+        ctx_move_to (ctx, arg(0), arg(1) );
+        parser->command = CTX_LINE_TO;
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        parser->left_margin = parser->pcx;
+        break;
+      case CTX_FONT_SIZE:
+        ctx_font_size (ctx, arg(0) );
+        break;
+      case CTX_MITER_LIMIT:
+        ctx_miter_limit (ctx, arg(0) );
+        break;
+      case CTX_SCALE:
+        ctx_scale (ctx, arg(0), arg(1) );
+        break;
+      case CTX_QUAD_TO:
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        ctx_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
+        parser->command = CTX_QUAD_TO;
+        break;
+      case CTX_REL_QUAD_TO:
+        parser->pcx = arg(0) + ctx_x (ctx);
+        parser->pcy = arg(1) + ctx_y (ctx);
+        ctx_rel_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
+        parser->command = CTX_REL_QUAD_TO;
+        break;
+      case CTX_CLIP:
+        ctx_clip (ctx);
+        break;
+      case CTX_TRANSLATE:
+        ctx_translate (ctx, arg(0), arg(1) );
+        break;
+      case CTX_ROTATE:
+        ctx_rotate (ctx, arg(0) );
+        break;
+      case CTX_FONT:
+        ctx_font (ctx, (char *) parser->holding);
+        break;
+
+      case CTX_STROKE_TEXT:
+      case CTX_TEXT:
+        if (parser->n_numbers == 1)
+          { ctx_rel_move_to (ctx, -parser->numbers[0], 0.0); }  //  XXX : scale by font(size)
+        else
+          {
+            for (char *c = (char *) parser->holding; c; )
+              {
+                char *next_nl = ctx_strchr (c, '\n');
+                if (next_nl)
+                  { *next_nl = 0; }
+                /* do our own layouting on a per-word basis?, to get justified
+                 * margins? then we'd want explict margins rather than the
+                 * implicit ones from move_to's .. making move_to work within
+                 * margins.
+                 */
+                if (cmd == CTX_STROKE_TEXT)
+                  { ctx_text_stroke (ctx, c); }
+                else
+                  { ctx_text (ctx, c); }
+                if (next_nl)
+                  {
+                    *next_nl = '\n'; // swap it newline back in
+                    ctx_move_to (ctx, parser->left_margin, ctx_y (ctx) +
+                                 ctx_get_font_size (ctx) );
+                    c = next_nl + 1;
+                    if (c[0] == 0)
+                      { c = NULL; }
+                  }
+                else
+                  {
+                    c = NULL;
+                  }
+              }
+          }
+        if (cmd == CTX_STROKE_TEXT)
+          { parser->command = CTX_STROKE_TEXT; }
+        else
+          { parser->command = CTX_TEXT; }
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rel_line_to (ctx, arg(0), arg(1) );
+        parser->pcx += arg(0);
+        parser->pcy += arg(1);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rel_move_to (ctx, arg(0), arg(1) );
+        parser->pcx += arg(0);
+        parser->pcy += arg(1);
+        parser->left_margin = ctx_x (ctx);
+        break;
+      case CTX_LINE_WIDTH:
+        ctx_line_width (ctx, arg(0));
+        break;
+      case CTX_LINE_DASH_OFFSET:
+        ctx_line_dash_offset (ctx, arg(0));
+        break;
+      case CTX_IMAGE_SMOOTHING:
+        ctx_image_smoothing (ctx, arg(0));
+        break;
+      case CTX_SHADOW_COLOR:
+        ctx_shadow_rgba (ctx, arg(0), arg(1), arg(2), arg(3));
+        break;
+      case CTX_SHADOW_BLUR:
+        ctx_shadow_blur (ctx, arg(0) );
+        break;
+      case CTX_SHADOW_OFFSET_X:
+        ctx_shadow_offset_x (ctx, arg(0) );
+        break;
+      case CTX_SHADOW_OFFSET_Y:
+        ctx_shadow_offset_y (ctx, arg(0) );
+        break;
+      case CTX_LINE_JOIN:
+        ctx_line_join (ctx, (CtxLineJoin) arg(0) );
+        break;
+      case CTX_LINE_CAP:
+        ctx_line_cap (ctx, (CtxLineCap) arg(0) );
+        break;
+      case CTX_COMPOSITING_MODE:
+        ctx_compositing_mode (ctx, (CtxCompositingMode) arg(0) );
+        break;
+      case CTX_BLEND_MODE:
+        {
+          int blend_mode = arg(0);
+          if (blend_mode == CTX_COLOR) blend_mode = CTX_BLEND_COLOR;
+          ctx_blend_mode (ctx, (CtxBlend)blend_mode);
+        }
+        break;
+      case CTX_FILL_RULE:
+        ctx_fill_rule (ctx, (CtxFillRule) arg(0) );
+        break;
+      case CTX_TEXT_ALIGN:
+        ctx_text_align (ctx, (CtxTextAlign) arg(0) );
+        break;
+      case CTX_TEXT_BASELINE:
+        ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) );
+        break;
+      case CTX_TEXT_DIRECTION:
+        ctx_text_direction (ctx, (CtxTextDirection) arg(0) );
+        break;
+      case CTX_IDENTITY:
+        ctx_identity (ctx);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_FILL_RECT:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        ctx_fill (ctx);
+        break;
+      case CTX_STROKE_RECT:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        ctx_stroke (ctx);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_round_rectangle (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+        break;
+      case CTX_VIEW_BOX:
+        ctx_view_box (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_LINEAR_GRADIENT:
+        ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_RADIAL_GRADIENT:
+        ctx_radial_gradient (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_GRADIENT_STOP:
+        {
+          float red, green, blue, alpha;
+          ctx_parser_get_color_rgba (parser, 1, &red, &green, &blue, &alpha);
+          ctx_gradient_add_stop (ctx, arg(0), red, green, blue, alpha);
+        }
+        break;
+      case CTX_GLOBAL_ALPHA:
+        ctx_global_alpha (ctx, arg(0) );
+        break;
+      case CTX_BEGIN_PATH:
+        ctx_begin_path (ctx);
+        break;
+      case CTX_GLYPH:
+        ctx_glyph (ctx, arg(0), 0);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_close_path (ctx);
+        break;
+      case CTX_EXIT:
+        if (parser->exit)
+          { parser->exit (parser->exit_data);
+            return;
+          }
+        break;
+      case CTX_FLUSH:
+        //ctx_flush (ctx);
+        break;
+      case CTX_RESET:
+        ctx_reset (ctx);
+        ctx_translate (ctx,
+                       (parser->cursor_x-1) * parser->cell_width * 1.0,
+                       (parser->cursor_y-1) * parser->cell_height * 1.0);
+        break;
+    }
+#undef arg
+//  parser->n_numbers = 0;
+}
+
+static void ctx_parser_holding_append (CtxParser *parser, int byte)
+{
+#if !CTX_PARSER_FIXED_TEMP
+  if (parser->hold_len < parser->pos + 1 + 1)
+  {
+    int new_len = parser->hold_len * 1.5;
+    if (new_len < 512) new_len = 512;
+    parser->holding = realloc (parser->holding, new_len);
+    parser->hold_len = new_len;
+  }
+#endif
+
+  parser->holding[parser->pos++]=byte;
+#if CTX_PARSER_FIXED_TEMP
+  if (parser->pos > (int) sizeof (parser->holding)-2)
+    { parser->pos = sizeof (parser->holding)-2; }
+#endif
+  parser->holding[parser->pos]=0;
+}
+
+static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int arg_no, float *value)
+{
+  int big   = parser->width;
+  int small = parser->height;
+  if (big < small)
+    {
+      small = parser->width;
+      big   = parser->height;
+    }
+  switch (code)
+    {
+      case CTX_RADIAL_GRADIENT:
+      case CTX_ARC:
+        switch (arg_no)
+          {
+            case 0:
+            case 3:
+              *value *= (parser->width/100.0);
+              break;
+            case 1:
+            case 4:
+              *value *= (parser->height/100.0);
+              break;
+            case 2:
+            case 5:
+              *value *= small/100.0;
+              break;
+          }
+        break;
+      case CTX_FONT_SIZE:
+      case CTX_MITER_LIMIT:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+        {
+          *value *= (small/100.0);
+        }
+        break;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+        if (arg_no > 3)
+          {
+            *value *= (small/100.0);
+          }
+        else
+          {
+            if (arg_no % 2 == 0)
+              { *value  *= ( (parser->width) /100.0); }
+            else
+              { *value *= ( (parser->height) /100.0); }
+          }
+        break;
+      case CTX_ROUND_RECTANGLE:
+        if (arg_no == 4)
+        {
+          { *value *= ((parser->height)/100.0); }
+          return;
+        }
+        /* FALLTHROUGH */
+      default: // even means x coord
+        if (arg_no % 2 == 0)
+          { *value  *= ((parser->width)/100.0); }
+        else
+          { *value *= ((parser->height)/100.0); }
+        break;
+    }
+}
+
+static void ctx_parser_transform_percent_height (CtxParser *parser, CtxCode code, int arg_no, float *value)
+{
+  *value *= (parser->height/100.0);
+}
+
+static void ctx_parser_transform_percent_width (CtxParser *parser, CtxCode code, int arg_no, float *value)
+{
+  *value *= (parser->height/100.0);
+}
+
+static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_no, float *value)
+{
+  float small = parser->cell_width;
+  if (small > parser->cell_height)
+    { small = parser->cell_height; }
+  switch (code)
+    {
+      case CTX_RADIAL_GRADIENT:
+      case CTX_ARC:
+        switch (arg_no)
+          {
+            case 0:
+            case 3:
+              *value *= parser->cell_width;
+              break;
+            case 1:
+            case 4:
+              *value *= parser->cell_height;
+              break;
+            case 2:
+            case 5:
+              *value *= small; // use height?
+              break;
+          }
+        break;
+      case CTX_MITER_LIMIT:
+      case CTX_FONT_SIZE:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+        {
+          *value *= parser->cell_height;
+        }
+        break;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+        if (arg_no > 3)
+          {
+            *value *= small;
+          }
+        else
+          {
+            *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
+          }
+        break;
+      case CTX_RECTANGLE:
+        if (arg_no % 2 == 0)
+          { *value *= parser->cell_width; }
+        else
+          {
+            if (! (arg_no > 1) )
+              { (*value) -= 1.0f; }
+            *value *= parser->cell_height;
+          }
+        break;
+      default: // even means x coord odd means y coord
+        *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
+        break;
+    }
+}
+
+// %h %v %m %M
+
+static void ctx_parser_number_done (CtxParser *parser)
+{
+
+}
+
+static void ctx_parser_word_done (CtxParser *parser)
+{
+  parser->holding[parser->pos]=0;
+  //int old_args = parser->expected_args;
+  int command = ctx_parser_resolve_command (parser, parser->holding);
+  if ((command >= 0 && command < 32)
+      || (command > 150) || (command < 0)
+      )  // special case low enum values
+    {                   // and enum values too high to be
+                        // commands - permitting passing words
+                        // for strings in some cases
+      parser->numbers[parser->n_numbers] = command;
+
+      // trigger transition from number
+      parser->state = CTX_PARSER_NUMBER;
+      ctx_parser_feed_byte (parser, ',');
+    }
+  else if (command > 0)
+    {
+#if 0
+      if (old_args == CTX_ARG_COLLECT_NUMBERS ||
+          old_args == CTX_ARG_STRING_OR_NUMBER)
+      {
+        int tmp1 = parser->command;
+        int tmp2 = parser->expected_args;
+        int tmp3 = parser->n_numbers;
+ //     int tmp4 = parser->n_args;
+        ctx_parser_dispatch_command (parser);
+        parser->command = (CtxCode)tmp1;
+        parser->expected_args = tmp2;
+        parser->n_numbers = tmp3;
+ //     parser->n_args = tmp4;
+      }
+#endif
+
+      parser->command = (CtxCode) command;
+      parser->n_numbers = 0;
+      parser->n_args = 0;
+      if (parser->expected_args == 0)
+        {
+          ctx_parser_dispatch_command (parser);
+        }
+    }
+  else
+    {
+      /* interpret char by char */
+      uint8_t buf[16]=" ";
+      for (int i = 0; parser->pos && parser->holding[i] > ' '; i++)
+        {
+          buf[0] = parser->holding[i];
+          parser->command = (CtxCode) ctx_parser_resolve_command (parser, buf);
+          parser->n_numbers = 0;
+          parser->n_args = 0;
+          if (parser->command > 0)
+            {
+              if (parser->expected_args == 0)
+                {
+                  ctx_parser_dispatch_command (parser);
+                }
+            }
+          else
+            {
+              ctx_log ("unhandled command '%c'\n", buf[0]);
+            }
+        }
+    }
+}
+
+static void ctx_parser_string_done (CtxParser *parser)
+{
+  if (parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
+  {
+          /*
+    if (parser->state != CTX_PARSER_NUMBER &&
+        parser->state != CTX_PARSER_NEGATIVE_NUMBER &&
+        parser->state != CTX_PARSER_STRING_A85 &&
+        parser->state != CTX_PARSER_STRING_APOS &&
+        parser->state != CTX_PARSER_STRING_QUOT
+        )
+        */
+    {
+    int tmp1 = parser->command;
+    int tmp2 = parser->expected_args;
+    int tmp3 = parser->n_numbers;
+    int tmp4 = parser->n_args;
+    ctx_parser_dispatch_command (parser);
+    parser->command = (CtxCode)tmp1;
+    parser->expected_args = tmp2;
+    parser->n_numbers = tmp3;
+    parser->n_args = tmp4;
+    }
+  }
+  else
+  {
+    ctx_parser_dispatch_command (parser);
+  }
+}
+
+void ctx_parser_feed_byte (CtxParser *parser, int byte)
+{
+  switch (byte)
+    {
+      case '\n':
+        parser->col=0;
+        parser->line++;
+        break;
+      default:
+        parser->col++;
+    }
+  switch (parser->state)
+    {
+      case CTX_PARSER_NEUTRAL:
+        switch (byte)
+          {
+            case  0: case  1: case  2: case  3:  case 4:  case 5:
+            case  6: case  7: case  8: case 11: case 12: case 14:
+            case 15: case 16: case 17: case 18: case 19: case 20:
+            case 21: case 22: case 23: case 24: case 25: case 26:
+            case 27: case 28: case 29: case 30: case 31:
+              break;
+            case ' ': case '\t': case '\r': case '\n':
+            case ';': case ',':
+            case '(': case ')':
+            case '{': case '}':
+            case '=':
               break;
             case '#':
               parser->state = CTX_PARSER_COMMENT;
@@ -16043,35 +18003,12 @@ void ctx_parser_feed_byte (CtxParser *parser, int byte)
         {
           switch (byte)
             {
-              case 0:
-              case 1:
-              case 2:
-              case 3:
-              case 4:
-              case 5:
-              case 6:
-              case 7:
-              case 8:
-              case 11:
-              case 12:
-              case 14:
-              case 15:
-              case 16:
-              case 17:
-              case 18:
-              case 19:
-              case 20:
-              case 21:
-              case 22:
-              case 23:
-              case 24:
-              case 25:
-              case 26:
-              case 27:
-              case 28:
-              case 29:
-              case 30:
-              case 31:
+              case 0: case 1: case 2: case 3: case 4: case 5:
+              case 6: case 7: case 8:
+              case 11: case 12: case 14: case 15: case 16:
+              case 17: case 18: case 19: case 20: case 21:
+              case 22: case 23: case 24: case 25: case 26:
+              case 27: case 28: case 29: case 30: case 31:
                 parser->state = CTX_PARSER_NEUTRAL;
                 break;
               case ' ':
@@ -16104,16 +18041,8 @@ void ctx_parser_feed_byte (CtxParser *parser, int byte)
                 //if (parser->decimal) // TODO permit .13.32.43 to equivalent to .12 .32 .43
                 parser->decimal = 1;
                 break;
-              case '0':
-              case '1':
-              case '2':
-              case '3':
-              case '4':
-              case '5':
-              case '6':
-              case '7':
-              case '8':
-              case '9':
+              case '0': case '1': case '2': case '3': case '4':
+              case '5': case '6': case '7': case '8': case '9':
                 if (parser->decimal)
                   {
                     parser->decimal *= 10;
@@ -16177,11 +18106,21 @@ void ctx_parser_feed_byte (CtxParser *parser, int byte)
                (parser->state != CTX_PARSER_NEGATIVE_NUMBER))
             {
               parser->n_numbers ++;
-              //parser->t_args ++;
+              ctx_parser_number_done (parser);
+
               if (parser->n_numbers == parser->expected_args ||
-                  parser->expected_args == 100)
+                  parser->expected_args == CTX_ARG_COLLECT_NUMBERS ||
+                  parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
                 {
+                  int tmp1 = parser->n_numbers;
+                  //int tmp2 = parser->n_args;
+                  int tmp3 = parser->command;
+                  int tmp4 = parser->expected_args;
                   ctx_parser_dispatch_command (parser);
+                  parser->n_numbers = tmp1;
+                  //parser->n_args = tmp2;
+                  parser->command = tmp3;
+                  parser->expected_args = tmp4;
                 }
               if (parser->n_numbers > CTX_PARSER_MAX_ARGS)
                 { parser->n_numbers = CTX_PARSER_MAX_ARGS;
@@ -16233,8 +18172,11 @@ void ctx_parser_feed_byte (CtxParser *parser, int byte)
       case CTX_PARSER_STRING_A85:
         switch (byte)
           {
-            case '~': parser->state = CTX_PARSER_NEUTRAL;
+            case '~':
+              parser->state = CTX_PARSER_NEUTRAL;
+                 //   fprintf (stderr, "got %i\n", parser->pos);
               parser->pos = ctx_a85dec ((char*)parser->holding, (char*)parser->holding, parser->pos);
+                 //   fprintf (stderr, "dec got %i\n", parser->pos);
               ctx_parser_string_done (parser);
               break;
             default:
@@ -16247,7 +18189,8 @@ void ctx_parser_feed_byte (CtxParser *parser, int byte)
           {
             case '\\': parser->state = CTX_PARSER_STRING_APOS_ESCAPED; break;
             case '\'': parser->state = CTX_PARSER_NEUTRAL;
-              ctx_parser_string_done (parser); break;
+              ctx_parser_string_done (parser);
+              break;
             default:
               ctx_parser_holding_append (parser, byte); break;
           }
@@ -16372,7 +18315,7 @@ ctx_load_font_ttf_file (const char *name, const char *path)
 {
   uint8_t *contents = NULL;
   long length = 0;
-  _ctx_file_get_contents (path, &contents, &length);
+  ctx_get_contents (path, &contents, &length);
   if (!contents)
     {
       ctx_log ( "File load failed\n");
@@ -16735,7 +18678,7 @@ ctx_load_font_ctx_file (const char *name, const char *path)
 {
   uint8_t *contents = NULL;
   long length = 0;
-  _ctx_file_get_contents (path, &contents, &length);
+  ctx_get_contents (path, &contents, &length);
   if (!contents)
     {
       ctx_log ( "File load failed\n");
@@ -16778,17 +18721,15 @@ ctx_glyph_width_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar)
   sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
   uint8_t *data = NULL;
   long int len_bytes = 0;
-  _ctx_file_get_contents (path, &data, &len_bytes);
+  ctx_get_contents (path, &data, &len_bytes);
   float ret = 0.0;
   float font_size = state->gstate.font_size;
   if (data){
     Ctx *glyph_ctx = ctx_new ();
     ctx_parse (glyph_ctx, data);
-    //fprintf (stderr, "\n");
     for (int i = 0; i < glyph_ctx->drawlist.count; i++)
     {
       CtxEntry *e = &glyph_ctx->drawlist.entries[i];
-   // fprintf (stderr, "%c:", e->code);
       if (e->code == CTX_DEFINE_GLYPH)
         ret = e->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE;
     }
@@ -16802,11 +18743,10 @@ static int
 ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
 {
   char path[1024];
-  sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
+  sprintf (path, "file://%s/%010p", font->ctx_fs.path, unichar);
   uint8_t *data = NULL;
   long int len_bytes = 0;
-  _ctx_file_get_contents (path, &data, &len_bytes);
-//fprintf (stderr, "%s %li\n", path, len_bytes);
+  ctx_get_contents (path, &data, &len_bytes);
 
   if (data){
     Ctx *glyph_ctx = ctx_new ();
@@ -16948,11 +18888,11 @@ _ctx_text (Ctx        *ctx,
     {
       case CTX_TEXT_BASELINE_HANGING:
         /* XXX : crude */
-        baseline_offset = ctx->state.gstate.font_size * 0.55;
+        baseline_offset = ctx->state.gstate.font_size  * 0.55;
         break;
       case CTX_TEXT_BASELINE_TOP:
         /* XXX : crude */
-        baseline_offset = ctx->state.gstate.font_size * 0.7;
+        baseline_offset = ctx->state.gstate.font_size  * 0.7;
         break;
       case CTX_TEXT_BASELINE_BOTTOM:
         baseline_offset = -ctx->state.gstate.font_size * 0.1;
@@ -17039,6 +18979,15 @@ ctx_text (Ctx        *ctx,
 #endif
 }
 
+
+void
+ctx_fill_text (Ctx *ctx, const char *string,
+               float x, float y)
+{
+  ctx_move_to (ctx, x, y);
+  ctx_text (ctx, string);
+}
+
 void
 ctx_text_stroke (Ctx        *ctx,
                  const char *string)
@@ -17046,13 +18995,21 @@ ctx_text_stroke (Ctx        *ctx,
   if (!string)
     return;
 #if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_TEXT_STROKE, string, 0, 0);
+  ctx_process_cmd_str (ctx, CTX_STROKE_TEXT, string, 0, 0);
   _ctx_text (ctx, string, 1, 0);
 #else
   _ctx_text (ctx, string, 1, 1);
 #endif
 }
 
+void
+ctx_stroke_text (Ctx *ctx, const char *string,
+               float x, float y)
+{
+  ctx_move_to (ctx, x, y);
+  ctx_text_stroke (ctx, string);
+}
+
 static int _ctx_resolve_font (const char *name)
 {
   for (int i = 0; i < ctx_font_count; i ++)
@@ -17158,25 +19115,6 @@ static void ctx_font_setup ()
 }
 
 
-/* mrg - MicroRaptor Gui
- * Copyright (c) 2014 Øyvind Kolås <pippin hodefoting com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-//#include "ctx-string.h"
-
 #ifndef _DEFAULT_SOURCE
 #define _DEFAULT_SOURCE
 #endif
@@ -17304,2846 +19242,3568 @@ void ctx_string_append_data (CtxString *string, const char *str, int len)
     { _ctx_string_append_byte (string, str[i]); }
 }
 
-void ctx_string_append_string (CtxString *string, CtxString *string2)
+void ctx_string_append_string (CtxString *string, CtxString *string2)
+{
+  const char *str = ctx_string_get (string2);
+  while (str && *str)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
+
+const char *ctx_string_get (CtxString *string)
+{
+  return string->str;
+}
+
+int ctx_string_get_length (CtxString *string)
+{
+  return string->length;
+}
+
+void
+ctx_string_free (CtxString *string, int freealloc)
+{
+  if (freealloc)
+    {
+      ctx_string_destroy (string);
+    }
+#if 0
+  if (string->is_line)
+  {
+    VtLine *line = (VtLine*)string;
+    if (line->style)
+      { free (line->style); }
+    if (line->ctx)
+      { ctx_free (line->ctx); }
+    if (line->ctx_copy)
+      { ctx_free (line->ctx_copy); }
+  }
+#endif
+  free (string);
+}
+
+void
+ctx_string_set (CtxString *string, const char *new_string)
+{
+  ctx_string_clear (string);
+  _ctx_string_append_str (string, new_string);
+}
+
+static char *ctx_strdup (const char *str)
+{
+  int len = strlen (str);
+  char *ret = (char*)malloc (len + 1);
+  memcpy (ret, str, len);
+  ret[len]=0;
+  return ret;
+}
+
+void ctx_string_replace_utf8 (CtxString *string, int pos, const char *new_glyph)
+{
+  int new_len = ctx_utf8_len (*new_glyph);
+#if 1
+  int old_len = string->utf8_length;
+#else
+  int old_len = ctx_utf8_strlen (string->str);// string->utf8_length;
+#endif
+  char tmpg[3]=" ";
+  if (pos == old_len)
+    {
+      _ctx_string_append_str (string, new_glyph);
+      return;
+    }
+  if (new_len <= 1 && new_glyph[0] < 32)
+    {
+      new_len = 1;
+      tmpg[0]=new_glyph[0]+64;
+      new_glyph = tmpg;
+    }
+  {
+    for (int i = old_len; i <= pos + 2; i++)
+      {
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
+      }
+  }
+  if (string->length + new_len  >= string->allocated_length - 2)
+    {
+      char *tmp;
+      char *defer;
+      string->allocated_length = string->length + new_len + 2;
+      tmp = (char*) ctx_calloc (string->allocated_length + 1 + 8, 1);
+      strcpy (tmp, string->str);
+      defer = string->str;
+      string->str = tmp;
+      free (defer);
+    }
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if (*p == 0 || * (p+prev_len) == 0)
+    {
+      rest = ctx_strdup ("");
+    }
+  else
+    {
+      if (p + prev_len >= string->length  + string->str)
+        { rest = ctx_strdup (""); }
+      else
+        { rest = ctx_strdup (p + prev_len); }
+    }
+  memcpy (p, new_glyph, new_len);
+  memcpy (p + new_len, rest, strlen (rest) + 1);
+  //string->length += new_len;
+  //string->length -= prev_len;
+  free (rest);
+  string->length = strlen (string->str);
+  string->utf8_length = ctx_utf8_strlen (string->str);
+}
+
+void ctx_string_replace_unichar (CtxString *string, int pos, uint32_t unichar)
+{
+  uint8_t utf8[8];
+  ctx_unichar_to_utf8 (unichar, utf8);
+  ctx_string_replace_utf8 (string, pos, (char *) utf8);
+}
+
+uint32_t ctx_string_get_unichar (CtxString *string, int pos)
+{
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  if (!p)
+    { return 0; }
+  return ctx_utf8_to_unichar (p);
+}
+
+void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
+{
+  int new_len = ctx_utf8_len (*new_glyph);
+  int old_len = string->utf8_length;
+  char tmpg[3]=" ";
+  if (old_len == pos && 0)
+    {
+      ctx_string_append_str (string, new_glyph);
+      return;
+    }
+  if (new_len <= 1 && new_glyph[0] < 32)
+    {
+      tmpg[0]=new_glyph[0]+64;
+      new_glyph = tmpg;
+    }
+  {
+    for (int i = old_len; i <= pos; i++)
+      {
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
+      }
+  }
+  if (string->length + new_len + 1  > string->allocated_length)
+    {
+      char *tmp;
+      char *defer;
+      string->allocated_length = string->length + new_len + 1;
+      tmp = (char*) ctx_calloc (string->allocated_length + 1, 1);
+      strcpy (tmp, string->str);
+      defer = string->str;
+      string->str = tmp;
+      free (defer);
+    }
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if ( (*p == 0 || * (p+prev_len) == 0) && pos != 0)
+    {
+      rest = ctx_strdup ("");
+    }
+  else
+    {
+      rest = ctx_strdup (p);
+    }
+  memcpy (p, new_glyph, new_len);
+  memcpy (p + new_len, rest, strlen (rest) + 1);
+  string->length += new_len;
+  free (rest);
+  string->utf8_length = ctx_utf8_strlen (string->str);
+}
+
+void ctx_string_remove (CtxString *string, int pos)
 {
-  const char *str = ctx_string_get (string2);
-  while (str && *str)
+  int old_len = string->utf8_length;
+  {
+    for (int i = old_len; i <= pos; i++)
+      {
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
+      }
+  }
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if (*p == 0 || * (p+prev_len) == 0)
     {
-      _ctx_string_append_byte (string, *str);
-      str++;
+      rest = ctx_strdup ("");
+      prev_len = 0;
     }
+  else
+    {
+      rest = ctx_strdup (p + prev_len);
+    }
+  strcpy (p, rest);
+  string->str[string->length - prev_len] = 0;
+  free (rest);
+  string->length = strlen (string->str);
+  string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-const char *ctx_string_get (CtxString *string)
+char *ctx_strdup_printf (const char *format, ...)
 {
-  return string->str;
+  va_list ap;
+  size_t needed;
+  char *buffer;
+  va_start (ap, format);
+  needed = vsnprintf (NULL, 0, format, ap) + 1;
+  buffer = (char*)malloc (needed);
+  va_end (ap);
+  va_start (ap, format);
+  vsnprintf (buffer, needed, format, ap);
+  va_end (ap);
+  return buffer;
 }
 
-int ctx_string_get_length (CtxString *string)
+void ctx_string_append_printf (CtxString *string, const char *format, ...)
 {
-  return string->length;
+  va_list ap;
+  size_t needed;
+  char *buffer;
+  va_start (ap, format);
+  needed = vsnprintf (NULL, 0, format, ap) + 1;
+  buffer = (char*)malloc (needed);
+  va_end (ap);
+  va_start (ap, format);
+  vsnprintf (buffer, needed, format, ap);
+  va_end (ap);
+  ctx_string_append_str (string, buffer);
+  free (buffer);
 }
 
-void
-ctx_string_free (CtxString *string, int freealloc)
+#if CTX_CAIRO
+
+typedef struct _CtxCairo CtxCairo;
+struct
+  _CtxCairo
 {
-  if (freealloc)
+  CtxImplementation vfuncs;
+  Ctx              *ctx;
+  cairo_t          *cr;
+  cairo_pattern_t  *pat;
+  cairo_surface_t  *image;
+  int               preserve;
+};
+
+static void
+ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
+{
+  CtxEntry *entry = (CtxEntry *) &c->entry;
+  cairo_t *cr = ctx_cairo->cr;
+  switch (entry->code)
     {
-      ctx_string_destroy (string);
-    }
+      case CTX_LINE_TO:
+        cairo_line_to (cr, c->line_to.x, c->line_to.y);
+        break;
+      case CTX_REL_LINE_TO:
+        cairo_rel_line_to (cr, c->rel_line_to.x, c->rel_line_to.y);
+        break;
+      case CTX_MOVE_TO:
+        cairo_move_to (cr, c->move_to.x, c->move_to.y);
+        break;
+      case CTX_REL_MOVE_TO:
+        cairo_rel_move_to (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_CURVE_TO:
+        cairo_curve_to (cr, ctx_arg_float (0), ctx_arg_float (1),
+                        ctx_arg_float (2), ctx_arg_float (3),
+                        ctx_arg_float (4), ctx_arg_float (5) );
+        break;
+      case CTX_REL_CURVE_TO:
+        cairo_rel_curve_to (cr,ctx_arg_float (0), ctx_arg_float (1),
+                            ctx_arg_float (2), ctx_arg_float (3),
+                            ctx_arg_float (4), ctx_arg_float (5) );
+        break;
+      case CTX_PRESERVE:
+        ctx_cairo->preserve = 1;
+        break;
+      case CTX_QUAD_TO:
+        {
+          double x0, y0;
+          cairo_get_current_point (cr, &x0, &y0);
+          float cx = ctx_arg_float (0);
+          float cy = ctx_arg_float (1);
+          float  x = ctx_arg_float (2);
+          float  y = ctx_arg_float (3);
+          cairo_curve_to (cr,
+                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
+                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                          x,                              y);
+        }
+        break;
+      case CTX_REL_QUAD_TO:
+        {
+          double x0, y0;
+          cairo_get_current_point (cr, &x0, &y0);
+          float cx = ctx_arg_float (0) + x0;
+          float cy = ctx_arg_float (1) + y0;
+          float  x = ctx_arg_float (2) + x0;
+          float  y = ctx_arg_float (3) + y0;
+          cairo_curve_to (cr,
+                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
+                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                          x,                              y);
+        }
+        break;
+      /* rotate/scale/translate does not occur in fully minified data stream */
+      case CTX_ROTATE:
+        cairo_rotate (cr, ctx_arg_float (0) );
+        break;
+      case CTX_SCALE:
+        cairo_scale (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_TRANSLATE:
+        cairo_translate (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_LINE_WIDTH:
+        cairo_set_line_width (cr, ctx_arg_float (0) );
+        break;
+      case CTX_ARC:
+#if 0
+        fprintf (stderr, "F %2.1f %2.1f %2.1f %2.1f %2.1f %2.1f\n",
+                        ctx_arg_float(0),
+                        ctx_arg_float(1),
+                        ctx_arg_float(2),
+                        ctx_arg_float(3),
+                        ctx_arg_float(4),
+                        ctx_arg_float(5),
+                        ctx_arg_float(6));
+#endif
+        if (ctx_arg_float (5) == 1)
+          cairo_arc (cr, ctx_arg_float (0), ctx_arg_float (1),
+                     ctx_arg_float (2), ctx_arg_float (3),
+                     ctx_arg_float (4) );
+        else
+          cairo_arc_negative (cr, ctx_arg_float (0), ctx_arg_float (1),
+                              ctx_arg_float (2), ctx_arg_float (3),
+                              ctx_arg_float (4) );
+        break;
+      case CTX_SET_RGBA_U8:
+        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
+                               ctx_u8_to_float (ctx_arg_u8 (1) ),
+                               ctx_u8_to_float (ctx_arg_u8 (2) ),
+                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
+        break;
+#if 0
+      case CTX_SET_RGBA_STROKE: // XXX : we need to maintain
+        //       state for the two kinds
+        cairo_set_source_rgba (cr, ctx_arg_u8 (0) /255.0,
+                               ctx_arg_u8 (1) /255.0,
+                               ctx_arg_u8 (2) /255.0,
+                               ctx_arg_u8 (3) /255.0);
+        break;
+#endif
+      case CTX_RECTANGLE:
+      case CTX_ROUND_RECTANGLE: // XXX - arcs
+        cairo_rectangle (cr, c->rectangle.x, c->rectangle.y,
+                         c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_SET_PIXEL:
+        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
+                               ctx_u8_to_float (ctx_arg_u8 (1) ),
+                               ctx_u8_to_float (ctx_arg_u8 (2) ),
+                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
+        cairo_rectangle (cr, ctx_arg_u16 (2), ctx_arg_u16 (3), 1, 1);
+        cairo_fill (cr);
+        break;
+      case CTX_FILL:
+        if (ctx_cairo->preserve)
+        {
+          cairo_fill_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_fill (cr);
+        }
+        break;
+      case CTX_STROKE:
+        if (ctx_cairo->preserve)
+        {
+          cairo_stroke_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_stroke (cr);
+        }
+        break;
+      case CTX_IDENTITY:
+        cairo_identity_matrix (cr);
+        break;
+      case CTX_CLIP:
+        if (ctx_cairo->preserve)
+        {
+          cairo_clip_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_clip (cr);
+        }
+        break;
+        break;
+      case CTX_BEGIN_PATH:
+        cairo_new_path (cr);
+        break;
+      case CTX_CLOSE_PATH:
+        cairo_close_path (cr);
+        break;
+      case CTX_SAVE:
+        cairo_save (cr);
+        break;
+      case CTX_RESTORE:
+        cairo_restore (cr);
+        break;
+      case CTX_FONT_SIZE:
+        cairo_set_font_size (cr, ctx_arg_float (0) );
+        break;
+      case CTX_MITER_LIMIT:
+        cairo_set_miter_limit (cr, ctx_arg_float (0) );
+        break;
+      case CTX_LINE_CAP:
+        {
+          int cairo_val = CAIRO_LINE_CAP_SQUARE;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_CAP_ROUND:
+                cairo_val = CAIRO_LINE_CAP_ROUND;
+                break;
+              case CTX_CAP_SQUARE:
+                cairo_val = CAIRO_LINE_CAP_SQUARE;
+                break;
+              case CTX_CAP_NONE:
+                cairo_val = CAIRO_LINE_CAP_BUTT;
+                break;
+            }
+          cairo_set_line_cap (cr, cairo_val);
+        }
+        break;
+      case CTX_BLEND_MODE:
+        {
+          // does not map to cairo
+        }
+        break;
+      case CTX_COMPOSITING_MODE:
+        {
+          int cairo_val = CAIRO_OPERATOR_OVER;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_COMPOSITE_SOURCE_OVER:
+                cairo_val = CAIRO_OPERATOR_OVER;
+                break;
+              case CTX_COMPOSITE_COPY:
+                cairo_val = CAIRO_OPERATOR_SOURCE;
+                break;
+            }
+          cairo_set_operator (cr, cairo_val);
+        }
+      case CTX_LINE_JOIN:
+        {
+          int cairo_val = CAIRO_LINE_JOIN_ROUND;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_JOIN_ROUND:
+                cairo_val = CAIRO_LINE_JOIN_ROUND;
+                break;
+              case CTX_JOIN_BEVEL:
+                cairo_val = CAIRO_LINE_JOIN_BEVEL;
+                break;
+              case CTX_JOIN_MITER:
+                cairo_val = CAIRO_LINE_JOIN_MITER;
+                break;
+            }
+          cairo_set_line_join (cr, cairo_val);
+        }
+        break;
+      case CTX_LINEAR_GRADIENT:
+        {
+          if (ctx_cairo->pat)
+            {
+              cairo_pattern_destroy (ctx_cairo->pat);
+              ctx_cairo->pat = NULL;
+            }
+          ctx_cairo->pat = cairo_pattern_create_linear (ctx_arg_float (0), ctx_arg_float (1),
+                           ctx_arg_float (2), ctx_arg_float (3) );
+          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 0, 0, 0, 0, 1);
+          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 1, 1, 1, 1, 1);
+          cairo_set_source (cr, ctx_cairo->pat);
+        }
+        break;
+      case CTX_RADIAL_GRADIENT:
+        {
+          if (ctx_cairo->pat)
+            {
+              cairo_pattern_destroy (ctx_cairo->pat);
+              ctx_cairo->pat = NULL;
+            }
+          ctx_cairo->pat = cairo_pattern_create_radial (ctx_arg_float (0), ctx_arg_float (1),
+                           ctx_arg_float (2), ctx_arg_float (3),
+                           ctx_arg_float (4), ctx_arg_float (5) );
+          cairo_set_source (cr, ctx_cairo->pat);
+        }
+        break;
+      case CTX_GRADIENT_STOP:
+        cairo_pattern_add_color_stop_rgba (ctx_cairo->pat,
+                                           ctx_arg_float (0),
+                                           ctx_u8_to_float (ctx_arg_u8 (4) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (5) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (6) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (7) ) );
+        break;
+        // XXX  implement TEXTURE
 #if 0
-  if (string->is_line)
-  {
-    VtLine *line = (VtLine*)string;
-    if (line->style)
-      { free (line->style); }
-    if (line->ctx)
-      { ctx_free (line->ctx); }
-    if (line->ctx_copy)
-      { ctx_free (line->ctx_copy); }
-  }
-#endif
-  free (string);
-}
-
-void
-ctx_string_set (CtxString *string, const char *new_string)
-{
-  ctx_string_clear (string);
-  _ctx_string_append_str (string, new_string);
-}
-
-static char *ctx_strdup (const char *str)
-{
-  int len = strlen (str);
-  char *ret = (char*)malloc (len + 1);
-  memcpy (ret, str, len);
-  ret[len]=0;
-  return ret;
-}
-
-void ctx_string_replace_utf8 (CtxString *string, int pos, const char *new_glyph)
-{
-  int new_len = ctx_utf8_len (*new_glyph);
-#if 1
-  int old_len = string->utf8_length;
-#else
-  int old_len = ctx_utf8_strlen (string->str);// string->utf8_length;
+      case CTX_LOAD_IMAGE:
+        {
+          if (image)
+            {
+              cairo_surface_destroy (image);
+              image = NULL;
+            }
+          if (pat)
+            {
+              cairo_pattern_destroy (pat);
+              pat = NULL;
+            }
+          image = cairo_image_surface_create_from_png (ctx_arg_string() );
+          cairo_set_source_surface (cr, image, ctx_arg_float (0), ctx_arg_float (1) );
+        }
+        break;
 #endif
-  char tmpg[3]=" ";
-  if (pos == old_len)
-    {
-      _ctx_string_append_str (string, new_glyph);
-      return;
-    }
-  if (new_len <= 1 && new_glyph[0] < 32)
-    {
-      new_len = 1;
-      tmpg[0]=new_glyph[0]+64;
-      new_glyph = tmpg;
-    }
-  {
-    for (int i = old_len; i <= pos + 2; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
-  }
-  if (string->length + new_len  >= string->allocated_length - 2)
-    {
-      char *tmp;
-      char *defer;
-      string->allocated_length = string->length + new_len + 2;
-      tmp = (char*) ctx_calloc (string->allocated_length + 1 + 8, 1);
-      strcpy (tmp, string->str);
-      defer = string->str;
-      string->str = tmp;
-      free (defer);
-    }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if (*p == 0 || * (p+prev_len) == 0)
-    {
-      rest = ctx_strdup ("");
-    }
-  else
-    {
-      if (p + prev_len >= string->length  + string->str)
-        { rest = ctx_strdup (""); }
-      else
-        { rest = ctx_strdup (p + prev_len); }
-    }
-  memcpy (p, new_glyph, new_len);
-  memcpy (p + new_len, rest, strlen (rest) + 1);
-  //string->length += new_len;
-  //string->length -= prev_len;
-  free (rest);
-  string->length = strlen (string->str);
-  string->utf8_length = ctx_utf8_strlen (string->str);
-}
-
-void ctx_string_replace_unichar (CtxString *string, int pos, uint32_t unichar)
-{
-  uint8_t utf8[8];
-  ctx_unichar_to_utf8 (unichar, utf8);
-  ctx_string_replace_utf8 (string, pos, (char *) utf8);
-}
-
-uint32_t ctx_string_get_unichar (CtxString *string, int pos)
-{
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  if (!p)
-    { return 0; }
-  return ctx_utf8_to_unichar (p);
-}
-
-void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
-{
-  int new_len = ctx_utf8_len (*new_glyph);
-  int old_len = string->utf8_length;
-  char tmpg[3]=" ";
-  if (old_len == pos && 0)
-    {
-      ctx_string_append_str (string, new_glyph);
-      return;
-    }
-  if (new_len <= 1 && new_glyph[0] < 32)
-    {
-      tmpg[0]=new_glyph[0]+64;
-      new_glyph = tmpg;
-    }
-  {
-    for (int i = old_len; i <= pos; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
-  }
-  if (string->length + new_len + 1  > string->allocated_length)
-    {
-      char *tmp;
-      char *defer;
-      string->allocated_length = string->length + new_len + 1;
-      tmp = (char*) ctx_calloc (string->allocated_length + 1, 1);
-      strcpy (tmp, string->str);
-      defer = string->str;
-      string->str = tmp;
-      free (defer);
-    }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if ( (*p == 0 || * (p+prev_len) == 0) && pos != 0)
-    {
-      rest = ctx_strdup ("");
-    }
-  else
-    {
-      rest = ctx_strdup (p);
+      case CTX_TEXT:
+        /* XXX: implement some linebreaking/wrap, positioning
+         *      behavior here
+         */
+        cairo_show_text (cr, ctx_arg_string () );
+        break;
+      case CTX_CONT:
+      case CTX_EDGE:
+      case CTX_DATA:
+      case CTX_DATA_REV:
+      case CTX_FLUSH:
+        break;
     }
-  memcpy (p, new_glyph, new_len);
-  memcpy (p + new_len, rest, strlen (rest) + 1);
-  string->length += new_len;
-  free (rest);
-  string->utf8_length = ctx_utf8_strlen (string->str);
+  ctx_process (ctx_cairo->ctx, entry);
 }
 
-void ctx_string_remove (CtxString *string, int pos)
+void ctx_cairo_free (CtxCairo *ctx_cairo)
 {
-  int old_len = string->utf8_length;
-  {
-    for (int i = old_len; i <= pos; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
-  }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if (*p == 0 || * (p+prev_len) == 0)
-    {
-      rest = ctx_strdup ("");
-      prev_len = 0;
-    }
-  else
-    {
-      rest = ctx_strdup (p + prev_len);
-    }
-  strcpy (p, rest);
-  string->str[string->length - prev_len] = 0;
-  free (rest);
-  string->length = strlen (string->str);
-  string->utf8_length = ctx_utf8_strlen (string->str);
+  if (ctx_cairo->pat)
+    { cairo_pattern_destroy (ctx_cairo->pat); }
+  if (ctx_cairo->image)
+    { cairo_surface_destroy (ctx_cairo->image); }
+  free (ctx_cairo);
 }
 
-char *ctx_strdup_printf (const char *format, ...)
+void
+ctx_render_cairo (Ctx *ctx, cairo_t *cr)
 {
-  va_list ap;
-  size_t needed;
-  char *buffer;
-  va_start (ap, format);
-  needed = vsnprintf (NULL, 0, format, ap) + 1;
-  buffer = malloc (needed);
-  va_end (ap);
-  va_start (ap, format);
-  vsnprintf (buffer, needed, format, ap);
-  va_end (ap);
-  return buffer;
+  CtxIterator iterator;
+  CtxCommand *command;
+  CtxCairo    ctx_cairo = {{(void*)ctx_cairo_process, NULL, NULL}, ctx, cr, NULL, NULL};
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_cairo_process (&ctx_cairo, command); }
 }
 
-void ctx_string_append_printf (CtxString *string, const char *format, ...)
+Ctx *
+ctx_new_for_cairo (cairo_t *cr)
 {
-  va_list ap;
-  size_t needed;
-  char *buffer;
-  va_start (ap, format);
-  needed = vsnprintf (NULL, 0, format, ap) + 1;
-  buffer = malloc (needed);
-  va_end (ap);
-  va_start (ap, format);
-  vsnprintf (buffer, needed, format, ap);
-  va_end (ap);
-  ctx_string_append_str (string, buffer);
-  free (buffer);
+  Ctx *ctx = ctx_new ();
+  CtxCairo *ctx_cairo = calloc(sizeof(CtxCairo),1);
+  ctx_cairo->vfuncs.free = (void*)ctx_cairo_free;
+  ctx_cairo->vfuncs.process = (void*)ctx_cairo_process;
+  ctx_cairo->ctx = ctx;
+  ctx_cairo->cr = cr;
+
+  ctx_set_renderer (ctx, (void*)ctx_cairo);
+  return ctx;
 }
 
-#if CTX_CAIRO
+#endif
 
-typedef struct _CtxCairo CtxCairo;
-struct
-  _CtxCairo
+#if CTX_EVENTS
+
+static int ctx_find_largest_matching_substring
+ (const char *X, const char *Y, int m, int n, int *offsetY, int *offsetX) 
+{ 
+  int longest_common_suffix[2][n+1];
+  int best_length = 0;
+  for (int i=0; i<=m; i++)
+  {
+    for (int j=0; j<=n; j++)
+    {
+      if (i == 0 || j == 0 || !(X[i-1] == Y[j-1]))
+      {
+        longest_common_suffix[i%2][j] = 0;
+      }
+      else
+      {
+          longest_common_suffix[i%2][j] = longest_common_suffix[(i-1)%2][j-1] + 1;
+          if (best_length < longest_common_suffix[i%2][j])
+          {
+            best_length = longest_common_suffix[i%2][j];
+            if (offsetY) *offsetY = j - best_length;
+            if (offsetX) *offsetX = i - best_length;
+          }
+      }
+    }
+  }
+  return best_length;
+} 
+
+typedef struct CtxSpan {
+  int from_prev;
+  int start;
+  int length;
+} CtxSpan;
+
+#define CHUNK_SIZE 32
+#define MIN_MATCH  7        // minimum match length to be encoded
+#define WINDOW_PADDING 16   // look-aside amount
+
+#if 0
+static void _dassert(int line, int condition, const char *str, int foo, int bar, int baz)
 {
-  CtxImplementation vfuncs;
-  Ctx              *ctx;
-  cairo_t          *cr;
-  cairo_pattern_t  *pat;
-  cairo_surface_t  *image;
-  int               preserve;
-};
+  if (!condition)
+  {
+    FILE *f = fopen ("/tmp/cdebug", "a");
+    fprintf (f, "%i: %s    %i %i %i\n", line, str, foo, bar, baz);
+    fclose (f);
+  }
+}
+#define dassert(cond, foo, bar, baz) _dassert(__LINE__, cond, #cond, foo, bar ,baz)
+#endif
+#define dassert(cond, foo, bar, baz)
 
-static void
-ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
+/* XXX repeated substring matching is slow, we'll be
+ * better off with a hash-table with linked lists of
+ * matching 3-4 characters in previous.. or even
+ * a naive approach that expects rough alignment..
+ */
+static char *encode_in_terms_of_previous (
+                const char *src,  int src_len,
+                const char *prev, int prev_len,
+                int *out_len,
+                int max_ticks)
 {
-  CtxEntry *entry = (CtxEntry *) &c->entry;
-  cairo_t *cr = ctx_cairo->cr;
-  switch (entry->code)
-    {
-      case CTX_LINE_TO:
-        cairo_line_to (cr, c->line_to.x, c->line_to.y);
-        break;
-      case CTX_REL_LINE_TO:
-        cairo_rel_line_to (cr, c->rel_line_to.x, c->rel_line_to.y);
-        break;
-      case CTX_MOVE_TO:
-        cairo_move_to (cr, c->move_to.x, c->move_to.y);
-        break;
-      case CTX_REL_MOVE_TO:
-        cairo_rel_move_to (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_CURVE_TO:
-        cairo_curve_to (cr, ctx_arg_float (0), ctx_arg_float (1),
-                        ctx_arg_float (2), ctx_arg_float (3),
-                        ctx_arg_float (4), ctx_arg_float (5) );
-        break;
-      case CTX_REL_CURVE_TO:
-        cairo_rel_curve_to (cr,ctx_arg_float (0), ctx_arg_float (1),
-                            ctx_arg_float (2), ctx_arg_float (3),
-                            ctx_arg_float (4), ctx_arg_float (5) );
-        break;
-      case CTX_PRESERVE:
-        ctx_cairo->preserve = 1;
-        break;
-      case CTX_QUAD_TO:
-        {
-          double x0, y0;
-          cairo_get_current_point (cr, &x0, &y0);
-          float cx = ctx_arg_float (0);
-          float cy = ctx_arg_float (1);
-          float  x = ctx_arg_float (2);
-          float  y = ctx_arg_float (3);
-          cairo_curve_to (cr,
-                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
-                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                          x,                              y);
-        }
-        break;
-      case CTX_REL_QUAD_TO:
-        {
-          double x0, y0;
-          cairo_get_current_point (cr, &x0, &y0);
-          float cx = ctx_arg_float (0) + x0;
-          float cy = ctx_arg_float (1) + y0;
-          float  x = ctx_arg_float (2) + x0;
-          float  y = ctx_arg_float (3) + y0;
-          cairo_curve_to (cr,
-                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
-                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                          x,                              y);
-        }
-        break;
-      /* rotate/scale/translate does not occur in fully minified data stream */
-      case CTX_ROTATE:
-        cairo_rotate (cr, ctx_arg_float (0) );
-        break;
-      case CTX_SCALE:
-        cairo_scale (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_TRANSLATE:
-        cairo_translate (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_LINE_WIDTH:
-        cairo_set_line_width (cr, ctx_arg_float (0) );
-        break;
-      case CTX_ARC:
+  CtxString *string = ctx_string_new ("");
+  CtxList *encoded_list = NULL;
+
+  /* TODO : make expected position offset in prev slide based on
+   * matches and not be constant */
+
+  long ticks_start = ctx_ticks ();
+  int start = 0;
+  int length = CHUNK_SIZE;
+  for (start = 0; start < src_len; start += length)
+  {
+    CtxSpan *span = calloc (sizeof (CtxSpan), 1);
+    span->start = start;
+    if (start + length > src_len)
+      span->length = src_len - start;
+    else
+      span->length = length;
+    span->from_prev = 0;
+    ctx_list_append (&encoded_list, span);
+  }
+
+  for (CtxList *l = encoded_list; l; l = l->next)
+  {
+    CtxSpan *span = l->data;
+    if (!span->from_prev)
+    {
+      if (span->length >= MIN_MATCH)
+      {
+         int prev_pos = 0;
+         int curr_pos = 0;
+         assert(1);
 #if 0
-        fprintf (stderr, "F %2.1f %2.1f %2.1f %2.1f %2.1f %2.1f\n",
-                        ctx_arg_float(0),
-                        ctx_arg_float(1),
-                        ctx_arg_float(2),
-                        ctx_arg_float(3),
-                        ctx_arg_float(4),
-                        ctx_arg_float(5),
-                        ctx_arg_float(6));
+         int prev_start =  0;
+         int prev_window_length = prev_len;
+#else
+         int window_padding = WINDOW_PADDING;
+         int prev_start = span->start - window_padding;
+         if (prev_start < 0)
+           prev_start = 0;
+
+         dassert(span->start>=0 , 0,0,0);
+
+         int prev_window_length = prev_len - prev_start;
+         if (prev_window_length > span->length + window_padding * 2 + span->start)
+           prev_window_length = span->length + window_padding * 2 + span->start;
 #endif
-        if (ctx_arg_float (5) == 1)
-          cairo_arc (cr, ctx_arg_float (0), ctx_arg_float (1),
-                     ctx_arg_float (2), ctx_arg_float (3),
-                     ctx_arg_float (4) );
-        else
-          cairo_arc_negative (cr, ctx_arg_float (0), ctx_arg_float (1),
-                              ctx_arg_float (2), ctx_arg_float (3),
-                              ctx_arg_float (4) );
-        break;
-      case CTX_SET_RGBA_U8:
-        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
-                               ctx_u8_to_float (ctx_arg_u8 (1) ),
-                               ctx_u8_to_float (ctx_arg_u8 (2) ),
-                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
-        break;
-#if 0
-      case CTX_SET_RGBA_STROKE: // XXX : we need to maintain
-        //       state for the two kinds
-        cairo_set_source_rgba (cr, ctx_arg_u8 (0) /255.0,
-                               ctx_arg_u8 (1) /255.0,
-                               ctx_arg_u8 (2) /255.0,
-                               ctx_arg_u8 (3) /255.0);
-        break;
+         int match_len = 0;
+         if (prev_window_length > 0)
+           match_len = ctx_find_largest_matching_substring(prev + prev_start, src + span->start, 
prev_window_length, span->length, &curr_pos, &prev_pos);
+#if 1
+         prev_pos += prev_start;
 #endif
-      case CTX_RECTANGLE:
-      case CTX_ROUND_RECTANGLE: // XXX - arcs
-        cairo_rectangle (cr, c->rectangle.x, c->rectangle.y,
-                         c->rectangle.width, c->rectangle.height);
-        break;
-      case CTX_SET_PIXEL:
-        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
-                               ctx_u8_to_float (ctx_arg_u8 (1) ),
-                               ctx_u8_to_float (ctx_arg_u8 (2) ),
-                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
-        cairo_rectangle (cr, ctx_arg_u16 (2), ctx_arg_u16 (3), 1, 1);
-        cairo_fill (cr);
-        break;
-      case CTX_FILL:
-        if (ctx_cairo->preserve)
-        {
-          cairo_fill_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_fill (cr);
-        }
-        break;
-      case CTX_STROKE:
-        if (ctx_cairo->preserve)
-        {
-          cairo_stroke_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_stroke (cr);
-        }
-        break;
-      case CTX_IDENTITY:
-        cairo_identity_matrix (cr);
-        break;
-      case CTX_CLIP:
-        if (ctx_cairo->preserve)
-        {
-          cairo_clip_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_clip (cr);
-        }
-        break;
-        break;
-      case CTX_BEGIN_PATH:
-        cairo_new_path (cr);
-        break;
-      case CTX_CLOSE_PATH:
-        cairo_close_path (cr);
-        break;
-      case CTX_SAVE:
-        cairo_save (cr);
-        break;
-      case CTX_RESTORE:
-        cairo_restore (cr);
-        break;
-      case CTX_FONT_SIZE:
-        cairo_set_font_size (cr, ctx_arg_float (0) );
-        break;
-      case CTX_MITER_LIMIT:
-        cairo_set_miter_limit (cr, ctx_arg_float (0) );
-        break;
-      case CTX_LINE_CAP:
-        {
-          int cairo_val = CAIRO_LINE_CAP_SQUARE;
-          switch (ctx_arg_u8 (0) )
+
+         if (match_len >= MIN_MATCH)
+         {
+            int start  = span->start;
+            int length = span->length;
+
+            span->from_prev = 1;
+            span->start     = prev_pos;
+            span->length    = match_len;
+            dassert (span->start >= 0, prev_pos, prev_start, span->start);
+            dassert (span->length > 0, prev_pos, prev_start, span->length);
+
+            if (curr_pos)
             {
-              case CTX_CAP_ROUND:
-                cairo_val = CAIRO_LINE_CAP_ROUND;
-                break;
-              case CTX_CAP_SQUARE:
-                cairo_val = CAIRO_LINE_CAP_SQUARE;
-                break;
-              case CTX_CAP_NONE:
-                cairo_val = CAIRO_LINE_CAP_BUTT;
-                break;
+              CtxSpan *prev = calloc (sizeof (CtxSpan), 1);
+              prev->start = start;
+              prev->length =  curr_pos;
+            dassert (prev->start >= 0, prev_pos, prev_start, prev->start);
+            dassert (prev->length > 0, prev_pos, prev_start, prev->length);
+              prev->from_prev = 0;
+              ctx_list_insert_before (&encoded_list, l, prev);
             }
-          cairo_set_line_cap (cr, cairo_val);
-        }
-        break;
-      case CTX_BLEND_MODE:
-        {
-          // does not map to cairo
-        }
-        break;
-      case CTX_COMPOSITING_MODE:
-        {
-          int cairo_val = CAIRO_OPERATOR_OVER;
-          switch (ctx_arg_u8 (0) )
+
+
+            if (match_len + curr_pos < start + length)
             {
-              case CTX_COMPOSITE_SOURCE_OVER:
-                cairo_val = CAIRO_OPERATOR_OVER;
-                break;
-              case CTX_COMPOSITE_COPY:
-                cairo_val = CAIRO_OPERATOR_SOURCE;
-                break;
+              CtxSpan *next = calloc (sizeof (CtxSpan), 1);
+              next->start = start + curr_pos + match_len;
+              next->length = (start + length) - next->start;
+            dassert (next->start >= 0, prev_pos, prev_start, next->start);
+      //    dassert (next->length > 0, prev_pos, prev_start, next->length);
+              next->from_prev = 0;
+              if (next->length)
+              {
+                if (l->next)
+                  ctx_list_insert_before (&encoded_list, l->next, next);
+                else
+                  ctx_list_append (&encoded_list, next);
+              }
+              else
+                free (next);
             }
-          cairo_set_operator (cr, cairo_val);
-        }
-      case CTX_LINE_JOIN:
-        {
-          int cairo_val = CAIRO_LINE_JOIN_ROUND;
-          switch (ctx_arg_u8 (0) )
+
+            if (curr_pos) // step one item back for forloop
             {
-              case CTX_JOIN_ROUND:
-                cairo_val = CAIRO_LINE_JOIN_ROUND;
-                break;
-              case CTX_JOIN_BEVEL:
-                cairo_val = CAIRO_LINE_JOIN_BEVEL;
-                break;
-              case CTX_JOIN_MITER:
-                cairo_val = CAIRO_LINE_JOIN_MITER;
-                break;
+              CtxList *tmp = encoded_list;
+              int found = 0;
+              while (!found && tmp && tmp->next)
+              {
+                if (tmp->next == l)
+                {
+                  l = tmp;
+                  break;
+                }
+                tmp = tmp->next;
+              }
             }
-          cairo_set_line_join (cr, cairo_val);
+         }
+      }
+    }
+
+    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
+      break;
+  }
+
+  /* merge adjecant prev span references  */
+  {
+    for (CtxList *l = encoded_list; l; l = l->next)
+    {
+      CtxSpan *span = l->data;
+again:
+      if (l->next)
+      {
+        CtxSpan *next_span = l->next->data;
+        if (span->from_prev && next_span->from_prev &&
+            span->start + span->length == 
+            next_span->start)
+        {
+           span->length += next_span->length;
+           ctx_list_remove (&encoded_list, next_span);
+           goto again;
         }
-        break;
-      case CTX_LINEAR_GRADIENT:
+      }
+    }
+  }
+
+  while (encoded_list)
+  {
+    CtxSpan *span = encoded_list->data;
+    if (span->from_prev)
+    {
+      char ref[128];
+      sprintf (ref, "%c%i %i%c", CTX_CODEC_CHAR, span->start, span->length, CTX_CODEC_CHAR);
+      ctx_string_append_data (string, ref, strlen(ref));
+    }
+    else
+    {
+      for (int i = span->start; i< span->start+span->length; i++)
+      {
+        if (src[i] == CTX_CODEC_CHAR)
         {
-          if (ctx_cairo->pat)
-            {
-              cairo_pattern_destroy (ctx_cairo->pat);
-              ctx_cairo->pat = NULL;
-            }
-          ctx_cairo->pat = cairo_pattern_create_linear (ctx_arg_float (0), ctx_arg_float (1),
-                           ctx_arg_float (2), ctx_arg_float (3) );
-          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 0, 0, 0, 0, 1);
-          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 1, 1, 1, 1, 1);
-          cairo_set_source (cr, ctx_cairo->pat);
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
         }
-        break;
-      case CTX_RADIAL_GRADIENT:
+        else
         {
-          if (ctx_cairo->pat)
-            {
-              cairo_pattern_destroy (ctx_cairo->pat);
-              ctx_cairo->pat = NULL;
-            }
-          ctx_cairo->pat = cairo_pattern_create_radial (ctx_arg_float (0), ctx_arg_float (1),
-                           ctx_arg_float (2), ctx_arg_float (3),
-                           ctx_arg_float (4), ctx_arg_float (5) );
-          cairo_set_source (cr, ctx_cairo->pat);
+          ctx_string_append_byte (string, src[i]);
         }
-        break;
-      case CTX_GRADIENT_STOP:
-        cairo_pattern_add_color_stop_rgba (ctx_cairo->pat,
-                                           ctx_arg_float (0),
-                                           ctx_u8_to_float (ctx_arg_u8 (4) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (5) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (6) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (7) ) );
-        break;
-        // XXX  implement TEXTURE
-#if 0
-      case CTX_LOAD_IMAGE:
+      }
+    }
+    free (span);
+    ctx_list_remove (&encoded_list, span);
+  }
+
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
+}
+
+#if 0 // for documentation/reference purposes
+static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int prev_len, int *out_len)
+{
+  CtxString *string = ctx_string_new ("");
+  char reference[32]="";
+  int ref_len = 0;
+  int in_ref = 0;
+  for (int i = 0; i < enc_len; i++)
+  {
+    if (encoded[i] == CTX_CODEC_CHAR)
+    {
+      if (!in_ref)
+      {
+        in_ref = 1;
+      }
+      else
+      {
+        int start = atoi (reference);
+        int len = 0;
+        if (strchr (reference, ' '))
+          len = atoi (strchr (reference, ' ')+1);
+
+        if (start < 0)start = 0;
+        if (start >= prev_len)start = prev_len-1;
+        if (len + start > prev_len)
+          len = prev_len - start;
+
+        if (start == 0 && len == 0)
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
+        else
+          ctx_string_append_data (string, prev + start, len);
+        ref_len = 0;
+        in_ref = 0;
+      }
+    }
+    else
+    {
+      if (in_ref)
+      {
+        if (ref_len < 16)
         {
-          if (image)
-            {
-              cairo_surface_destroy (image);
-              image = NULL;
-            }
-          if (pat)
-            {
-              cairo_pattern_destroy (pat);
-              pat = NULL;
-            }
-          image = cairo_image_surface_create_from_png (ctx_arg_string() );
-          cairo_set_source_surface (cr, image, ctx_arg_float (0), ctx_arg_float (1) );
+          reference[ref_len++] = encoded[i];
+          reference[ref_len] = 0;
         }
-        break;
-#endif
-      case CTX_TEXT:
-        /* XXX: implement some linebreaking/wrap, positioning
-         *      behavior here
-         */
-        cairo_show_text (cr, ctx_arg_string () );
-        break;
-      case CTX_CONT:
-      case CTX_EDGE:
-      case CTX_DATA:
-      case CTX_DATA_REV:
-      case CTX_FLUSH:
-        break;
+      }
+      else
+      ctx_string_append_byte (string, encoded[i]);
     }
-  ctx_process (ctx_cairo->ctx, entry);
+  }
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
 }
+#endif
 
-void ctx_cairo_free (CtxCairo *ctx_cairo)
+#define CTX_START_STRING "U\n"  // or " reset "
+#define CTX_END_STRING   "\nX"  // or "\ndone"
+#define CTX_END_STRING2  "\n\e"
+
+int ctx_frame_ack = -1;
+static char *prev_frame_contents = NULL;
+static int   prev_frame_len = 0;
+
+static void ctx_ctx_flush (CtxCtx *ctxctx)
 {
-  if (ctx_cairo->pat)
-    { cairo_pattern_destroy (ctx_cairo->pat); }
-  if (ctx_cairo->image)
-    { cairo_surface_destroy (ctx_cairo->image); }
-  free (ctx_cairo);
+#if 0
+  FILE *debug = fopen ("/tmp/ctx-debug", "a");
+  fprintf (debug, "------\n");
+#endif
+
+  if (ctx_native_events)
+    fprintf (stdout, "\e[?201h");
+  fprintf (stdout, "\e[H\e[?25l\e[?200h");
+#if 0
+  fprintf (stdout, CTX_START_STRING);
+  ctx_render_stream (ctxctx->ctx, stdout, 0);
+  fprintf (stdout, CTX_END_STRING);
+#else
+  {
+    int cur_frame_len = 0;
+    char *rest = ctx_render_string (ctxctx->ctx, 0, &cur_frame_len);
+    char *cur_frame_contents = malloc (cur_frame_len + strlen(CTX_START_STRING) + strlen (CTX_END_STRING) + 
1);
+
+    cur_frame_contents[0]=0;
+    strcat (cur_frame_contents, CTX_START_STRING);
+    strcat (cur_frame_contents, rest);
+    strcat (cur_frame_contents, CTX_END_STRING);
+    free (rest);
+    cur_frame_len += strlen (CTX_START_STRING) + strlen (CTX_END_STRING);
+
+    if (prev_frame_contents && 0)  // XXX : 
+    {
+      char *encoded;
+      int encoded_len = 0;
+      //uint64_t ticks_start = ctx_ticks ();
+
+      encoded = encode_in_terms_of_previous (cur_frame_contents, cur_frame_len, prev_frame_contents, 
prev_frame_len, &encoded_len, 1000 * 10);
+//    encoded = strdup (cur_frame_contents);
+//    encoded_len = strlen (encoded);
+      //uint64_t ticks_end = ctx_ticks ();
+
+      fwrite (encoded, encoded_len, 1, stdout);
+//    fwrite (encoded, cur_frame_len, 1, stdout);
+#if 0
+      fprintf (debug, "---prev-frame(%i)\n%s", (int)strlen(prev_frame_contents), prev_frame_contents);
+      fprintf (debug, "---cur-frame(%i)\n%s", (int)strlen(cur_frame_contents), cur_frame_contents);
+      fprintf (debug, "---encoded(%.4f %i)---\n%s--------\n",
+                      (ticks_end-ticks_start)/1000.0,
+                      (int)strlen(encoded), encoded);
+#endif
+      free (encoded);
+    }
+    else
+    {
+      fwrite (cur_frame_contents, cur_frame_len, 1, stdout);
+    }
+
+    if (prev_frame_contents)
+      free (prev_frame_contents);
+    prev_frame_contents = cur_frame_contents;
+    prev_frame_len = cur_frame_len;
+  }
+#endif
+#if 0
+    fclose (debug);
+#endif
+  fprintf (stdout, CTX_END_STRING2);
+
+  fprintf (stdout, "\e[5n");
+  fflush (stdout);
+
+  ctx_frame_ack = 0;
+  do {
+     ctx_consume_events (ctxctx->ctx);
+  } while (ctx_frame_ack != 1);
 }
 
-void
-ctx_render_cairo (Ctx *ctx, cairo_t *cr)
+void ctx_ctx_free (CtxCtx *ctx)
 {
-  CtxIterator iterator;
-  CtxCommand *command;
-  CtxCairo    ctx_cairo = {{(void*)ctx_cairo_process, NULL, NULL}, ctx, cr, NULL, NULL};
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_cairo_process (&ctx_cairo, command); }
+  nc_at_exit ();
+  free (ctx);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-Ctx *
-ctx_new_for_cairo (cairo_t *cr)
+Ctx *ctx_new_ctx (int width, int height)
 {
   Ctx *ctx = ctx_new ();
-  CtxCairo *ctx_cairo = calloc(sizeof(CtxCairo),1);
-  ctx_cairo->vfuncs.free = (void*)ctx_cairo_free;
-  ctx_cairo->vfuncs.process = (void*)ctx_cairo_process;
-  ctx_cairo->ctx = ctx;
-  ctx_cairo->cr = cr;
-
-  ctx_set_renderer (ctx, (void*)ctx_cairo);
+  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
+  fprintf (stdout, "\e[?1049h");
+  //fprintf (stderr, "\e[H");
+  //fprintf (stderr, "\e[2J");
+  ctx_native_events = 1;
+  if (width <= 0 || height <= 0)
+  {
+    ctxctx->cols = ctx_terminal_cols ();
+    ctxctx->rows = ctx_terminal_rows ();
+    width  = ctxctx->width  = ctx_terminal_width ();
+    height = ctxctx->height = ctx_terminal_height ();
+  }
+  else
+  {
+    ctxctx->width  = width;
+    ctxctx->height = height;
+    ctxctx->cols   = width / 80;
+    ctxctx->rows   = height / 24;
+  }
+  ctxctx->ctx = ctx;
+  if (!ctx_native_events)
+    _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, ctxctx);
+  ctx_set_size (ctx, width, height);
+  ctxctx->flush = (void(*)(void *))ctx_ctx_flush;
+  ctxctx->free  = (void(*)(void *))ctx_ctx_free;
   return ctx;
 }
 
+int ctx_ctx_consume_events (Ctx *ctx)
+{
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
+  if (ctx_native_events)
+    {
+      float x = 0, y = 0;
+      int b = 0;
+      char event_type[128]="";
+      event = ctx_native_get_event (ctx, 1000/120);
+#if 0
+      if(event){
+        FILE *file = fopen ("/tmp/log", "a");
+        fprintf (file, "[%s]\n", event);
+        fclose (file);
+      }
 #endif
+      if (event)
+      {
+      sscanf (event, "%s %f %f %i", event_type, &x, &y, &b);
+      if (!strcmp (event_type, "idle"))
+      {
+      }
+      else if (!strcmp (event_type, "mouse-press"))
+      {
+        ctx_pointer_press (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "mouse-drag")||
+               !strcmp (event_type, "mouse-motion"))
+      {
+        ctx_pointer_motion (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "mouse-release"))
+      {
+        ctx_pointer_release (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "message"))
+      {
+        ctx_incoming_message (ctx, event + strlen ("message"), 0);
+      } else if (!strcmp (event, "size-changed"))
+      {
+        fprintf (stdout, "\e[H\e[2J\e[?25l");
+        ctxctx->cols = ctx_terminal_cols ();
+        ctxctx->rows = ctx_terminal_rows ();
+        ctxctx->width  = ctx_terminal_width ();
+        ctxctx->height = ctx_terminal_height ();
+        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
 
-#if CTX_EVENTS
-
-static int ctx_find_largest_matching_substring
- (const char *X, const char *Y, int m, int n, int *offsetY, int *offsetX) 
-{ 
-  int longest_common_suffix[2][n+1];
-  int best_length = 0;
-  for (int i=0; i<=m; i++)
-  {
-    for (int j=0; j<=n; j++)
-    {
-      if (i == 0 || j == 0 || !(X[i-1] == Y[j-1]))
+        if (prev_frame_contents)
+          free (prev_frame_contents);
+        prev_frame_contents = NULL;
+        prev_frame_len = 0;
+        ctx_set_dirty (ctx, 1);
+        //ctx_key_press (ctx, 0, "size-changed", 0);
+      }
+      else if (!strcmp (event_type, "keyup"))
       {
-        longest_common_suffix[i%2][j] = 0;
+        char buf[4]={ x, 0 };
+        ctx_key_up (ctx, (int)x, buf, 0);
+      }
+      else if (!strcmp (event_type, "keydown"))
+      {
+        char buf[4]={ x, 0 };
+        ctx_key_down (ctx, (int)x, buf, 0);
       }
       else
       {
-          longest_common_suffix[i%2][j] = longest_common_suffix[(i-1)%2][j-1] + 1;
-          if (best_length < longest_common_suffix[i%2][j])
-          {
-            best_length = longest_common_suffix[i%2][j];
-            if (offsetY) *offsetY = j - best_length;
-            if (offsetX) *offsetX = i - best_length;
-          }
+        ctx_key_press (ctx, 0, event, 0);
+      }
       }
     }
-  }
-  return best_length;
-} 
+  else
+    {
+      float x, y;
+      event = ctx_nct_get_event (ctx, 20, &ix, &iy);
 
+      x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
+      y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
 
-typedef struct CtxSpan {
-  int from_prev;
-  int start;
-  int length;
-} CtxSpan;
+      if (!strcmp (event, "mouse-press"))
+      {
+        ctx_pointer_press (ctx, x, y, 0, 0);
+        ctxctx->was_down = 1;
+      } else if (!strcmp (event, "mouse-release"))
+      {
+        ctx_pointer_release (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "mouse-motion"))
+      {
+        //nct_set_cursor_pos (backend->term, ix, iy);
+        //nct_flush (backend->term);
+        if (ctxctx->was_down)
+        {
+          ctx_pointer_release (ctx, x, y, 0, 0);
+          ctxctx->was_down = 0;
+        }
+        ctx_pointer_motion (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "mouse-drag"))
+      {
+        ctx_pointer_motion (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "size-changed"))
+      {
+        fprintf (stdout, "\e[H\e[2J\e[?25l");
+        ctxctx->cols = ctx_terminal_cols ();
+        ctxctx->rows = ctx_terminal_rows ();
+        ctxctx->width  = ctx_terminal_width ();
+        ctxctx->height = ctx_terminal_height ();
+        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
 
-#define CHUNK_SIZE 32
-#define MIN_MATCH  7        // minimum match length to be encoded
-#define WINDOW_PADDING 16   // look-aside amount
+        if (prev_frame_contents)
+           free (prev_frame_contents);
+        prev_frame_contents = NULL;
+        prev_frame_len = 0;
+        ctx_set_dirty (ctx, 1);
+        //ctx_key_press (ctx, 0, "size-changed", 0);
+      }
+      else
+      {
+        if (!strcmp (event, "esc"))
+          ctx_key_press (ctx, 0, "escape", 0);
+        else if (!strcmp (event, "space"))
+          ctx_key_press (ctx, 0, "space", 0);
+        else if (!strcmp (event, "enter")||
+                 !strcmp (event, "return"))
+          ctx_key_press (ctx, 0, "\n", 0);
+        else
+        ctx_key_press (ctx, 0, event, 0);
+      }
+    }
 
-#if 0
-static void _dassert(int line, int condition, const char *str, int foo, int bar, int baz)
-{
-  if (!condition)
-  {
-    FILE *f = fopen ("/tmp/cdebug", "a");
-    fprintf (f, "%i: %s    %i %i %i\n", line, str, foo, bar, baz);
-    fclose (f);
-  }
+  return 1;
 }
-#define dassert(cond, foo, bar, baz) _dassert(__LINE__, cond, #cond, foo, bar ,baz)
-#endif
-#define dassert(cond, foo, bar, baz)
 
-/* XXX repeated substring matching is slow, we'll be
- * better off with a hash-table with linked lists of
- * matching 3-4 characters in previous.. or even
- * a naive approach that expects rough alignment..
- */
-static char *encode_in_terms_of_previous (
-                const char *src,  int src_len,
-                const char *prev, int prev_len,
-                int *out_len,
-                int max_ticks)
+int ctx_renderer_is_ctx (Ctx *ctx)
 {
-  CtxString *string = ctx_string_new ("");
-  CtxList *encoded_list = NULL;
-
-  /* TODO : make expected position offset in prev slide based on
-   * matches and not be constant */
-
-  long ticks_start = ctx_ticks ();
-  int start = 0;
-  int length = CHUNK_SIZE;
-  for (start = 0; start < src_len; start += length)
-  {
-    CtxSpan *span = calloc (sizeof (CtxSpan), 1);
-    span->start = start;
-    if (start + length > src_len)
-      span->length = src_len - start;
-    else
-      span->length = length;
-    span->from_prev = 0;
-    ctx_list_append (&encoded_list, span);
-  }
-
-  for (CtxList *l = encoded_list; l; l = l->next)
-  {
-    CtxSpan *span = l->data;
-    if (!span->from_prev)
-    {
-      if (span->length >= MIN_MATCH)
-      {
-         int prev_pos = 0;
-         int curr_pos = 0;
-         assert(1);
-#if 0
-         int prev_start =  0;
-         int prev_window_length = prev_len;
-#else
-         int window_padding = WINDOW_PADDING;
-         int prev_start = span->start - window_padding;
-         if (prev_start < 0)
-           prev_start = 0;
-
-         dassert(span->start>=0 , 0,0,0);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_ctx_free)
+          return 1;
+  return 0;
+}
 
-         int prev_window_length = prev_len - prev_start;
-         if (prev_window_length > span->length + window_padding * 2 + span->start)
-           prev_window_length = span->length + window_padding * 2 + span->start;
-#endif
-         int match_len = 0;
-         if (prev_window_length > 0)
-           match_len = ctx_find_largest_matching_substring(prev + prev_start, src + span->start, 
prev_window_length, span->length, &curr_pos, &prev_pos);
-#if 1
-         prev_pos += prev_start;
 #endif
 
-         if (match_len >= MIN_MATCH)
-         {
-            int start  = span->start;
-            int length = span->length;
-
-            span->from_prev = 1;
-            span->start     = prev_pos;
-            span->length    = match_len;
-            dassert (span->start >= 0, prev_pos, prev_start, span->start);
-            dassert (span->length > 0, prev_pos, prev_start, span->length);
-
-            if (curr_pos)
-            {
-              CtxSpan *prev = calloc (sizeof (CtxSpan), 1);
-              prev->start = start;
-              prev->length =  curr_pos;
-            dassert (prev->start >= 0, prev_pos, prev_start, prev->start);
-            dassert (prev->length > 0, prev_pos, prev_start, prev->length);
-              prev->from_prev = 0;
-              ctx_list_insert_before (&encoded_list, l, prev);
-            }
-
+#if CTX_TILED
+static inline int
+ctx_tiled_threads_done (CtxTiled *tiled)
+{
+  int sum = 0;
+  for (int i = 0; i < _ctx_max_threads; i++)
+  {
+     if (tiled->rendered_frame[i] == tiled->render_frame)
+       sum ++;
+  }
+  return sum;
+}
 
-            if (match_len + curr_pos < start + length)
-            {
-              CtxSpan *next = calloc (sizeof (CtxSpan), 1);
-              next->start = start + curr_pos + match_len;
-              next->length = (start + length) - next->start;
-            dassert (next->start >= 0, prev_pos, prev_start, next->start);
-      //    dassert (next->length > 0, prev_pos, prev_start, next->length);
-              next->from_prev = 0;
-              if (next->length)
-              {
-                if (l->next)
-                  ctx_list_insert_before (&encoded_list, l->next, next);
-                else
-                  ctx_list_append (&encoded_list, next);
-              }
-              else
-                free (next);
-            }
+void ctx_tiled_free (CtxTiled *tiled)
+{
+  tiled->quit = 1;
+  mtx_lock (&tiled->mtx);
+  cnd_broadcast (&tiled->cond);
+  mtx_unlock (&tiled->mtx);
 
-            if (curr_pos) // step one item back for forloop
-            {
-              CtxList *tmp = encoded_list;
-              int found = 0;
-              while (!found && tmp && tmp->next)
-              {
-                if (tmp->next == l)
-                {
-                  l = tmp;
-                  break;
-                }
-                tmp = tmp->next;
-              }
-            }
-         }
-      }
-    }
+  while (tiled->thread_quit < _ctx_max_threads)
+    usleep (1000);
 
-    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
-      break;
+  if (tiled->pixels)
+  {
+    free (tiled->pixels);
+  tiled->pixels = NULL;
+  for (int i = 0 ; i < _ctx_max_threads; i++)
+  {
+    ctx_free (tiled->host[i]);
+    tiled->host[i]=NULL;
   }
 
-  /* merge adjecant prev span references  */
+  ctx_free (tiled->ctx_copy);
+  }
+  // leak?
+}
+
+inline static void ctx_tiled_flush (CtxTiled *tiled)
+{
+  if (tiled->shown_frame == tiled->render_frame)
   {
-    for (CtxList *l = encoded_list; l; l = l->next)
+    int dirty_tiles = 0;
+    ctx_set_drawlist (tiled->ctx_copy, &tiled->ctx->drawlist.entries[0],
+                                           tiled->ctx->drawlist.count * 9);
+    if (_ctx_enable_hash_cache)
     {
-      CtxSpan *span = l->data;
-again:
-      if (l->next)
+      Ctx *hasher = ctx_hasher_new (tiled->width, tiled->height,
+                        CTX_HASH_COLS, CTX_HASH_ROWS);
+      ctx_render_ctx (tiled->ctx_copy, hasher);
+
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+        for (int col = 0; col < CTX_HASH_COLS; col++)
+        {
+          uint8_t *new_hash = ctx_hasher_get_hash (hasher, col, row);
+          if (new_hash && memcmp (new_hash, &tiled->hashes[(row * CTX_HASH_COLS + col) *  20], 20))
+          {
+            memcpy (&tiled->hashes[(row * CTX_HASH_COLS +  col)*20], new_hash, 20);
+            tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
+            dirty_tiles++;
+          }
+          else
+          {
+            tiled->tile_affinity[row * CTX_HASH_COLS + col] = -1;
+          }
+        }
+      free (((CtxHasher*)(hasher->renderer))->hashes);
+      ctx_free (hasher);
+    }
+    else
+    {
+    for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
+        {
+          tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
+          dirty_tiles++;
+        }
+    }
+    int dirty_no = 0;
+    if (dirty_tiles)
+    for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
       {
-        CtxSpan *next_span = l->next->data;
-        if (span->from_prev && next_span->from_prev &&
-            span->start + span->length == 
-            next_span->start)
+        if (tiled->tile_affinity[row * CTX_HASH_COLS + col] != -1)
         {
-           span->length += next_span->length;
-           ctx_list_remove (&encoded_list, next_span);
-           goto again;
+          tiled->tile_affinity[row * CTX_HASH_COLS + col] = dirty_no * (_ctx_max_threads) / dirty_tiles;
+          dirty_no++;
+          if (col > tiled->max_col) tiled->max_col = col;
+          if (col < tiled->min_col) tiled->min_col = col;
+          if (row > tiled->max_row) tiled->max_row = row;
+          if (row < tiled->min_row) tiled->min_row = row;
         }
       }
+
+#if CTX_DAMAGE_CONTROL
+    for (int i = 0; i < tiled->width * tiled->height; i++)
+    {
+      int new_ = (tiled->pixels[i*4+0]+ tiled->pixels[i*4+1]+ tiled->pixels[i*4+2])/3;
+      //if (new_>1) new_--;
+      tiled->pixels[i*4]  = (tiled->pixels[i*4] + 255)/2;
+      tiled->pixels[i*4+1]= (tiled->pixels[i*4+1] + new_)/2;
+      tiled->pixels[i*4+2]= (tiled->pixels[i*4+1] + new_)/2;
     }
+#endif
+
+    tiled->render_frame = ++tiled->frame;
+
+    mtx_lock (&tiled->mtx);
+    cnd_broadcast (&tiled->cond);
+    mtx_unlock (&tiled->mtx);
   }
+}
+static unsigned char *sdl_icc = NULL;
+static long sdl_icc_length = 0;
 
-  while (encoded_list)
+static
+void ctx_tiled_render_fun (void **data)
+{
+  int      no = (size_t)data[0];
+  CtxTiled *tiled = data[1];
+
+  while (!tiled->quit)
   {
-    CtxSpan *span = encoded_list->data;
-    if (span->from_prev)
-    {
-      char ref[128];
-      sprintf (ref, "%c%i %i%c", CTX_CODEC_CHAR, span->start, span->length, CTX_CODEC_CHAR);
-      ctx_string_append_data (string, ref, strlen(ref));
-    }
-    else
+    Ctx *host = tiled->host[no];
+
+    mtx_lock (&tiled->mtx);
+    cnd_wait(&tiled->cond, &tiled->mtx);
+    mtx_unlock (&tiled->mtx);
+
+    if (tiled->render_frame != tiled->rendered_frame[no])
     {
-      for (int i = span->start; i< span->start+span->length; i++)
-      {
-        if (src[i] == CTX_CODEC_CHAR)
-        {
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-        }
-        else
+      int hno = 0;
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
         {
-          ctx_string_append_byte (string, src[i]);
+          if (tiled->tile_affinity[hno]==no)
+          {
+            int x0 = ((tiled->width)/CTX_HASH_COLS) * col;
+            int y0 = ((tiled->height)/CTX_HASH_ROWS) * row;
+            int width = tiled->width / CTX_HASH_COLS;
+            int height = tiled->height / CTX_HASH_ROWS;
+
+            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
+#if 1 // merge horizontally adjecant tiles of same affinity into one job
+            while (col + 1 < CTX_HASH_COLS &&
+                   tiled->tile_affinity[hno+1] == no)
+            {
+              width += tiled->width / CTX_HASH_COLS;
+              col++;
+              hno++;
+            }
+#endif
+            int swap_red_green = ((CtxRasterizer*)(host->renderer))->swap_red_green;
+            ctx_rasterizer_init (rasterizer,
+                                 host, tiled->ctx, &host->state,
+                                 &tiled->pixels[tiled->width * 4 * y0 + x0 * 4],
+                                 0, 0, width, height,
+                                 tiled->width*4, CTX_FORMAT_RGBA8,
+                                 tiled->antialias);
+            ((CtxRasterizer*)(host->renderer))->swap_red_green = swap_red_green;
+            if (sdl_icc_length)
+              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
+
+            ctx_translate (host, -x0, -y0);
+            ctx_render_ctx (tiled->ctx_copy, host);
+          }
         }
-      }
+      tiled->rendered_frame[no] = tiled->render_frame;
     }
-    free (span);
-    ctx_list_remove (&encoded_list, span);
   }
+  tiled->thread_quit++; // need atomic?
+}
+
+#endif
+
+
+#if CTX_EVENTS
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+
+#if CTX_FB
+  #include <linux/fb.h>
+  #include <linux/vt.h>
+  #include <linux/kd.h>
+  #include <sys/mman.h>
+  #include <threads.h>
+  #include <libdrm/drm.h>
+  #include <libdrm/drm_mode.h>
+
+typedef struct _EvSource EvSource;
+ 
+
+struct _EvSource
+{
+  void   *priv; /* private storage  */
+
+  /* returns non 0 if there is events waiting */
+  int   (*has_event) (EvSource *ev_source);
+
+  /* get an event, the returned event should be freed by the caller  */
+  char *(*get_event) (EvSource *ev_source);
+
+  /* destroy/unref this instance */
+  void  (*destroy)   (EvSource *ev_source);
+
+  /* get the underlying fd, useful for using select on  */
+  int   (*get_fd)    (EvSource *ev_source);
+
+
+  void  (*set_coord) (EvSource *ev_source, double x, double y);
+  /* set_coord is needed to warp relative cursors into normalized range,
+   * like normal mice/trackpads/nipples - to obey edges and more.
+   */
+
+  /* if this returns non-0 select can be used for non-blocking.. */
+};
+
+
+typedef struct _CtxFb CtxFb;
+struct _CtxFb
+{
+   CtxTiled tiled;
+#if 0
+   void (*render) (void *fb, CtxCommand *command);
+   void (*reset)  (void *fb);
+   void (*flush)  (void *fb);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard) (void *ctxctx, const char *text);
+   void (*free)   (void *fb);
+   Ctx          *ctx;
+   int           width;
+   int           height;
+   int           cols; // unused
+   int           rows; // unused
+   int           was_down;
+   uint8_t      *pixels;
+   Ctx          *ctx_copy;
+   Ctx          *host[CTX_MAX_THREADS];
+   CtxAntialias  antialias;
+   int           quit;
+   _Atomic int   thread_quit;
+   int           shown_frame;
+   int           render_frame;
+   int           rendered_frame[CTX_MAX_THREADS];
+   int           frame;
+   int           min_col; // hasher cols and rows
+   int           min_row;
+   int           max_col;
+   int           max_row;
+   uint8_t       hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
+   int8_t        tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
+                                                           // responsible for a tile
+                                                           //
+
+
+   int           pointer_down[3];
+#endif
+   int           key_balance;
+   int           key_repeat;
+   int           lctrl;
+   int           lalt;
+   int           rctrl;
+
+   uint8_t      *fb;
+
+   int          fb_fd;
+   char        *fb_path;
+   int          fb_bits;
+   int          fb_bpp;
+   int          fb_mapped_size;
+   struct       fb_var_screeninfo vinfo;
+   struct       fb_fix_screeninfo finfo;
+   int          vt;
+   int          tty;
+   int          vt_active;
+   EvSource    *evsource[4];
+   int          evsource_count;
+   int          is_drm;
+   cnd_t        cond;
+   mtx_t        mtx;
+   struct drm_mode_crtc crtc;
+};
+
+static char *ctx_fb_clipboard = NULL;
+static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
+{
+  if (ctx_fb_clipboard)
+    free (ctx_fb_clipboard);
+  ctx_fb_clipboard = NULL;
+  if (text)
+  {
+    ctx_fb_clipboard = strdup (text);
+  }
+}
 
-  char *ret = string->str;
-  if (out_len) *out_len = string->length;
-  ctx_string_free (string, 0);
-  return ret;
+static char *ctx_fb_get_clipboard (CtxFb *sdl)
+{
+  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
+  return strdup ("");
 }
 
-#if 0 // for documentation/reference purposes
-static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int prev_len, int *out_len)
+#if UINTPTR_MAX == 0xffFFffFF
+  #define fbdrmuint_t uint32_t
+#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
+  #define fbdrmuint_t uint64_t
+#endif
+
+void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
 {
-  CtxString *string = ctx_string_new ("");
-  char reference[32]="";
-  int ref_len = 0;
-  int in_ref = 0;
-  for (int i = 0; i < enc_len; i++)
-  {
-    if (encoded[i] == CTX_CODEC_CHAR)
-    {
-      if (!in_ref)
-      {
-        in_ref = 1;
-      }
-      else
-      {
-        int start = atoi (reference);
-        int len = 0;
-        if (strchr (reference, ' '))
-          len = atoi (strchr (reference, ' ')+1);
+   int got_master = 0;
+   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
+   if (!fb->fb_fd)
+     return NULL;
+   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
+                                         // are used by the flip callback
+   fbdrmuint_t res_fb_buf[20]={0};
+   fbdrmuint_t res_crtc_buf[20]={0};
+   fbdrmuint_t res_enc_buf[20]={0};
+   struct   drm_mode_card_res res={0};
 
-        if (start < 0)start = 0;
-        if (start >= prev_len)start = prev_len-1;
-        if (len + start > prev_len)
-          len = prev_len - start;
+   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
+     goto cleanup;
+   got_master = 1;
 
-        if (start == 0 && len == 0)
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-        else
-          ctx_string_append_data (string, prev + start, len);
-        ref_len = 0;
-        in_ref = 0;
-      }
-    }
-    else
-    {
-      if (in_ref)
-      {
-        if (ref_len < 16)
-        {
-          reference[ref_len++] = encoded[i];
-          reference[ref_len] = 0;
-        }
-      }
-      else
-      ctx_string_append_byte (string, encoded[i]);
-    }
-  }
-  char *ret = string->str;
-  if (out_len) *out_len = string->length;
-  ctx_string_free (string, 0);
-  return ret;
-}
-#endif
+   if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
+     goto cleanup;
+   res.fb_id_ptr=(fbdrmuint_t)res_fb_buf;
+   res.crtc_id_ptr=(fbdrmuint_t)res_crtc_buf;
+   res.connector_id_ptr=(fbdrmuint_t)res_conn_buf;
+   res.encoder_id_ptr=(fbdrmuint_t)res_enc_buf;
+   if(ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
+      goto cleanup;
 
-#define CTX_START_STRING "U\n"  // or " reset "
-#define CTX_END_STRING   "\nX"  // or "\ndone"
-#define CTX_END_STRING2   "\n\e"
 
-int ctx_frame_ack = -1;
-static char *prev_frame_contents = NULL;
-static int   prev_frame_len = 0;
+   unsigned int i;
+   for (i=0;i<res.count_connectors;i++)
+   {
+     struct drm_mode_modeinfo conn_mode_buf[20]={0};
+     fbdrmuint_t conn_prop_buf[20]={0},
+                     conn_propval_buf[20]={0},
+                     conn_enc_buf[20]={0};
 
-static void ctx_ctx_flush (CtxCtx *ctxctx)
-{
-#if 0
-  FILE *debug = fopen ("/tmp/ctx-debug", "a");
-  fprintf (debug, "------\n");
-#endif
+     struct drm_mode_get_connector conn={0};
 
-  if (ctx_native_events)
-    fprintf (stdout, "\e[?201h");
-  fprintf (stdout, "\e[H\e[?25l\e[?200h");
-#if 0
-  fprintf (stdout, CTX_START_STRING);
-  ctx_render_stream (ctxctx->ctx, stdout, 0);
-  fprintf (stdout, CTX_END_STRING);
-#else
-  {
-    int cur_frame_len = 0;
-    char *rest = ctx_render_string (ctxctx->ctx, 0, &cur_frame_len);
-    char *cur_frame_contents = malloc (cur_frame_len + strlen(CTX_START_STRING) + strlen (CTX_END_STRING) + 
1);
+     conn.connector_id=res_conn_buf[i];
 
-    cur_frame_contents[0]=0;
-    strcat (cur_frame_contents, CTX_START_STRING);
-    strcat (cur_frame_contents, rest);
-    strcat (cur_frame_contents, CTX_END_STRING);
-    free (rest);
-    cur_frame_len += strlen (CTX_START_STRING) + strlen (CTX_END_STRING);
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
 
-    if (prev_frame_contents)
-    {
-      char *encoded;
-      int encoded_len = 0;
-      //uint64_t ticks_start = ctx_ticks ();
+     conn.modes_ptr=(fbdrmuint_t)conn_mode_buf;
+     conn.props_ptr=(fbdrmuint_t)conn_prop_buf;
+     conn.prop_values_ptr=(fbdrmuint_t)conn_propval_buf;
+     conn.encoders_ptr=(fbdrmuint_t)conn_enc_buf;
 
-      encoded = encode_in_terms_of_previous (cur_frame_contents, cur_frame_len, prev_frame_contents, 
prev_frame_len, &encoded_len, 1000 * 10);
-//    encoded = strdup (cur_frame_contents);
-//    encoded_len = strlen (encoded);
-      //uint64_t ticks_end = ctx_ticks ();
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
 
-      fwrite (encoded, encoded_len, 1, stdout);
-//    fwrite (encoded, cur_frame_len, 1, stdout);
-#if 0
-      fprintf (debug, "---prev-frame(%i)\n%s", (int)strlen(prev_frame_contents), prev_frame_contents);
-      fprintf (debug, "---cur-frame(%i)\n%s", (int)strlen(cur_frame_contents), cur_frame_contents);
-      fprintf (debug, "---encoded(%.4f %i)---\n%s--------\n",
-                      (ticks_end-ticks_start)/1000.0,
-                      (int)strlen(encoded), encoded);
-#endif
-      free (encoded);
-    }
-    else
-    {
-      fwrite (cur_frame_contents, cur_frame_len, 1, stdout);
-    }
+     //Check if the connector is OK to use (connected to something)
+     if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection)
+       continue;
 
-    if (prev_frame_contents)
-      free (prev_frame_contents);
-    prev_frame_contents = cur_frame_contents;
-    prev_frame_len = cur_frame_len;
-  }
-#endif
-#if 0
-    fclose (debug);
-#endif
-  fprintf (stdout, CTX_END_STRING2);
+//------------------------------------------------------------------------------
+//Creating a dumb buffer
+//------------------------------------------------------------------------------
+     struct drm_mode_create_dumb create_dumb={0};
+     struct drm_mode_map_dumb    map_dumb={0};
+     struct drm_mode_fb_cmd      cmd_dumb={0};
+     create_dumb.width  = conn_mode_buf[0].hdisplay;
+     create_dumb.height = conn_mode_buf[0].vdisplay;
+     create_dumb.bpp   = 32;
+     create_dumb.flags = 0;
+     create_dumb.pitch = 0;
+     create_dumb.size  = 0;
+     create_dumb.handle = 0;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) ||
+         !create_dumb.handle)
+       goto cleanup;
 
-  fprintf (stdout, "\e[5n");
-  fflush (stdout);
+     cmd_dumb.width =create_dumb.width;
+     cmd_dumb.height=create_dumb.height;
+     cmd_dumb.bpp   =create_dumb.bpp;
+     cmd_dumb.pitch =create_dumb.pitch;
+     cmd_dumb.depth =24;
+     cmd_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb))
+       goto cleanup;
+
+     map_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
+       goto cleanup;
+
+     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                       fb->fb_fd, map_dumb.offset);
+     if (!base)
+     {
+       goto cleanup;
+     }
+     *width  = create_dumb.width;
+     *height = create_dumb.height;
+
+     struct drm_mode_get_encoder enc={0};
+     enc.encoder_id=conn.encoder_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
+        goto cleanup;
+
+     fb->crtc.crtc_id=enc.crtc_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
+        goto cleanup;
+
+     fb->crtc.fb_id=cmd_dumb.fb_id;
+     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
+     fb->crtc.count_connectors=1;
+     fb->crtc.mode=conn_mode_buf[0];
+     fb->crtc.mode_valid=1;
+     return base;
+   }
+cleanup:
+   if (got_master)
+     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+   fb->fb_fd = 0;
+   return NULL;
+}
 
-  ctx_frame_ack = 0;
-  do {
-     ctx_consume_events (ctxctx->ctx);
-  } while (ctx_frame_ack != 1);
+void ctx_fbdrm_flip (CtxFb *fb)
+{
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
 }
 
-void ctx_ctx_free (CtxCtx *ctx)
+void ctx_fbdrm_close (CtxFb *fb)
 {
-  nc_at_exit ();
-  free (ctx);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+  close (fb->fb_fd);
+  fb->fb_fd = 0;
 }
 
-Ctx *ctx_new_ctx (int width, int height)
+static void ctx_fb_flip (CtxFb *fb)
 {
-  Ctx *ctx = ctx_new ();
-  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
-  fprintf (stdout, "\e[?1049h");
-  //fprintf (stderr, "\e[H");
-  //fprintf (stderr, "\e[2J");
-  ctx_native_events = 1;
-  if (width <= 0 || height <= 0)
-  {
-    ctxctx->cols = ctx_terminal_cols ();
-    ctxctx->rows = ctx_terminal_rows ();
-    width  = ctxctx->width  = ctx_terminal_width ();
-    height = ctxctx->height = ctx_terminal_height ();
-  }
+  if (fb->is_drm)
+    ctx_fbdrm_flip (fb);
   else
-  {
-    ctxctx->width  = width;
-    ctxctx->height = height;
-    ctxctx->cols   = width / 80;
-    ctxctx->rows   = height / 24;
-  }
-  ctxctx->ctx = ctx;
-  if (!ctx_native_events)
-    _ctx_mouse (ctx, NC_MOUSE_DRAG);
-  ctx_set_renderer (ctx, ctxctx);
-  ctx_set_size (ctx, width, height);
-  ctxctx->flush = (void(*)(void *))ctx_ctx_flush;
-  ctxctx->free  = (void(*)(void *))ctx_ctx_free;
-  return ctx;
+    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
 }
 
-int ctx_ctx_consume_events (Ctx *ctx)
+inline static uint32_t
+ctx_swap_red_green2 (uint32_t orig)
 {
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
-  if (ctx_native_events)
-    {
-      float x = 0, y = 0;
-      int b = 0;
-      char event_type[128]="";
-      event = ctx_native_get_event (ctx, 1000/120);
-#if 0
-      if(event){
-        FILE *file = fopen ("/tmp/log", "a");
-        fprintf (file, "[%s]\n", event);
-        fclose (file);
-      }
-#endif
-      if (event)
-      {
-      sscanf (event, "%s %f %f %i", event_type, &x, &y, &b);
-      if (!strcmp (event_type, "idle"))
+  uint32_t  green_alpha = (orig & 0xff00ff00);
+  uint32_t  red_blue    = (orig & 0x00ff00ff);
+  uint32_t  red         = red_blue << 16;
+  uint32_t  blue        = red_blue >> 16;
+  return green_alpha | red | blue;
+}
+
+static int       fb_cursor_drawn   = 0;
+static int       fb_cursor_drawn_x = 0;
+static int       fb_cursor_drawn_y = 0;
+static CtxCursor fb_cursor_drawn_shape = 0;
+
+
+#define CTX_FB_HIDE_CURSOR_FRAMES 200
+
+static int fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
+
+static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
+{
+  switch (shape)
+  {
+    case CTX_CURSOR_ARROW:
+      if (x > ((size * 4)-y*4)) return 0;
+      if (x < y && x > y / 16)
+        return 1;
+      return 0;
+
+    case CTX_CURSOR_RESIZE_SE:
+    case CTX_CURSOR_RESIZE_NW:
+    case CTX_CURSOR_RESIZE_SW:
+    case CTX_CURSOR_RESIZE_NE:
       {
+        float theta = -45.0/180 * M_PI;
+        float cos_theta;
+        float sin_theta;
+
+        if ((shape == CTX_CURSOR_RESIZE_SW) ||
+            (shape == CTX_CURSOR_RESIZE_NE))
+        {
+          theta = -theta;
+          cos_theta = cos (theta);
+          sin_theta = sin (theta);
+        }
+        else
+        {
+          cos_theta = cos (theta);
+          sin_theta = sin (theta);
+        }
+        int rot_x = x * cos_theta - y * sin_theta;
+        int rot_y = y * cos_theta + x * sin_theta;
+        x = rot_x;
+        y = rot_y;
       }
-      else if (!strcmp (event_type, "mouse-press"))
+      /*FALLTHROUGH*/
+    case CTX_CURSOR_RESIZE_W:
+    case CTX_CURSOR_RESIZE_E:
+    case CTX_CURSOR_RESIZE_ALL:
+      if (abs (x) < size/2 && abs (y) < size/2)
       {
-        ctx_pointer_press (ctx, x, y, b, 0);
+        if (abs(y) < size/10)
+        {
+          return 1;
+        }
       }
-      else if (!strcmp (event_type, "mouse-drag")||
-               !strcmp (event_type, "mouse-motion"))
+      if ((abs (x) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
       {
-        ctx_pointer_motion (ctx, x, y, b, 0);
+        if (abs(y) < (size/2.8)-(abs(x) - (size/2)))
+          return 1;
       }
-      else if (!strcmp (event_type, "mouse-release"))
+      if (shape != CTX_CURSOR_RESIZE_ALL)
+        break;
+      /* FALLTHROUGH */
+    case CTX_CURSOR_RESIZE_S:
+    case CTX_CURSOR_RESIZE_N:
+      if (abs (y) < size/2 && abs (x) < size/2)
       {
-        ctx_pointer_release (ctx, x, y, b, 0);
+        if (abs(x) < size/10)
+        {
+          return 1;
+        }
       }
-      else if (!strcmp (event_type, "message"))
-      {
-        ctx_incoming_message (ctx, event + strlen ("message"), 0);
-      } else if (!strcmp (event, "size-changed"))
+      if ((abs (y) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
       {
-        fprintf (stdout, "\e[H\e[2J\e[?25l");
-        ctxctx->cols = ctx_terminal_cols ();
-        ctxctx->rows = ctx_terminal_rows ();
-        ctxctx->width  = ctx_terminal_width ();
-        ctxctx->height = ctx_terminal_height ();
-        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
-
-        if (prev_frame_contents)
-           free (prev_frame_contents);
-        prev_frame_contents = NULL;
-        prev_frame_len = 0;
-        ctx_set_dirty (ctx, 1);
-        //ctx_key_press (ctx, 0, "size-changed", 0);
+        if (abs(x) < (size/2.8)-(abs(y) - (size/2)))
+          return 1;
       }
-      else
+      break;
+#if 0
+    case CTX_CURSOR_RESIZE_ALL:
+      if (abs (x) < size/2 && abs (y) < size/2)
       {
-        ctx_key_press (ctx, 0, event, 0);
-      }
+        if (abs (x) < size/10 || abs(y) < size/10)
+          return 1;
       }
-    }
-  else
-    {
-      float x, y;
-      event = ctx_nct_get_event (ctx, 20, &ix, &iy);
+      break;
+#endif
+    default:
+      return (x ^ y) & 1;
+  }
+  return 0;
+}
 
-      x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
-      y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
+static void ctx_fb_undraw_cursor (CtxFb *fb)
+{
+    CtxTiled *tiled = (void*)fb;
+    int cursor_size = ctx_height (tiled->ctx) / 28;
 
-      if (!strcmp (event, "mouse-press"))
-      {
-        ctx_pointer_press (ctx, x, y, 0, 0);
-        ctxctx->was_down = 1;
-      } else if (!strcmp (event, "mouse-release"))
-      {
-        ctx_pointer_release (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "mouse-motion"))
+    if (fb_cursor_drawn)
+    {
+      int no = 0;
+      int startx = -cursor_size;
+      int starty = -cursor_size;
+      if (fb_cursor_drawn_shape == CTX_CURSOR_ARROW)
+        startx = starty = 0;
+
+      for (int y = starty; y < cursor_size; y++)
+      for (int x = startx; x < cursor_size; x++, no+=4)
       {
-        //nct_set_cursor_pos (backend->term, ix, iy);
-        //nct_flush (backend->term);
-        if (ctxctx->was_down)
+        if (x + fb_cursor_drawn_x < tiled->width && y + fb_cursor_drawn_y < tiled->height)
         {
-          ctx_pointer_release (ctx, x, y, 0, 0);
-          ctxctx->was_down = 0;
+          if (ctx_is_in_cursor (x, y, cursor_size, fb_cursor_drawn_shape))
+          {
+            int o = ((fb_cursor_drawn_y + y) * tiled->width + (fb_cursor_drawn_x + x)) * 4;
+            fb->fb[o+0]^=0x88;
+            fb->fb[o+1]^=0x88;
+            fb->fb[o+2]^=0x88;
+          }
         }
-        ctx_pointer_motion (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "mouse-drag"))
-      {
-        ctx_pointer_motion (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "size-changed"))
-      {
-        fprintf (stdout, "\e[H\e[2J\e[?25l");
-        ctxctx->cols = ctx_terminal_cols ();
-        ctxctx->rows = ctx_terminal_rows ();
-        ctxctx->width  = ctx_terminal_width ();
-        ctxctx->height = ctx_terminal_height ();
-        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
-
-        if (prev_frame_contents)
-           free (prev_frame_contents);
-        prev_frame_contents = NULL;
-        prev_frame_len = 0;
-        ctx_set_dirty (ctx, 1);
-        //ctx_key_press (ctx, 0, "size-changed", 0);
-      }
-      else
-      {
-        if (!strcmp (event, "esc"))
-          ctx_key_press (ctx, 0, "escape", 0);
-        else if (!strcmp (event, "space"))
-          ctx_key_press (ctx, 0, "space", 0);
-        else if (!strcmp (event, "enter"))
-          ctx_key_press (ctx, 0, "\n", 0);
-        else if (!strcmp (event, "return"))
-          ctx_key_press (ctx, 0, "\n", 0);
-        else
-        ctx_key_press (ctx, 0, event, 0);
       }
+
+    fb_cursor_drawn = 0;
+    }
+}
+
+static void ctx_fb_draw_cursor (CtxFb *fb)
+{
+    CtxTiled *tiled = (void*)fb;
+    int cursor_x    = ctx_pointer_x (tiled->ctx);
+    int cursor_y    = ctx_pointer_y (tiled->ctx);
+    int cursor_size = ctx_height (tiled->ctx) / 28;
+    CtxCursor cursor_shape = tiled->ctx->cursor;
+    int no = 0;
+
+    if (cursor_x == fb_cursor_drawn_x &&
+        cursor_y == fb_cursor_drawn_y &&
+        cursor_shape == fb_cursor_drawn_shape)
+      fb_cursor_same_pos ++;
+    else
+      fb_cursor_same_pos = 0;
+
+    if (fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
+    {
+      if (fb_cursor_drawn)
+        ctx_fb_undraw_cursor (fb);
+      return;
     }
 
-  return 1;
-}
+    /* no need to flicker when stationary, motion flicker can also be removed
+     * by combining the previous and next position masks when a motion has
+     * occured..
+     */
+    if (fb_cursor_same_pos && fb_cursor_drawn)
+      return;
 
-#endif
+    ctx_fb_undraw_cursor (fb);
 
-#if CTX_EVENTS
+    no = 0;
 
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <signal.h>
+    int startx = -cursor_size;
+    int starty = -cursor_size;
 
-#if CTX_FB
-  #include <linux/fb.h>
-  #include <linux/vt.h>
-  #include <linux/kd.h>
-  #include <sys/mman.h>
-  #include <threads.h>
-  #include <libdrm/drm.h>
-  #include <libdrm/drm_mode.h>
+    if (cursor_shape == CTX_CURSOR_ARROW)
+      startx = starty = 0;
 
-typedef struct _EvSource EvSource;
+    for (int y = starty; y < cursor_size; y++)
+      for (int x = startx; x < cursor_size; x++, no+=4)
+      {
+        if (x + cursor_x < tiled->width && y + cursor_y < tiled->height)
+        {
+          if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape))
+          {
+            int o = ((cursor_y + y) * tiled->width + (cursor_x + x)) * 4;
+            fb->fb[o+0]^=0x88;
+            fb->fb[o+1]^=0x88;
+            fb->fb[o+2]^=0x88;
+          }
+        }
+      }
+    fb_cursor_drawn = 1;
+    fb_cursor_drawn_x = cursor_x;
+    fb_cursor_drawn_y = cursor_y;
+    fb_cursor_drawn_shape = cursor_shape;
+}
 
-struct _EvSource
+static void ctx_fb_show_frame (CtxFb *fb, int block)
 {
-  void   *priv; /* private storage  */
+  CtxTiled *tiled = (void*)fb;
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    if (block == 0) // consume event call
+    {
+      ctx_fb_draw_cursor (fb);
+      ctx_fb_flip (fb);
+    }
+    return;
+  }
 
-  /* returns non 0 if there is events waiting */
-  int   (*has_event) (EvSource *ev_source);
+  if (block)
+  {
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+    {
+      usleep (500);
+      count ++;
+      if (count > 2000)
+      {
+        tiled->shown_frame = tiled->render_frame;
+        return;
+      }
+    }
+  }
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
 
-  /* get an event, the returned event should be freed by the caller  */
-  char *(*get_event) (EvSource *ev_source);
+    if (fb->vt_active)
+    {
+       int pre_skip = tiled->min_row * tiled->height/CTX_HASH_ROWS * tiled->width;
+       int post_skip = (CTX_HASH_ROWS-tiled->max_row-1) * tiled->height/CTX_HASH_ROWS * tiled->width;
 
-  /* destroy/unref this instance */
-  void  (*destroy)   (EvSource *ev_source);
+       int rows = ((tiled->width * tiled->height) - pre_skip - post_skip)/tiled->width;
 
-  /* get the underlying fd, useful for using select on  */
-  int   (*get_fd)    (EvSource *ev_source);
+       int col_pre_skip = tiled->min_col * tiled->width/CTX_HASH_COLS;
+       int col_post_skip = (CTX_HASH_COLS-tiled->max_col-1) * tiled->width/CTX_HASH_COLS;
+#if CTX_DAMAGE_CONTROL
+       pre_skip = post_skip = col_pre_skip = col_post_skip = 0;
+#endif
 
+       if (pre_skip < 0) pre_skip = 0;
+       if (post_skip < 0) post_skip = 0;
 
-  void  (*set_coord) (EvSource *ev_source, double x, double y);
-  /* set_coord is needed to warp relative cursors into normalized range,
-   * like normal mice/trackpads/nipples - to obey edges and more.
-   */
+     __u32 dummy = 0;
 
-  /* if this returns non-0 select can be used for non-blocking.. */
-};
+       if (tiled->min_row == 100){
+          pre_skip = 0;
+          post_skip = 0;
+          // not when drm ?
+          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+          ctx_fb_undraw_cursor (fb);
+       }
+       else
+       {
 
+      tiled->min_row = 100;
+      tiled->max_row = 0;
+      tiled->min_col = 100;
+      tiled->max_col = 0;
 
-typedef struct _CtxFb CtxFb;
-struct _CtxFb
-{
-   void (*render) (void *fb, CtxCommand *command);
-   void (*reset)  (void *fb);
-   void (*flush)  (void *fb);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)   (void *fb);
-   Ctx          *ctx;
-   int           width;
-   int           height;
-   int           cols; // unused
-   int           rows; // unused
-   int           was_down;
-   uint8_t      *scratch_fb;
-   Ctx          *ctx_copy;
-   Ctx          *host[CTX_MAX_THREADS];
-   CtxAntialias  antialias;
-   int           quit;
-   _Atomic int   thread_quit;
-   int           shown_frame;
-   int           render_frame;
-   int           rendered_frame[CTX_MAX_THREADS];
-   int           frame;
-   int           min_col; // hasher cols and rows
-   int           min_row;
-   int           max_col;
-   int           max_row;
-   uint8_t       hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
-   int8_t        tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
-                                                           // responsible for a tile
-                                                           //
+     // not when drm ?
+     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+     ctx_fb_undraw_cursor (fb);
+     switch (fb->fb_bits)
+     {
+       case 32:
+#if 1
+         {
+           uint8_t *dst = fb->fb + pre_skip * 4;
+           uint8_t *src = tiled->pixels + pre_skip * 4;
+           int pre = col_pre_skip * 4;
+           int post = col_post_skip * 4;
+           int core = tiled->width * 4 - pre - post;
+           for (int i = 0; i < rows; i++)
+           {
+             dst  += pre;
+             src  += pre;
+             memcpy (dst, src, core);
+             src  += core;
+             dst  += core;
+             dst  += post;
+             src  += post;
+           }
+         }
+#else
+         { int count = tiled->width * tiled->height;
+           const uint32_t *src = (void*)tiled->pixels;
+           uint32_t *dst = (void*)fb->fb;
+           count-= pre_skip;
+           src+= pre_skip;
+           dst+= pre_skip;
+           count-= post_skip;
+           while (count -- > 0)
+           {
+             dst[0] = ctx_swap_red_green2 (src[0]);
+             src++;
+             dst++;
+           }
+         }
+#endif
+         break;
+         /* XXX  :  note: converting a scanline (or all) to target and
+          * then doing a bulk memcpy be faster (at least with som /dev/fbs)  */
+       case 24:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 3;
+           count-= post_skip;
+           while (count -- > 0)
+           {
+             dst[0] = src[0];
+             dst[1] = src[1];
+             dst[2] = src[2];
+             dst+=3;
+             src+=4;
+           }
+         }
+         break;
+       case 16:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 2;
+           while (count -- > 0)
+           {
+             int big = ((src[0] >> 3)) +
+                ((src[1] >> 2)<<5) +
+                ((src[2] >> 3)<<11);
+             dst[0] = big & 255;
+             dst[1] = big >>  8;
+             dst+=2;
+             src+=4;
+           }
+         }
+         break;
+       case 15:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 2;
+           while (count -- > 0)
+           {
+             int big = ((src[2] >> 3)) +
+                       ((src[1] >> 2)<<5) +
+                       ((src[0] >> 3)<<10);
+             dst[0] = big & 255;
+             dst[1] = big >>  8;
+             dst+=2;
+             src+=4;
+           }
+         }
+         break;
+       case 8:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip;
+           while (count -- > 0)
+           {
+             dst[0] = ((src[0] >> 5)) +
+                      ((src[1] >> 5)<<3) +
+                      ((src[2] >> 6)<<6);
+             dst+=1;
+             src+=4;
+           }
+         }
+         break;
+     }
+    }
+    fb_cursor_drawn = 0;
+    ctx_fb_draw_cursor (fb);
+    ctx_fb_flip (fb);
+    tiled->shown_frame = tiled->render_frame;
+  }
+}
 
 
+#define evsource_has_event(es)   (es)->has_event((es))
+#define evsource_get_event(es)   (es)->get_event((es))
+#define evsource_destroy(es)     do{if((es)->destroy)(es)->destroy((es));}while(0)
+#define evsource_set_coord(es,x,y) do{if((es)->set_coord)(es)->set_coord((es),(x),(y));}while(0)
+#define evsource_get_fd(es)      ((es)->get_fd?(es)->get_fd((es)):0)
 
-   int           pointer_down[3];
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
 
-   uint8_t      *fb;
 
-   int          fb_fd;
-   char        *fb_path;
-   int          fb_bits;
-   int          fb_bpp;
-   int          fb_mapped_size;
-   struct       fb_var_screeninfo vinfo;
-   struct       fb_fix_screeninfo finfo;
-   int          vt;
-   int          tty;
-   int          vt_active;
-   EvSource    *evsource[4];
-   int          evsource_count;
-   int          is_drm;
-   cnd_t        cond;
-   mtx_t        mtx;
-   struct drm_mode_crtc crtc;
+static int mice_has_event ();
+static char *mice_get_event ();
+static void mice_destroy ();
+static int mice_get_fd (EvSource *ev_source);
+static void mice_set_coord (EvSource *ev_source, double x, double y);
+
+static EvSource ev_src_mice = {
+  NULL,
+  (void*)mice_has_event,
+  (void*)mice_get_event,
+  (void*)mice_destroy,
+  mice_get_fd,
+  mice_set_coord
 };
 
-static char *ctx_fb_clipboard = NULL;
-static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
+typedef struct Mice
 {
-  if (ctx_fb_clipboard)
-    free (ctx_fb_clipboard);
-  ctx_fb_clipboard = NULL;
-  if (text)
-  {
-    ctx_fb_clipboard = strdup (text);
-  }
-}
+  int     fd;
+  double  x;
+  double  y;
+  int     button;
+  int     prev_state;
+} Mice;
 
-static char *ctx_fb_get_clipboard (CtxFb *sdl)
+Mice *_mrg_evsrc_coord = NULL;
+
+void _mmm_get_coords (Ctx *ctx, double *x, double *y)
 {
-  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
-  return strdup ("");
+  if (!_mrg_evsrc_coord)
+    return;
+  if (x)
+    *x = _mrg_evsrc_coord->x;
+  if (y)
+    *y = _mrg_evsrc_coord->y;
 }
 
-#if UINTPTR_MAX == 0xffFFffFF
-  #define fbdrmuint_t uint32_t
-#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
-  #define fbdrmuint_t uint64_t
-#endif
+static Mice  mice;
+static Mice* mrg_mice_this = &mice;
 
-void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
+static int mmm_evsource_mice_init ()
 {
-   int got_master = 0;
-   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
-   if (!fb->fb_fd)
-     return NULL;
-   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
-                                         // are used by the flip callback
-   fbdrmuint_t res_fb_buf[20]={0};
-   fbdrmuint_t res_crtc_buf[20]={0};
-   fbdrmuint_t res_enc_buf[20]={0};
-   struct   drm_mode_card_res res={0};
-
-   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
-     goto cleanup;
-   got_master = 1;
+  unsigned char reset[]={0xff};
+  /* need to detect which event */
 
-   if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
-     goto cleanup;
-   res.fb_id_ptr=(fbdrmuint_t)res_fb_buf;
-   res.crtc_id_ptr=(fbdrmuint_t)res_crtc_buf;
-   res.connector_id_ptr=(fbdrmuint_t)res_conn_buf;
-   res.encoder_id_ptr=(fbdrmuint_t)res_enc_buf;
-   if(ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
-      goto cleanup;
+  mrg_mice_this->prev_state = 0;
+  mrg_mice_this->fd = open ("/dev/input/mice", O_RDONLY | O_NONBLOCK);
+  if (mrg_mice_this->fd == -1)
+  {
+    fprintf (stderr, "error opening /dev/input/mice device, maybe add user to input group if such group 
exist, or otherwise make the rights be satisfied.\n");
+    return -1;
+  }
+  if (write (mrg_mice_this->fd, reset, 1) == -1)
+  {
+    // might happen if we're a regular user with only read permission
+  }
+  _mrg_evsrc_coord = mrg_mice_this;
+  return 0;
+}
 
+static void mice_destroy ()
+{
+  if (mrg_mice_this->fd != -1)
+    close (mrg_mice_this->fd);
+}
 
-   unsigned int i;
-   for (i=0;i<res.count_connectors;i++)
-   {
-     struct drm_mode_modeinfo conn_mode_buf[20]={0};
-     fbdrmuint_t conn_prop_buf[20]={0},
-                     conn_propval_buf[20]={0},
-                     conn_enc_buf[20]={0};
+static int mice_has_event ()
+{
+  struct timeval tv;
+  int retval;
 
-     struct drm_mode_get_connector conn={0};
+  if (mrg_mice_this->fd == -1)
+    return 0;
 
-     conn.connector_id=res_conn_buf[i];
+  fd_set rfds;
+  FD_ZERO (&rfds);
+  FD_SET(mrg_mice_this->fd, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = 0;
+  retval = select (mrg_mice_this->fd+1, &rfds, NULL, NULL, &tv);
+  if (retval == 1)
+    return FD_ISSET (mrg_mice_this->fd, &rfds);
+  return 0;
+}
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
+static char *mice_get_event ()
+{
+  const char *ret = "mouse-motion";
+  double relx, rely;
+  signed char buf[3];
+  int n_read = 0;
+  CtxFb *fb = ev_src_mice.priv;
+  CtxTiled *tiled = (void*)fb;
+  n_read = read (mrg_mice_this->fd, buf, 3);
+  if (n_read == 0)
+     return strdup ("");
+  relx = buf[1];
+  rely = -buf[2];
 
-     conn.modes_ptr=(fbdrmuint_t)conn_mode_buf;
-     conn.props_ptr=(fbdrmuint_t)conn_prop_buf;
-     conn.prop_values_ptr=(fbdrmuint_t)conn_propval_buf;
-     conn.encoders_ptr=(fbdrmuint_t)conn_enc_buf;
+  if (relx < 0)
+  {
+    if (relx > -6)
+    relx = - relx*relx;
+    else
+    relx = -36;
+  }
+  else
+  {
+    if (relx < 6)
+    relx = relx*relx;
+    else
+    relx = 36;
+  }
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
+  if (rely < 0)
+  {
+    if (rely > -6)
+    rely = - rely*rely;
+    else
+    rely = -36;
+  }
+  else
+  {
+    if (rely < 6)
+    rely = rely*rely;
+    else
+    rely = 36;
+  }
 
-     //Check if the connector is OK to use (connected to something)
-     if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection)
-       continue;
+  mrg_mice_this->x += relx;
+  mrg_mice_this->y += rely;
 
-//------------------------------------------------------------------------------
-//Creating a dumb buffer
-//------------------------------------------------------------------------------
-     struct drm_mode_create_dumb create_dumb={0};
-     struct drm_mode_map_dumb    map_dumb={0};
-     struct drm_mode_fb_cmd      cmd_dumb={0};
-     create_dumb.width  = conn_mode_buf[0].hdisplay;
-     create_dumb.height = conn_mode_buf[0].vdisplay;
-     create_dumb.bpp   = 32;
-     create_dumb.flags = 0;
-     create_dumb.pitch = 0;
-     create_dumb.size  = 0;
-     create_dumb.handle = 0;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) ||
-         !create_dumb.handle)
-       goto cleanup;
+  if (mrg_mice_this->x < 0)
+    mrg_mice_this->x = 0;
+  if (mrg_mice_this->y < 0)
+    mrg_mice_this->y = 0;
+  if (mrg_mice_this->x >= tiled->width)
+    mrg_mice_this->x = tiled->width -1;
+  if (mrg_mice_this->y >= tiled->height)
+    mrg_mice_this->y = tiled->height -1;
+  int button = 0;
+  
+  if ((mrg_mice_this->prev_state & 1) != (buf[0] & 1))
+    {
+      if (buf[0] & 1)
+        {
+          ret = "mouse-press";
+        }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 1;
+    }
+  else if (buf[0] & 1)
+  {
+    ret = "mouse-drag";
+    button = 1;
+  }
 
-     cmd_dumb.width =create_dumb.width;
-     cmd_dumb.height=create_dumb.height;
-     cmd_dumb.bpp   =create_dumb.bpp;
-     cmd_dumb.pitch =create_dumb.pitch;
-     cmd_dumb.depth =24;
-     cmd_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb))
-       goto cleanup;
+  if (!button)
+  {
+    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
+    {
+      if (buf[0] & 2)
+        {
+          ret = "mouse-press";
+        }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 3;
+    }
+    else if (buf[0] & 2)
+    {
+      ret = "mouse-drag";
+      button = 3;
+    }
+  }
 
-     map_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
-       goto cleanup;
+  if (!button)
+  {
+    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
+    {
+      if (buf[0] & 4)
+        {
+          ret = "mouse-press";
+        }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 2;
+    }
+    else if (buf[0] & 4)
+    {
+      ret = "mouse-drag";
+      button = 2;
+    }
+  }
 
-     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
-                       fb->fb_fd, map_dumb.offset);
-     if (!base)
-     {
-       goto cleanup;
-     }
-     *width  = create_dumb.width;
-     *height = create_dumb.height;
+  mrg_mice_this->prev_state = buf[0];
 
-     struct drm_mode_get_encoder enc={0};
-     enc.encoder_id=conn.encoder_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
-        goto cleanup;
+  //if (!is_active (ev_src_mice.priv))
+  //  return NULL;
 
-     fb->crtc.crtc_id=enc.crtc_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
-        goto cleanup;
+  {
+    char *r = malloc (64);
+    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
+    return r;
+  }
 
-     fb->crtc.fb_id=cmd_dumb.fb_id;
-     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
-     fb->crtc.count_connectors=1;
-     fb->crtc.mode=conn_mode_buf[0];
-     fb->crtc.mode_valid=1;
-     return base;
-   }
-cleanup:
-   if (got_master)
-     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-   fb->fb_fd = 0;
-   return NULL;
+  return NULL;
 }
 
-void ctx_fbdrm_flip (CtxFb *fb)
+static int mice_get_fd (EvSource *ev_source)
 {
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
+  return mrg_mice_this->fd;
 }
 
-void ctx_fbdrm_close (CtxFb *fb)
+static void mice_set_coord (EvSource *ev_source, double x, double y)
 {
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-  close (fb->fb_fd);
-  fb->fb_fd = 0;
+  mrg_mice_this->x = x;
+  mrg_mice_this->y = y;
 }
 
-static void ctx_fb_flip (CtxFb *fb)
+EvSource *evsource_mice_new (void)
 {
-  if (fb->is_drm)
-    ctx_fbdrm_flip (fb);
-  else
-    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+  if (mmm_evsource_mice_init () == 0)
+    {
+      mrg_mice_this->x = 0;
+      mrg_mice_this->y = 0;
+      return &ev_src_mice;
+    }
+  return NULL;
 }
 
-static inline int
-fb_render_threads_done (CtxFb *fb)
-{
-  int sum = 0;
-  for (int i = 0; i < _ctx_max_threads; i++)
-  {
-     if (fb->rendered_frame[i] == fb->render_frame)
-       sum ++;
-  }
-  return sum;
-}
+static int evsource_kb_has_event (void);
+static char *evsource_kb_get_event (void);
+static void evsource_kb_destroy (int sign);
+static int evsource_kb_get_fd (void);
 
-inline static uint32_t
-ctx_swap_red_green2 (uint32_t orig)
+/* kept out of struct to be reachable by atexit */
+static EvSource ev_src_kb = {
+  NULL,
+  (void*)evsource_kb_has_event,
+  (void*)evsource_kb_get_event,
+  (void*)evsource_kb_destroy,
+  (void*)evsource_kb_get_fd,
+  NULL
+};
+
+static struct termios orig_attr;
+
+int is_active (void *host)
 {
-  uint32_t  green_alpha = (orig & 0xff00ff00);
-  uint32_t  red_blue    = (orig & 0x00ff00ff);
-  uint32_t  red         = red_blue << 16;
-  uint32_t  blue        = red_blue >> 16;
-  return green_alpha | red | blue;
+  return 1;
 }
+static void real_evsource_kb_destroy (int sign)
+{
+  static int done = 0;
 
-static int       fb_cursor_drawn   = 0;
-static int       fb_cursor_drawn_x = 0;
-static int       fb_cursor_drawn_y = 0;
-static CtxCursor fb_cursor_drawn_shape = CTX_CURSOR_ARROW;
+  if (sign == 0)
+    return;
 
+  if (done)
+    return;
+  done = 1;
 
-#define CTX_FB_HIDE_CURSOR_FRAMES 200
+  switch (sign)
+  {
+    case  -11:break; /* will be called from atexit with sign==-11 */
+    case   SIGSEGV: break;//fprintf (stderr, " SIGSEGV\n");break;
+    case   SIGABRT: fprintf (stderr, " SIGABRT\n");break;
+    case   SIGBUS:  fprintf (stderr, " SIGBUS\n");break;
+    case   SIGKILL: fprintf (stderr, " SIGKILL\n");break;
+    case   SIGINT:  fprintf (stderr, " SIGINT\n");break;
+    case   SIGTERM: fprintf (stderr, " SIGTERM\n");break;
+    case   SIGQUIT: fprintf (stderr, " SIGQUIT\n");break;
+    default: fprintf (stderr, "sign: %i\n", sign);
+             fprintf (stderr, "%i %i %i %i %i %i %i\n", SIGSEGV, SIGABRT, SIGBUS, SIGKILL, SIGINT, SIGTERM, 
SIGQUIT);
+  }
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  //fprintf (stderr, "evsource kb destroy\n");
+}
 
-static int fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
+static void evsource_kb_destroy (int sign)
+{
+  real_evsource_kb_destroy (-11);
+}
 
-static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
+static int evsource_kb_init ()
 {
-  switch (shape)
-  {
-    case CTX_CURSOR_ARROW:
-      if (x > ((size * 4)-y*4)) return 0;
-      if (x < y && x > y / 16)
-        return 1;
-      return 0;
+//  ioctl(STDIN_FILENO, KDSKBMODE, K_RAW);
+  atexit ((void*) real_evsource_kb_destroy);
+  signal (SIGSEGV, (void*) real_evsource_kb_destroy);
+  signal (SIGABRT, (void*) real_evsource_kb_destroy);
+  signal (SIGBUS,  (void*) real_evsource_kb_destroy);
+  signal (SIGKILL, (void*) real_evsource_kb_destroy);
+  signal (SIGINT,  (void*) real_evsource_kb_destroy);
+  signal (SIGTERM, (void*) real_evsource_kb_destroy);
+  signal (SIGQUIT, (void*) real_evsource_kb_destroy);
 
-    case CTX_CURSOR_RESIZE_SE:
-    case CTX_CURSOR_RESIZE_NW:
-    case CTX_CURSOR_RESIZE_SW:
-    case CTX_CURSOR_RESIZE_NE:
-      {
-        float theta = -45.0/180 * M_PI;
-        float cos_theta;
-        float sin_theta;
+  struct termios raw;
+  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+    {
+      fprintf (stderr, "error initializing keyboard\n");
+      return -1;
+    }
+  raw = orig_attr;
+
+  cfmakeraw (&raw);
+
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0; // XXX? return other value?
 
-        if ((shape == CTX_CURSOR_RESIZE_SW) ||
-            (shape == CTX_CURSOR_RESIZE_NE))
-        {
-          theta = -theta;
-          cos_theta = cos (theta);
-          sin_theta = sin (theta);
-        }
-        else
-        {
-          //cos_theta = -0.707106781186548;
-          cos_theta = cos (theta);
-          sin_theta = sin (theta);
-        }
-        int rot_x = x * cos_theta - y * sin_theta;
-        int rot_y = y * cos_theta + x * sin_theta;
-        x = rot_x;
-        y = rot_y;
-      }
-      /*FALLTHROUGH*/
-    case CTX_CURSOR_RESIZE_W:
-    case CTX_CURSOR_RESIZE_E:
-    case CTX_CURSOR_RESIZE_ALL:
-      if (abs (x) < size/2 && abs (y) < size/2)
-      {
-        if (abs(y) < size/10)
-        {
-          return 1;
-        }
-      }
-      if ((abs (x) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
-      {
-        if (abs(y) < (size/2.8)-(abs(x) - (size/2)))
-          return 1;
-      }
-      if (shape != CTX_CURSOR_RESIZE_ALL)
-        break;
-      /* FALLTHROUGH */
-    case CTX_CURSOR_RESIZE_S:
-    case CTX_CURSOR_RESIZE_N:
-      if (abs (y) < size/2 && abs (x) < size/2)
-      {
-        if (abs(x) < size/10)
-        {
-          return 1;
-        }
-      }
-      if ((abs (y) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
-      {
-        if (abs(x) < (size/2.8)-(abs(y) - (size/2)))
-          return 1;
-      }
-      break;
-#if 0
-    case CTX_CURSOR_RESIZE_ALL:
-      if (abs (x) < size/2 && abs (y) < size/2)
-      {
-        if (abs (x) < size/10 || abs(y) < size/10)
-          return 1;
-      }
-      break;
-#endif
-    default:
-      return (x ^ y) & 1;
-  }
   return 0;
 }
+static int evsource_kb_has_event (void)
+{
+  struct timeval tv;
+  int retval;
 
-static void ctx_fb_undraw_cursor (CtxFb *fb)
-  {
-    int cursor_size = ctx_height (fb->ctx) / 28;
-
-    if (fb_cursor_drawn)
-    {
-      int no = 0;
-      for (int y = -cursor_size; y < cursor_size; y++)
-      for (int x = -cursor_size; x < cursor_size; x++, no+=4)
-      {
-        if (x + fb_cursor_drawn_x < fb->width && y + fb_cursor_drawn_y < fb->height)
-        {
-          if (ctx_is_in_cursor (x, y, cursor_size, fb_cursor_drawn_shape))
-          {
-            int o = ((fb_cursor_drawn_y + y) * fb->width + (fb_cursor_drawn_x + x)) * 4;
-            fb->fb[o+0]^=0x88;
-            fb->fb[o+1]^=0x88;
-            fb->fb[o+2]^=0x88;
-          }
-        }
-      }
-    fb_cursor_drawn = 0;
-    }
+  fd_set rfds;
+  FD_ZERO (&rfds);
+  FD_SET(STDIN_FILENO, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = 0;
+  retval = select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
+  return retval == 1;
 }
 
-static void ctx_fb_draw_cursor (CtxFb *fb)
-  {
-    int cursor_x    = ctx_pointer_x (fb->ctx);
-    int cursor_y    = ctx_pointer_y (fb->ctx);
-    int cursor_size = ctx_height (fb->ctx) / 28;
-    CtxCursor cursor_shape = fb->ctx->cursor;
-    int no = 0;
+/* note that a nick can have multiple occurences, the labels
+ * should be kept the same for all occurences of a combination.
+ *
+ * this table is taken from nchanterm.
+ */
+typedef struct MmmKeyCode {
+  char *nick;          /* programmers name for key */
+  char  sequence[10];  /* terminal sequence */
+} MmmKeyCode;
+static const MmmKeyCode ufb_keycodes[]={
+  {"up",                  "\e[A"},
+  {"down",                "\e[B"},
+  {"right",               "\e[C"},
+  {"left",                "\e[D"},
 
-    if (cursor_x == fb_cursor_drawn_x &&
-        cursor_y == fb_cursor_drawn_y &&
-        cursor_shape == fb_cursor_drawn_shape)
-      fb_cursor_same_pos ++;
-    else
-      fb_cursor_same_pos = 0;
+  {"shift-up",            "\e[1;2A"},
+  {"shift-down",          "\e[1;2B"},
+  {"shift-right",         "\e[1;2C"},
+  {"shift-left",          "\e[1;2D"},
 
-    if (fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
-    {
-      if (fb_cursor_drawn)
-        ctx_fb_undraw_cursor (fb);
-      return;
-    }
+  {"alt-up",              "\e[1;3A"},
+  {"alt-down",            "\e[1;3B"},
+  {"alt-right",           "\e[1;3C"},
+  {"alt-left",            "\e[1;3D"},
+  {"alt-shift-up",         "\e[1;4A"},
+  {"alt-shift-down",       "\e[1;4B"},
+  {"alt-shift-right",      "\e[1;4C"},
+  {"alt-shift-left",       "\e[1;4D"},
 
-    /* no need to flicker when stationary, motion flicker can also be removed
-     * by combining the previous and next position masks when a motion has
-     * occured..
-     */
-    if (fb_cursor_same_pos && fb_cursor_drawn)
-      return;
+  {"control-up",          "\e[1;5A"},
+  {"control-down",        "\e[1;5B"},
+  {"control-right",       "\e[1;5C"},
+  {"control-left",        "\e[1;5D"},
 
-    ctx_fb_undraw_cursor (fb);
+  /* putty */
+  {"control-up",          "\eOA"},
+  {"control-down",        "\eOB"},
+  {"control-right",       "\eOC"},
+  {"control-left",        "\eOD"},
 
-    no = 0;
-    for (int y = -cursor_size; y < cursor_size; y++)
-      for (int x = -cursor_size; x < cursor_size; x++, no+=4)
-      {
-        if (x + cursor_x < fb->width && y + cursor_y < fb->height)
-        {
-          if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape))
-          {
-            int o = ((cursor_y + y) * fb->width + (cursor_x + x)) * 4;
-            fb->fb[o+0]^=0x88;
-            fb->fb[o+1]^=0x88;
-            fb->fb[o+2]^=0x88;
-          }
-        }
-      }
-    fb_cursor_drawn = 1;
-    fb_cursor_drawn_x = cursor_x;
-    fb_cursor_drawn_y = cursor_y;
-    fb_cursor_drawn_shape = cursor_shape;
-  }
+  {"control-shift-up",    "\e[1;6A"},
+  {"control-shift-down",  "\e[1;6B"},
+  {"control-shift-right", "\e[1;6C"},
+  {"control-shift-left",  "\e[1;6D"},
 
-static void ctx_fb_show_frame (CtxFb *fb, int block)
-{
-  if (fb->shown_frame == fb->render_frame)
-  {
-    if (block == 0) // consume event call
-    {
-      ctx_fb_draw_cursor (fb);
-      ctx_fb_flip (fb);
-    }
-    return;
-  }
+  {"control-up",          "\eOa"},
+  {"control-down",        "\eOb"},
+  {"control-right",       "\eOc"},
+  {"control-left",        "\eOd"},
+
+  {"shift-up",            "\e[a"},
+  {"shift-down",          "\e[b"},
+  {"shift-right",         "\e[c"},
+  {"shift-left",          "\e[d"},
+
+  {"insert",              "\e[2~"},
+  {"delete",              "\e[3~"},
+  {"page-up",             "\e[5~"},
+  {"page-down",           "\e[6~"},
+  {"home",                "\eOH"},
+  {"end",                 "\eOF"},
+  {"home",                "\e[H"},
+  {"end",                 "\e[F"},
+ {"control-delete",       "\e[3;5~"},
+  {"shift-delete",        "\e[3;2~"},
+  {"control-shift-delete","\e[3;6~"},
+
+  {"F1",         "\e[25~"},
+  {"F2",         "\e[26~"},
+  {"F3",         "\e[27~"},
+  {"F4",         "\e[26~"},
+
+
+  {"F1",         "\e[11~"},
+  {"F2",         "\e[12~"},
+  {"F3",         "\e[13~"},
+  {"F4",         "\e[14~"},
+  {"F1",         "\eOP"},
+  {"F2",         "\eOQ"},
+  {"F3",         "\eOR"},
+  {"F4",         "\eOS"},
+  {"F5",         "\e[15~"},
+  {"F6",         "\e[16~"},
+  {"F7",         "\e[17~"},
+  {"F8",         "\e[18~"},
+  {"F9",         "\e[19~"},
+  {"F9",         "\e[20~"},
+  {"F10",        "\e[21~"},
+  {"F11",        "\e[22~"},
+  {"F12",        "\e[23~"},
+  {"tab",         {9, '\0'}},
+  {"shift-tab",   {27, 9, '\0'}}, // also generated by alt-tab in linux console
+  {"alt-space",   {27, ' ', '\0'}},
+  {"shift-tab",   "\e[Z"},
+  {"backspace",   {127, '\0'}},
+  {"space",       " "},
+  {"\e",          "\e"},
+  {"return",      {10,0}},
+  {"return",      {13,0}},
+  /* this section could be autogenerated by code */
+  {"control-a",   {1,0}},
+  {"control-b",   {2,0}},
+  {"control-c",   {3,0}},
+  {"control-d",   {4,0}},
+  {"control-e",   {5,0}},
+  {"control-f",   {6,0}},
+  {"control-g",   {7,0}},
+  {"control-h",   {8,0}}, /* backspace? */
+  {"control-i",   {9,0}},
+  {"control-j",   {10,0}},
+  {"control-k",   {11,0}},
+  {"control-l",   {12,0}},
+  {"control-n",   {14,0}},
+  {"control-o",   {15,0}},
+  {"control-p",   {16,0}},
+  {"control-q",   {17,0}},
+  {"control-r",   {18,0}},
+  {"control-s",   {19,0}},
+  {"control-t",   {20,0}},
+  {"control-u",   {21,0}},
+  {"control-v",   {22,0}},
+  {"control-w",   {23,0}},
+  {"control-x",   {24,0}},
+  {"control-y",   {25,0}},
+  {"control-z",   {26,0}},
+  {"alt-`",       "\e`"},
+  {"alt-0",       "\e0"},
+  {"alt-1",       "\e1"},
+  {"alt-2",       "\e2"},
+  {"alt-3",       "\e3"},
+  {"alt-4",       "\e4"},
+  {"alt-5",       "\e5"},
+  {"alt-6",       "\e6"},
+  {"alt-7",       "\e7"}, /* backspace? */
+  {"alt-8",       "\e8"},
+  {"alt-9",       "\e9"},
+  {"alt-+",       "\e+"},
+  {"alt--",       "\e-"},
+  {"alt-/",       "\e/"},
+  {"alt-a",       "\ea"},
+  {"alt-b",       "\eb"},
+  {"alt-c",       "\ec"},
+  {"alt-d",       "\ed"},
+  {"alt-e",       "\ee"},
+  {"alt-f",       "\ef"},
+  {"alt-g",       "\eg"},
+  {"alt-h",       "\eh"}, /* backspace? */
+  {"alt-i",       "\ei"},
+  {"alt-j",       "\ej"},
+  {"alt-k",       "\ek"},
+  {"alt-l",       "\el"},
+  {"alt-n",       "\em"},
+  {"alt-n",       "\en"},
+  {"alt-o",       "\eo"},
+  {"alt-p",       "\ep"},
+  {"alt-q",       "\eq"},
+  {"alt-r",       "\er"},
+  {"alt-s",       "\es"},
+  {"alt-t",       "\et"},
+  {"alt-u",       "\eu"},
+  {"alt-v",       "\ev"},
+  {"alt-w",       "\ew"},
+  {"alt-x",       "\ex"},
+  {"alt-y",       "\ey"},
+  {"alt-z",       "\ez"},
+  /* Linux Console  */
+  {"home",       "\e[1~"},
+  {"end",        "\e[4~"},
+  {"F1",         "\e[[A"},
+  {"F2",         "\e[[B"},
+  {"F3",         "\e[[C"},
+  {"F4",         "\e[[D"},
+  {"F5",         "\e[[E"},
+  {"F6",         "\e[[F"},
+  {"F7",         "\e[[G"},
+  {"F8",         "\e[[H"},
+  {"F9",         "\e[[I"},
+  {"F10",        "\e[[J"},
+  {"F11",        "\e[[K"},
+  {"F12",        "\e[[L"},
+  {NULL, }
+};
+static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyCode **ret)
+{
+  int i;
+  int matches = 0;
 
-  if (block)
-  {
-    int count = 0;
-    while (fb_render_threads_done (fb) != _ctx_max_threads)
+  if (!strncmp (buf, "\e[M", MIN(length,3)))
     {
-      usleep (500);
-      count ++;
-      if (count > 2000)
+      if (length >= 6)
+        return 9001;
+      return 2342;
+    }
+  for (i = 0; ufb_keycodes[i].nick; i++)
+    if (!strncmp (buf, ufb_keycodes[i].sequence, length))
       {
-        fb->shown_frame = fb->render_frame;
-        return;
+        matches ++;
+        if ((int)strlen (ufb_keycodes[i].sequence) == length && ret)
+          {
+            *ret = &ufb_keycodes[i];
+            return 1;
+          }
       }
-    }
-  }
-  else
-  {
-    if (fb_render_threads_done (fb) != _ctx_max_threads)
-      return;
-  }
-
-    if (fb->vt_active)
-    {
-       int pre_skip = fb->min_row * fb->height/CTX_HASH_ROWS * fb->width;
-       int post_skip = (CTX_HASH_ROWS-fb->max_row-1) * fb->height/CTX_HASH_ROWS * fb->width;
-
-       int rows = ((fb->width * fb->height) - pre_skip - post_skip)/fb->width;
-
-       int col_pre_skip = fb->min_col * fb->width/CTX_HASH_COLS;
-       int col_post_skip = (CTX_HASH_COLS-fb->max_col-1) * fb->width/CTX_HASH_COLS;
-#if CTX_DAMAGE_CONTROL
-       pre_skip = post_skip = col_pre_skip = col_post_skip = 0;
-#endif
-
-       if (pre_skip < 0) pre_skip = 0;
-       if (post_skip < 0) post_skip = 0;
-
-     __u32 dummy = 0;
-
-       if (fb->min_row == 100){
-          pre_skip = 0;
-          post_skip = 0;
-          // not when drm ?
-          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
-          ctx_fb_undraw_cursor (fb);
-       }
-       else
-       {
-
-      fb->min_row = 100;
-      fb->max_row = 0;
-      fb->min_col = 100;
-      fb->max_col = 0;
-
-     // not when drm ?
-     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
-     ctx_fb_undraw_cursor (fb);
-     switch (fb->fb_bits)
-     {
-       case 32:
-#if 1
-         {
-           uint8_t *dst = fb->fb + pre_skip * 4;
-           uint8_t *src = fb->scratch_fb + pre_skip * 4;
-           int pre = col_pre_skip * 4;
-           int post = col_post_skip * 4;
-           int core = fb->width * 4 - pre - post;
-           for (int i = 0; i < rows; i++)
-           {
-             dst  += pre;
-             src  += pre;
-             memcpy (dst, src, core);
-             src  += core;
-             dst  += core;
-             dst  += post;
-             src  += post;
-           }
-         }
-#else
-         { int count = fb->width * fb->height;
-           const uint32_t *src = (void*)fb->scratch_fb;
-           uint32_t *dst = (void*)fb->fb;
-           count-= pre_skip;
-           src+= pre_skip;
-           dst+= pre_skip;
-           count-= post_skip;
-           while (count -- > 0)
-           {
-             dst[0] = ctx_swap_red_green2 (src[0]);
-             src++;
-             dst++;
-           }
-         }
-#endif
-         break;
-         /* XXX  :  note: converting a scanline (or all) to target and
-          * then doing a bulk memcpy be faster (at least with som /dev/fbs)  */
-       case 24:
-         { int count = fb->width * fb->height;
-           const uint8_t *src = fb->scratch_fb;
-           uint8_t *dst = fb->fb;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 3;
-           count-= post_skip;
-           while (count -- > 0)
-           {
-             dst[0] = src[0];
-             dst[1] = src[1];
-             dst[2] = src[2];
-             dst+=3;
-             src+=4;
-           }
-         }
-         break;
-       case 16:
-         { int count = fb->width * fb->height;
-           const uint8_t *src = fb->scratch_fb;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 2;
-           while (count -- > 0)
-           {
-             int big = ((src[0] >> 3)) +
-                ((src[1] >> 2)<<5) +
-                ((src[2] >> 3)<<11);
-             dst[0] = big & 255;
-             dst[1] = big >>  8;
-             dst+=2;
-             src+=4;
-           }
-         }
-         break;
-       case 15:
-         { int count = fb->width * fb->height;
-           const uint8_t *src = fb->scratch_fb;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 2;
-           while (count -- > 0)
-           {
-             int big = ((src[2] >> 3)) +
-                       ((src[1] >> 2)<<5) +
-                       ((src[0] >> 3)<<10);
-             dst[0] = big & 255;
-             dst[1] = big >>  8;
-             dst+=2;
-             src+=4;
-           }
-         }
-         break;
-       case 8:
-         { int count = fb->width * fb->height;
-           const uint8_t *src = fb->scratch_fb;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip;
-           while (count -- > 0)
-           {
-             dst[0] = ((src[0] >> 5)) +
-                      ((src[1] >> 5)<<3) +
-                      ((src[2] >> 6)<<6);
-             dst+=1;
-             src+=4;
-           }
-         }
-         break;
-     }
-    }
-    fb_cursor_drawn = 0;
-    ctx_fb_draw_cursor (fb);
-    ctx_fb_flip (fb);
-    fb->shown_frame = fb->render_frame;
-  }
+  if (matches != 1 && ret)
+    *ret = NULL;
+  return matches==1?2:matches;
 }
 
+static char *evsource_kb_get_event (void)
+{
+  unsigned char buf[20];
+  int length;
 
-#define evsource_has_event(es)   (es)->has_event((es))
-#define evsource_get_event(es)   (es)->get_event((es))
-#define evsource_destroy(es)     do{if((es)->destroy)(es)->destroy((es));}while(0)
-#define evsource_set_coord(es,x,y) do{if((es)->set_coord)(es)->set_coord((es),(x),(y));}while(0)
-#define evsource_get_fd(es)      ((es)->get_fd?(es)->get_fd((es)):0)
 
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const MmmKeyCode *match = NULL;
 
+        if (!is_active (ev_src_kb.priv))
+           return NULL;
 
-static int mice_has_event ();
-static char *mice_get_event ();
-static void mice_destroy ();
-static int mice_get_fd (EvSource *ev_source);
-static void mice_set_coord (EvSource *ev_source, double x, double y);
+        /* special case ESC, so that we can use it alone in keybindings */
+        if (length == 0 && buf[0] == 27)
+          {
+            struct timeval tv;
+            fd_set rfds;
+            FD_ZERO (&rfds);
+            FD_SET (STDIN_FILENO, &rfds);
+            tv.tv_sec = 0;
+            tv.tv_usec = 1000 * 120;
+            if (select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 0)
+              return strdup ("escape");
+          }
 
-static EvSource ev_src_mice = {
-  NULL,
-  (void*)mice_has_event,
-  (void*)mice_get_event,
-  (void*)mice_destroy,
-  mice_get_fd,
-  mice_set_coord
-};
+        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
+          {
+            case 1: /* unique match */
+              if (!match)
+                return NULL;
+              return strdup (match->nick);
+              break;
+            case 0: /* no matches, bail*/
+             {
+                static char ret[256]="";
+                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
+                                                             * single unicode
+                                                             * utf8 character
+                                                             */
+                  {
+                    int bytes = read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
+                    if (bytes)
+                    {
+                      buf[ctx_utf8_len(buf[0])]=0;
+                      strcpy (ret, (void*)buf);
+                    }
+                    return strdup(ret); //XXX: simplify
+                  }
+                if (length == 0) /* ascii */
+                  {
+                    buf[1]=0;
+                    strcpy (ret, (void*)buf);
+                    return strdup(ret);
+                  }
+                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
+                    length >=0 ? buf[0] : 0,
+                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
+                    length >=1 ? buf[1] : 0,
+                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
+                    length >=2 ? buf[2] : 0,
+                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
+                    length >=3 ? buf[3] : 0,
+                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
+                    length >=4 ? buf[4] : 0,
+                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
+                    length >=5 ? buf[5] : 0,
+                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
+                    length >=6 ? buf[6] : 0,
+                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
+                    );
+                return strdup(ret);
+            }
+              return NULL;
+            default: /* continue */
+              break;
+          }
+      }
+    else
+      return strdup("key read eek");
+  return strdup("fail");
+}
 
-typedef struct Mice
+static int evsource_kb_get_fd (void)
 {
-  int     fd;
-  double  x;
-  double  y;
-  int     button;
-  int     prev_state;
-} Mice;
+  return STDIN_FILENO;
+}
 
-Mice *_mrg_evsrc_coord = NULL;
 
-void _mmm_get_coords (Ctx *ctx, double *x, double *y)
+EvSource *evsource_kb_new (void)
 {
-  if (!_mrg_evsrc_coord)
-    return;
-  if (x)
-    *x = _mrg_evsrc_coord->x;
-  if (y)
-    *y = _mrg_evsrc_coord->y;
+  if (evsource_kb_init() == 0)
+  {
+    return &ev_src_kb;
+  }
+  return NULL;
 }
 
-static Mice  mice;
-static Mice* mrg_mice_this = &mice;
-
-static int mmm_evsource_mice_init ()
+static int event_check_pending (CtxFb *fb)
 {
-  unsigned char reset[]={0xff};
-  /* need to detect which event */
-
-  mrg_mice_this->prev_state = 0;
-  mrg_mice_this->fd = open ("/dev/input/mice", O_RDONLY | O_NONBLOCK);
-  if (mrg_mice_this->fd == -1)
+  CtxTiled *tiled = (void*)fb;
+  int events = 0;
+  for (int i = 0; i < fb->evsource_count; i++)
   {
-    fprintf (stderr, "error opening /dev/input/mice device, maybe add user to input group if such group 
exist, or otherwise make the rights be satisfied.\n");
-    return -1;
+    while (evsource_has_event (fb->evsource[i]))
+    {
+      char *event = evsource_get_event (fb->evsource[i]);
+      if (event)
+      {
+        if (fb->vt_active)
+        {
+          ctx_key_press (tiled->ctx, 0, event, 0); // we deliver all events as key-press, the key_press 
handler disambiguates
+          events++;
+        }
+        free (event);
+      }
+    }
   }
-  write (mrg_mice_this->fd, reset, 1);
-  _mrg_evsrc_coord = mrg_mice_this;
+  return events;
+}
+
+int ctx_fb_consume_events (Ctx *ctx)
+{
+  CtxFb *fb = (void*)ctx->renderer;
+  ctx_fb_show_frame (fb, 0);
+  event_check_pending (fb);
   return 0;
 }
 
-static void mice_destroy ()
+inline static void ctx_fb_reset (CtxFb *fb)
 {
-  if (mrg_mice_this->fd != -1)
-    close (mrg_mice_this->fd);
+  ctx_fb_show_frame (fb, 1);
 }
 
-static int mice_has_event ()
+inline static void ctx_fb_flush (CtxFb *fb)
 {
-  struct timeval tv;
-  int retval;
+  ctx_tiled_flush ((CtxTiled*)fb);
+}
 
-  if (mrg_mice_this->fd == -1)
-    return 0;
+void ctx_fb_free (CtxFb *fb)
+{
+  if (fb->is_drm)
+  {
+    ctx_fbdrm_close (fb);
+  }
 
-  fd_set rfds;
-  FD_ZERO (&rfds);
-  FD_SET(mrg_mice_this->fd, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = 0;
-  retval = select (mrg_mice_this->fd+1, &rfds, NULL, NULL, &tv);
-  if (retval == 1)
-    return FD_ISSET (mrg_mice_this->fd, &rfds);
-  return 0;
+  ioctl (0, KDSETMODE, KD_TEXT);
+  if (system("stty sane")){};
+  ctx_tiled_free ((CtxTiled*)fb);
+  //free (fb);
 }
 
-static char *mice_get_event ()
+//static unsigned char *fb_icc = NULL;
+//static long fb_icc_length = 0;
+
+int ctx_renderer_is_fb (Ctx *ctx)
 {
-  const char *ret = "mouse-motion";
-  double relx, rely;
-  signed char buf[3];
-  CtxFb *fb = ev_src_mice.priv;
-  read (mrg_mice_this->fd, buf, 3);
-  relx = buf[1];
-  rely = -buf[2];
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_fb_free)
+          return 1;
+  return 0;
+}
 
-  if (relx < 0)
+static CtxFb *ctx_fb = NULL;
+static void vt_switch_cb (int sig)
+{
+  CtxTiled *tiled = (void*)ctx_fb;
+  if (sig == SIGUSR1)
   {
-    if (relx > -6)
-    relx = - relx*relx;
-    else
-    relx = -36;
+    if (ctx_fb->is_drm)
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+    ioctl (0, VT_RELDISP, 1);
+    ctx_fb->vt_active = 0;
+    ioctl (0, KDSETMODE, KD_TEXT);
   }
   else
   {
-    if (relx < 6)
-    relx = relx*relx;
+    ioctl (0, VT_RELDISP, VT_ACKACQ);
+    ctx_fb->vt_active = 1;
+    // queue draw
+    tiled->render_frame = ++tiled->frame;
+    ioctl (0, KDSETMODE, KD_GRAPHICS);
+    if (ctx_fb->is_drm)
+    {
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_SET_MASTER, 0);
+      ctx_fb_flip (ctx_fb);
+    }
     else
-    relx = 36;
+    {
+      tiled->ctx->dirty=1;
+
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
+      {
+        tiled->hashes[(row * CTX_HASH_COLS + col) *  20] += 1;
+      }
+    }
   }
+}
 
-  if (rely < 0)
+Ctx *ctx_new_fb (int width, int height, int drm)
+{
+#if CTX_RASTERIZER
+  CtxFb *fb = calloc (sizeof (CtxFb), 1);
+
+  CtxTiled *tiled = (void*)fb;
+  ctx_fb = fb;
+  if (drm)
+    fb->fb = ctx_fbdrm_new (fb, &tiled->width, &tiled->height);
+  if (fb->fb)
   {
-    if (rely > -6)
-    rely = - rely*rely;
-    else
-    rely = -36;
+    fb->is_drm         = 1;
+    width              = tiled->width;
+    height             = tiled->height;
+    /*
+       we're ignoring the input width and height ,
+       maybe turn them into properties - for
+       more generic handling.
+     */
+    fb->fb_mapped_size = tiled->width * tiled->height * 4;
+    fb->fb_bits        = 32;
+    fb->fb_bpp         = 4;
   }
   else
   {
-    if (rely < 6)
-    rely = rely*rely;
+  fb->fb_fd = open ("/dev/fb0", O_RDWR);
+  if (fb->fb_fd > 0)
+    fb->fb_path = strdup ("/dev/fb0");
+  else
+  {
+    fb->fb_fd = open ("/dev/graphics/fb0", O_RDWR);
+    if (fb->fb_fd > 0)
+    {
+      fb->fb_path = strdup ("/dev/graphics/fb0");
+    }
     else
-    rely = 36;
-  }
-
-  mrg_mice_this->x += relx;
-  mrg_mice_this->y += rely;
-
-  if (mrg_mice_this->x < 0)
-    mrg_mice_this->x = 0;
-  if (mrg_mice_this->y < 0)
-    mrg_mice_this->y = 0;
-  if (mrg_mice_this->x >= fb->width)
-    mrg_mice_this->x = fb->width -1;
-  if (mrg_mice_this->y >= fb->height)
-    mrg_mice_this->y = fb->height -1;
-  int button = 0;
-  
-  if ((mrg_mice_this->prev_state & 1) != (buf[0] & 1))
     {
-      if (buf[0] & 1)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 1;
+      free (fb);
+      return NULL;
     }
-  else if (buf[0] & 1)
-  {
-    ret = "mouse-drag";
-    button = 1;
   }
 
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
+  if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
     {
-      if (buf[0] & 2)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 3;
+      fprintf (stderr, "error getting fbinfo\n");
+      close (fb->fb_fd);
+      free (fb->fb_path);
+      free (fb);
+      return NULL;
     }
-    else if (buf[0] & 2)
+
+   if (ioctl(fb->fb_fd, FBIOGET_VSCREENINFO, &fb->vinfo))
+     {
+       fprintf (stderr, "error getting fbinfo\n");
+      close (fb->fb_fd);
+      free (fb->fb_path);
+      free (fb);
+      return NULL;
+     }
+
+//fprintf (stderr, "%s\n", fb->fb_path);
+  width = tiled->width = fb->vinfo.xres;
+  height = tiled->height = fb->vinfo.yres;
+
+  fb->fb_bits = fb->vinfo.bits_per_pixel;
+//fprintf (stderr, "fb bits: %i\n", fb->fb_bits);
+
+  if (fb->fb_bits == 16)
+    fb->fb_bits =
+      fb->vinfo.red.length +
+      fb->vinfo.green.length +
+      fb->vinfo.blue.length;
+
+   else if (fb->fb_bits == 8)
+  {
+    unsigned short red[256],  green[256],  blue[256];
+    unsigned short original_red[256];
+    unsigned short original_green[256];
+    unsigned short original_blue[256];
+    struct fb_cmap cmap = {0, 256, red, green, blue, NULL};
+    struct fb_cmap original_cmap = {0, 256, original_red, original_green, original_blue, NULL};
+    int i;
+
+    /* do we really need to restore it ? */
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
     {
-      ret = "mouse-drag";
-      button = 3;
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
     }
-  }
 
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
+    for (i = 0; i < 256; i++)
     {
-      if (buf[0] & 4)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 2;
+      red[i]   = ((( i >> 5) & 0x7) << 5) << 8;
+      green[i] = ((( i >> 2) & 0x7) << 5) << 8;
+      blue[i]  = ((( i >> 0) & 0x3) << 6) << 8;
     }
-    else if (buf[0] & 4)
+
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &cmap) == -1)
     {
-      ret = "mouse-drag";
-      button = 2;
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
     }
   }
 
-  mrg_mice_this->prev_state = buf[0];
-
-  //if (!is_active (ev_src_mice.priv))
-  //  return NULL;
-
-  {
-    char *r = malloc (64);
-    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
-    return r;
+  fb->fb_bpp = fb->vinfo.bits_per_pixel / 8;
+  fb->fb_mapped_size = fb->finfo.smem_len;
+                                              
+  fb->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
   }
+  if (!fb->fb)
+    return NULL;
+  tiled->pixels = calloc (fb->fb_mapped_size, 1);
+  ctx_fb_events = 1;
 
-  return NULL;
-}
+#if CTX_BABL
+  babl_init ();
+#endif
 
-static int mice_get_fd (EvSource *ev_source)
-{
-  return mrg_mice_this->fd;
-}
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
 
-static void mice_set_coord (EvSource *ev_source, double x, double y)
-{
-  mrg_mice_this->x = x;
-  mrg_mice_this->y = y;
-}
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
 
-EvSource *evsource_mice_new (void)
-{
-  if (mmm_evsource_mice_init () == 0)
-    {
-      mrg_mice_this->x = 0;
-      mrg_mice_this->y = 0;
-      return &ev_src_mice;
-    }
-  return NULL;
-}
+  ctx_set_renderer (tiled->ctx, fb);
+  ctx_set_renderer (tiled->ctx_copy, fb);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
 
+  ctx_set_size (tiled->ctx, width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
 
+  tiled->flush = (void*)ctx_fb_flush;
+  tiled->reset = (void*)ctx_fb_reset;
+  tiled->free  = (void*)ctx_fb_free;
+  tiled->set_clipboard = (void*)ctx_fb_set_clipboard;
+  tiled->get_clipboard = (void*)ctx_fb_get_clipboard;
 
-static int evsource_kb_has_event (void);
-static char *evsource_kb_get_event (void);
-static void evsource_kb_destroy (int sign);
-static int evsource_kb_get_fd (void);
+  for (int i = 0; i < _ctx_max_threads; i++)
+  {
+    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
+                   tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
+                   tiled->width * 4, CTX_FORMAT_RGBA8); // this format
+                                  // is overriden in  thread
+    ((CtxRasterizer*)(tiled->host[i]->renderer))->swap_red_green = 1;
+    ctx_set_texture_source (tiled->host[i], tiled->ctx);
+  }
 
-/* kept out of struct to be reachable by atexit */
-static EvSource ev_src_kb = {
-  NULL,
-  (void*)evsource_kb_has_event,
-  (void*)evsource_kb_get_event,
-  (void*)evsource_kb_destroy,
-  (void*)evsource_kb_get_fd,
-  NULL
-};
+  mtx_init (&tiled->mtx, mtx_plain);
+  cnd_init (&tiled->cond);
 
-static struct termios orig_attr;
+#define start_thread(no)\
+  if(_ctx_max_threads>no){ \
+    static void *args[2]={(void*)no, };\
+    thrd_t tid;\
+    args[1]=fb;\
+    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
+  }
+  start_thread(0);
+  start_thread(1);
+  start_thread(2);
+  start_thread(3);
+  start_thread(4);
+  start_thread(5);
+  start_thread(6);
+  start_thread(7);
+  start_thread(8);
+  start_thread(9);
+  start_thread(10);
+  start_thread(11);
+  start_thread(12);
+  start_thread(13);
+  start_thread(14);
+  start_thread(15);
+#undef start_thread
 
-int is_active (void *host)
-{
-  return 1;
-}
-static void real_evsource_kb_destroy (int sign)
-{
-  static int done = 0;
+  ctx_flush (tiled->ctx);
 
-  if (sign == 0)
-    return;
+  EvSource *kb = evsource_kb_new ();
+  if (kb)
+  {
+    fb->evsource[fb->evsource_count++] = kb;
+    kb->priv = fb;
+  }
+  EvSource *mice  = evsource_mice_new ();
+  if (mice)
+  {
+    fb->evsource[fb->evsource_count++] = mice;
+    mice->priv = fb;
+  }
 
-  if (done)
-    return;
-  done = 1;
+  fb->vt_active = 1;
+  ioctl(0, KDSETMODE, KD_GRAPHICS);
+  signal (SIGUSR1, vt_switch_cb);
+  signal (SIGUSR2, vt_switch_cb);
+  struct vt_stat st;
+  if (ioctl (0, VT_GETSTATE, &st) == -1)
+  {
+    ctx_log ("VT_GET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
+  }
 
-  switch (sign)
+  fb->vt = st.v_active;
+
+  struct vt_mode mode;
+  mode.mode   = VT_PROCESS;
+  mode.relsig = SIGUSR1;
+  mode.acqsig = SIGUSR2;
+  if (ioctl (0, VT_SETMODE, &mode) < 0)
   {
-    case  -11:break; /* will be called from atexit with sign==-11 */
-    case   SIGSEGV: fprintf (stderr, " SIGSEGV\n");break;
-    case   SIGABRT: fprintf (stderr, " SIGABRT\n");break;
-    case   SIGBUS: fprintf (stderr, " SIGBUS\n");break;
-    case   SIGKILL: fprintf (stderr, " SIGKILL\n");break;
-    case   SIGINT: fprintf (stderr, " SIGINT\n");break;
-    case   SIGTERM: fprintf (stderr, " SIGTERM\n");break;
-    case   SIGQUIT: fprintf (stderr, " SIGQUIT\n");break;
-    default: fprintf (stderr, "sign: %i\n", sign);
-             fprintf (stderr, "%i %i %i %i %i %i %i\n", SIGSEGV, SIGABRT, SIGBUS, SIGKILL, SIGINT, SIGTERM, 
SIGQUIT);
+    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
   }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  //fprintf (stderr, "evsource kb destroy\n");
-}
 
-static void evsource_kb_destroy (int sign)
-{
-  real_evsource_kb_destroy (-11);
+  return tiled->ctx;
+#else
+  return NULL;
+#endif
 }
+#else
 
-static int evsource_kb_init ()
+int ctx_renderer_is_fb (Ctx *ctx)
 {
-//  ioctl(STDIN_FILENO, KDSKBMODE, K_RAW);
-  atexit ((void*) real_evsource_kb_destroy);
-  signal (SIGSEGV, (void*) real_evsource_kb_destroy);
-  signal (SIGABRT, (void*) real_evsource_kb_destroy);
-  signal (SIGBUS,  (void*) real_evsource_kb_destroy);
-  signal (SIGKILL, (void*) real_evsource_kb_destroy);
-  signal (SIGINT,  (void*) real_evsource_kb_destroy);
-  signal (SIGTERM, (void*) real_evsource_kb_destroy);
-  signal (SIGQUIT, (void*) real_evsource_kb_destroy);
-
-  struct termios raw;
-  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
-    {
-      fprintf (stderr, "error initializing keyboard\n");
-      return -1;
-    }
-  raw = orig_attr;
-
-  cfmakeraw (&raw);
-
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return 0; // XXX? return other value?
-
   return 0;
 }
-static int evsource_kb_has_event (void)
-{
-  struct timeval tv;
-  int retval;
-
-  fd_set rfds;
-  FD_ZERO (&rfds);
-  FD_SET(STDIN_FILENO, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = 0;
-  retval = select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
-  return retval == 1;
-}
-
-/* note that a nick can have multiple occurences, the labels
- * should be kept the same for all occurences of a combination.
- *
- * this table is taken from nchanterm.
- */
-typedef struct MmmKeyCode {
-  char *nick;          /* programmers name for key */
-  char  sequence[10];  /* terminal sequence */
-} MmmKeyCode;
-static const MmmKeyCode ufb_keycodes[]={
-  {"up",                  "\e[A"},
-  {"down",                "\e[B"},
-  {"right",               "\e[C"},
-  {"left",                "\e[D"},
-
-  {"shift-up",            "\e[1;2A"},
-  {"shift-down",          "\e[1;2B"},
-  {"shift-right",         "\e[1;2C"},
-  {"shift-left",          "\e[1;2D"},
-
-  {"alt-up",              "\e[1;3A"},
-  {"alt-down",            "\e[1;3B"},
-  {"alt-right",           "\e[1;3C"},
-  {"alt-left",            "\e[1;3D"},
-  {"alt-shift-up",         "\e[1;4A"},
-  {"alt-shift-down",       "\e[1;4B"},
-  {"alt-shift-right",      "\e[1;4C"},
-  {"alt-shift-left",       "\e[1;4D"},
-
-  {"control-up",          "\e[1;5A"},
-  {"control-down",        "\e[1;5B"},
-  {"control-right",       "\e[1;5C"},
-  {"control-left",        "\e[1;5D"},
-
-  /* putty */
-  {"control-up",          "\eOA"},
-  {"control-down",        "\eOB"},
-  {"control-right",       "\eOC"},
-  {"control-left",        "\eOD"},
-
-  {"control-shift-up",    "\e[1;6A"},
-  {"control-shift-down",  "\e[1;6B"},
-  {"control-shift-right", "\e[1;6C"},
-  {"control-shift-left",  "\e[1;6D"},
-
-  {"control-up",          "\eOa"},
-  {"control-down",        "\eOb"},
-  {"control-right",       "\eOc"},
-  {"control-left",        "\eOd"},
-
-  {"shift-up",            "\e[a"},
-  {"shift-down",          "\e[b"},
-  {"shift-right",         "\e[c"},
-  {"shift-left",          "\e[d"},
+#endif
+#endif
 
-  {"insert",              "\e[2~"},
-  {"delete",              "\e[3~"},
-  {"page-up",             "\e[5~"},
-  {"page-down",           "\e[6~"},
-  {"home",                "\eOH"},
-  {"end",                 "\eOF"},
-  {"home",                "\e[H"},
-  {"end",                 "\e[F"},
- {"control-delete",       "\e[3;5~"},
-  {"shift-delete",        "\e[3;2~"},
-  {"control-shift-delete","\e[3;6~"},
+#if CTX_SDL
 
-  {"F1",         "\e[25~"},
-  {"F2",         "\e[26~"},
-  {"F3",         "\e[27~"},
-  {"F4",         "\e[26~"},
+/**/
 
+typedef struct _CtxSDL CtxSDL;
+struct _CtxSDL
+{
+   CtxTiled  tiled;
+   /* where we diverge from fb*/
+   int           key_balance;
+   int           key_repeat;
+   int           lctrl;
+   int           lalt;
+   int           rctrl;
+   int           lshift;
+   int           rshift;
 
-  {"F1",         "\e[11~"},
-  {"F2",         "\e[12~"},
-  {"F3",         "\e[13~"},
-  {"F4",         "\e[14~"},
-  {"F1",         "\eOP"},
-  {"F2",         "\eOQ"},
-  {"F3",         "\eOR"},
-  {"F4",         "\eOS"},
-  {"F5",         "\e[15~"},
-  {"F6",         "\e[16~"},
-  {"F7",         "\e[17~"},
-  {"F8",         "\e[18~"},
-  {"F9",         "\e[19~"},
-  {"F9",         "\e[20~"},
-  {"F10",        "\e[21~"},
-  {"F11",        "\e[22~"},
-  {"F12",        "\e[23~"},
-  {"tab",         {9, '\0'}},
-  {"shift-tab",   {27, 9, '\0'}}, // also generated by alt-tab in linux console
-  {"alt-space",   {27, ' ', '\0'}},
-  {"shift-tab",   "\e[Z"},
-  {"backspace",   {127, '\0'}},
-  {"space",       " "},
-  {"\e",          "\e"},
-  {"return",      {10,0}},
-  {"return",      {13,0}},
-  /* this section could be autogenerated by code */
-  {"control-a",   {1,0}},
-  {"control-b",   {2,0}},
-  {"control-c",   {3,0}},
-  {"control-d",   {4,0}},
-  {"control-e",   {5,0}},
-  {"control-f",   {6,0}},
-  {"control-g",   {7,0}},
-  {"control-h",   {8,0}}, /* backspace? */
-  {"control-i",   {9,0}},
-  {"control-j",   {10,0}},
-  {"control-k",   {11,0}},
-  {"control-l",   {12,0}},
-  {"control-n",   {14,0}},
-  {"control-o",   {15,0}},
-  {"control-p",   {16,0}},
-  {"control-q",   {17,0}},
-  {"control-r",   {18,0}},
-  {"control-s",   {19,0}},
-  {"control-t",   {20,0}},
-  {"control-u",   {21,0}},
-  {"control-v",   {22,0}},
-  {"control-w",   {23,0}},
-  {"control-x",   {24,0}},
-  {"control-y",   {25,0}},
-  {"control-z",   {26,0}},
-  {"alt-`",       "\e`"},
-  {"alt-0",       "\e0"},
-  {"alt-1",       "\e1"},
-  {"alt-2",       "\e2"},
-  {"alt-3",       "\e3"},
-  {"alt-4",       "\e4"},
-  {"alt-5",       "\e5"},
-  {"alt-6",       "\e6"},
-  {"alt-7",       "\e7"}, /* backspace? */
-  {"alt-8",       "\e8"},
-  {"alt-9",       "\e9"},
-  {"alt-+",       "\e+"},
-  {"alt--",       "\e-"},
-  {"alt-/",       "\e/"},
-  {"alt-a",       "\ea"},
-  {"alt-b",       "\eb"},
-  {"alt-c",       "\ec"},
-  {"alt-d",       "\ed"},
-  {"alt-e",       "\ee"},
-  {"alt-f",       "\ef"},
-  {"alt-g",       "\eg"},
-  {"alt-h",       "\eh"}, /* backspace? */
-  {"alt-i",       "\ei"},
-  {"alt-j",       "\ej"},
-  {"alt-k",       "\ek"},
-  {"alt-l",       "\el"},
-  {"alt-n",       "\em"},
-  {"alt-n",       "\en"},
-  {"alt-o",       "\eo"},
-  {"alt-p",       "\ep"},
-  {"alt-q",       "\eq"},
-  {"alt-r",       "\er"},
-  {"alt-s",       "\es"},
-  {"alt-t",       "\et"},
-  {"alt-u",       "\eu"},
-  {"alt-v",       "\ev"},
-  {"alt-w",       "\ew"},
-  {"alt-x",       "\ex"},
-  {"alt-y",       "\ey"},
-  {"alt-z",       "\ez"},
-  /* Linux Console  */
-  {"home",       "\e[1~"},
-  {"end",        "\e[4~"},
-  {"F1",         "\e[[A"},
-  {"F2",         "\e[[B"},
-  {"F3",         "\e[[C"},
-  {"F4",         "\e[[D"},
-  {"F5",         "\e[[E"},
-  {"F6",         "\e[[F"},
-  {"F7",         "\e[[G"},
-  {"F8",         "\e[[H"},
-  {"F9",         "\e[[I"},
-  {"F10",        "\e[[J"},
-  {"F11",        "\e[[K"},
-  {"F12",        "\e[[L"},
-  {NULL, }
+   SDL_Window   *window;
+   SDL_Renderer *renderer;
+   SDL_Texture  *texture;
+
+// cnd_t  cond;
+// mtx_t  mtx;
 };
-static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyCode **ret)
-{
-  int i;
-  int matches = 0;
 
-  if (!strncmp (buf, "\e[M", MIN(length,3)))
-    {
-      if (length >= 6)
-        return 9001;
-      return 2342;
-    }
-  for (i = 0; ufb_keycodes[i].nick; i++)
-    if (!strncmp (buf, ufb_keycodes[i].sequence, length))
-      {
-        matches ++;
-        if ((int)strlen (ufb_keycodes[i].sequence) == length && ret)
-          {
-            *ret = &ufb_keycodes[i];
-            return 1;
-          }
-      }
-  if (matches != 1 && ret)
-    *ret = NULL;
-  return matches==1?2:matches;
-}
+#include "stb_image_write.h"
 
-static char *evsource_kb_get_event (void)
+void ctx_screenshot (Ctx *ctx, const char *output_path)
 {
-  unsigned char buf[20];
-  int length;
+#if CTX_SCREENSHOT
+  int valid = 0;
+  CtxSDL *sdl = (void*)ctx->renderer;
 
+  if (ctx_renderer_is_sdl (ctx)) valid = 1;
+#if CTX_FB
+  if (ctx_renderer_is_fb  (ctx)) valid = 1;
+#endif
 
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const MmmKeyCode *match = NULL;
+  if (!valid)
+    return;
 
-        if (!is_active (ev_src_kb.priv))
-           return NULL;
+#if CTX_FB
+  // we rely on the same layout
+  for (int i = 0; i < sdl->width * sdl->height; i++)
+  {
+    int tmp = sdl->pixels[i*4];
+    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
+    sdl->pixels[i*4 + 2] = tmp;
+  }
+#endif
 
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
-          {
-            struct timeval tv;
-            fd_set rfds;
-            FD_ZERO (&rfds);
-            FD_SET (STDIN_FILENO, &rfds);
-            tv.tv_sec = 0;
-            tv.tv_usec = 1000 * 120;
-            if (select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 0)
-              return strdup ("escape");
-          }
+  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
 
-        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
-          {
-            case 1: /* unique match */
-              if (!match)
-                return NULL;
-              return strdup (match->nick);
-              break;
-            case 0: /* no matches, bail*/
-             {
-                static char ret[256];
-                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
-                                                             * single unicode
-                                                             * utf8 character
-                                                             */
-                  {
-                    read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
-                    buf[ctx_utf8_len(buf[0])]=0;
-                    strcpy (ret, (void*)buf);
-                    return strdup(ret); //XXX: simplify
-                  }
-                if (length == 0) /* ascii */
-                  {
-                    buf[1]=0;
-                    strcpy (ret, (void*)buf);
-                    return strdup(ret);
-                  }
-                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
-                    length >=0 ? buf[0] : 0,
-                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
-                    length >=1 ? buf[1] : 0,
-                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
-                    length >=2 ? buf[2] : 0,
-                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
-                    length >=3 ? buf[3] : 0,
-                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
-                    length >=4 ? buf[4] : 0,
-                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
-                    length >=5 ? buf[5] : 0,
-                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
-                    length >=6 ? buf[6] : 0,
-                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
-                    );
-                return strdup(ret);
-            }
-              return NULL;
-            default: /* continue */
-              break;
-          }
-      }
-    else
-      return strdup("key read eek");
-  return strdup("fail");
+#if CTX_FB
+  for (int i = 0; i < sdl->width * sdl->height; i++)
+  {
+    int tmp = sdl->pixels[i*4];
+    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
+    sdl->pixels[i*4 + 2] = tmp;
+  }
+#endif
+#endif
 }
 
-static int evsource_kb_get_fd (void)
+void ctx_sdl_set_title (void *self, const char *new_title)
 {
-  return STDIN_FILENO;
+   CtxSDL *sdl = self;
+   SDL_SetWindowTitle (sdl->window, new_title);
 }
 
-
-EvSource *evsource_kb_new (void)
+static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
 {
-  if (evsource_kb_init() == 0)
+  CtxTiled *tiled = &sdl->tiled;
+  if (tiled->shown_cursor != tiled->ctx->cursor)
   {
-    return &ev_src_kb;
+    tiled->shown_cursor = tiled->ctx->cursor;
+    SDL_Cursor *new_cursor =  NULL;
+    switch (tiled->shown_cursor)
+    {
+      case CTX_CURSOR_UNSET: // XXX: document how this differs from none
+                             //      perhaps falling back to arrow?
+        break;
+      case CTX_CURSOR_NONE:
+        new_cursor = NULL;
+        break;
+      case CTX_CURSOR_ARROW:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
+        break;
+      case CTX_CURSOR_CROSSHAIR:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
+        break;
+      case CTX_CURSOR_WAIT:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
+        break;
+      case CTX_CURSOR_HAND:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
+        break;
+      case CTX_CURSOR_IBEAM:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
+        break;
+      case CTX_CURSOR_MOVE:
+      case CTX_CURSOR_RESIZE_ALL:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
+        break;
+      case CTX_CURSOR_RESIZE_N:
+      case CTX_CURSOR_RESIZE_S:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
+        break;
+      case CTX_CURSOR_RESIZE_E:
+      case CTX_CURSOR_RESIZE_W:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
+        break;
+      case CTX_CURSOR_RESIZE_NE:
+      case CTX_CURSOR_RESIZE_SW:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
+        break;
+      case CTX_CURSOR_RESIZE_NW:
+      case CTX_CURSOR_RESIZE_SE:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
+        break;
+    }
+    if (new_cursor)
+    {
+      SDL_Cursor *old_cursor = SDL_GetCursor();
+      SDL_SetCursor (new_cursor);
+      SDL_ShowCursor (1);
+      if (old_cursor)
+        SDL_FreeCursor (old_cursor);
+    }
+    else
+    {
+      SDL_ShowCursor (0);
+    }
   }
-  return NULL;
-}
 
-static int event_check_pending (CtxFb *fb)
-{
-  int events = 0;
-  for (int i = 0; i < fb->evsource_count; i++)
+  if (tiled->shown_frame == tiled->render_frame)
   {
-    while (evsource_has_event (fb->evsource[i]))
+    return;
+  }
+
+  if (block)
+  {
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
     {
-      char *event = evsource_get_event (fb->evsource[i]);
-      if (event)
+      usleep (50);
+      count ++;
+      if (count > 2000)
       {
-        if (fb->vt_active)
-        {
-          ctx_key_press (fb->ctx, 0, event, 0); // we deliver all events as key-press, it disamibuates
-          events++;
-        }
-        free (event);
+        tiled->shown_frame = tiled->render_frame;
+        return;
       }
     }
   }
-  return events;
-}
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
 
-int ctx_fb_consume_events (Ctx *ctx)
-{
-  CtxFb *fb = (void*)ctx->renderer;
-  ctx_fb_show_frame (fb, 0);
-  event_check_pending (fb);
-  return 0;
+  if (tiled->min_row == 100)
+  {
+  }
+  else
+  {
+#if 1
+    int x = tiled->min_col * tiled->width/CTX_HASH_COLS;
+    int y = tiled->min_row * tiled->height/CTX_HASH_ROWS;
+    int x1 = (tiled->max_col+1) * tiled->width/CTX_HASH_COLS;
+    int y1 = (tiled->max_row+1) * tiled->height/CTX_HASH_ROWS;
+    int width = x1 - x;
+    int height = y1 - y;
+#endif
+    tiled->min_row = 100;
+    tiled->max_row = 0;
+    tiled->min_col = 100;
+    tiled->max_col = 0;
+
+    SDL_Rect r = {x, y, width, height};
+    SDL_UpdateTexture (sdl->texture, &r,
+                      //(void*)sdl->pixels,
+                      (void*)(tiled->pixels + y * tiled->width * 4 + x * 4),
+                      
+                      tiled->width * 4);
+    SDL_RenderClear (sdl->renderer);
+    SDL_RenderCopy (sdl->renderer, sdl->texture, NULL, NULL);
+    SDL_RenderPresent (sdl->renderer);
+  }
+  tiled->shown_frame = tiled->render_frame;
 }
 
-inline static void ctx_fb_reset (CtxFb *fb)
+static const char *ctx_sdl_keysym_to_name (unsigned int sym, int *r_keycode)
 {
-  ctx_fb_show_frame (fb, 1);
+  static char buf[16]="";
+  buf[ctx_unichar_to_utf8 (sym, (void*)buf)]=0;
+  int code = sym;
+  const char *name = &buf[0];
+   switch (sym)
+   {
+     case SDLK_RSHIFT: code = 16 ; break;
+     case SDLK_LSHIFT: code = 16 ; break;
+     case SDLK_LCTRL: code = 17 ; break;
+     case SDLK_RCTRL: code = 17 ; break;
+     case SDLK_LALT:  code = 18 ; break;
+     case SDLK_RALT:  code = 18 ; break;
+     case SDLK_CAPSLOCK: name = "capslock"; code = 20 ; break;
+     //case SDLK_NUMLOCK: name = "numlock"; code = 144 ; break;
+     //case SDLK_SCROLLLOCK: name = "scrollock"; code = 145 ; break;
+
+     case SDLK_F1:     name = "F1"; code = 112; break;
+     case SDLK_F2:     name = "F2"; code = 113; break;
+     case SDLK_F3:     name = "F3"; code = 114; break;
+     case SDLK_F4:     name = "F4"; code = 115; break;
+     case SDLK_F5:     name = "F5"; code = 116; break;
+     case SDLK_F6:     name = "F6"; code = 117; break;
+     case SDLK_F7:     name = "F7"; code = 118; break;
+     case SDLK_F8:     name = "F8"; code = 119; break;
+     case SDLK_F9:     name = "F9"; code = 120; break;
+     case SDLK_F10:    name = "F10"; code = 121; break;
+     case SDLK_F11:    name = "F11"; code = 122; break;
+     case SDLK_F12:    name = "F12"; code = 123; break;
+     case SDLK_ESCAPE: name = "escape"; break;
+     case SDLK_DOWN:   name = "down"; code = 40; break;
+     case SDLK_LEFT:   name = "left"; code = 37; break;
+     case SDLK_UP:     name = "up"; code = 38;  break;
+     case SDLK_RIGHT:  name = "right"; code = 39; break;
+     case SDLK_BACKSPACE: name = "backspace"; break;
+     case SDLK_SPACE:  name = "space"; break;
+     case SDLK_TAB:    name = "tab"; break;
+     case SDLK_DELETE: name = "delete"; code = 46; break;
+     case SDLK_INSERT: name = "insert"; code = 45; break;
+     case SDLK_RETURN:
+       //if (key_repeat == 0) // return never should repeat
+       name = "return";   // on a DEC like terminal
+       break;
+     case SDLK_HOME:     name = "home"; code = 36; break;
+     case SDLK_END:      name = "end"; code = 35; break;
+     case SDLK_PAGEDOWN: name = "page-down"; code = 34; break;
+     case SDLK_PAGEUP:   name = "page-up"; code = 33; break;
+     case ',': code = 188; break;
+     case '.': code = 190; break;
+     case '/': code = 191; break;
+     case '`': code = 192; break;
+     case '[': code = 219; break;
+     case '\\': code = 220; break;
+     case ']':  code = 221; break;
+     case '\'': code = 222; break;
+     default:
+       ;
+   }
+   if (sym >= 'a' && sym <='z') code -= 32;
+   if (r_keycode)
+   {
+     *r_keycode = code;
+   }
+   return name;
 }
 
-inline static void ctx_fb_flush (CtxFb *fb)
+int ctx_sdl_consume_events (Ctx *ctx)
 {
-  if (fb->shown_frame == fb->render_frame)
+  CtxTiled *tiled = (void*)ctx->renderer;
+  CtxSDL *sdl = (void*)ctx->renderer;
+  SDL_Event event;
+  int got_events = 0;
+
+  ctx_sdl_show_frame (sdl, 0);
+
+  while (SDL_PollEvent (&event))
   {
-    int dirty_tiles = 0;
-    ctx_set_drawlist (fb->ctx_copy, &fb->ctx->drawlist.entries[0],
-                                         fb->ctx->drawlist.count * 9);
-    if (_ctx_enable_hash_cache)
+    got_events ++;
+    switch (event.type)
     {
-      Ctx *hasher = ctx_hasher_new (fb->width, fb->height,
-                        CTX_HASH_COLS, CTX_HASH_ROWS);
-      ctx_render_ctx (fb->ctx_copy, hasher);
-
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++)
+      case SDL_MOUSEBUTTONDOWN:
+        SDL_CaptureMouse (1);
+        ctx_pointer_press (ctx, event.button.x, event.button.y, event.button.button, 0);
+        break;
+      case SDL_MOUSEBUTTONUP:
+        SDL_CaptureMouse (0);
+        ctx_pointer_release (ctx, event.button.x, event.button.y, event.button.button, 0);
+        break;
+      case SDL_MOUSEMOTION:
+        //  XXX : look at mask and generate motion for each pressed
+        //        button
+        ctx_pointer_motion (ctx, event.motion.x, event.motion.y, 1, 0);
+        break;
+      case SDL_FINGERMOTION:
+        ctx_pointer_motion (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
+            (event.tfinger.fingerId%10) + 4, 0);
+        break;
+      case SDL_FINGERDOWN:
         {
-          uint8_t *new_hash = ctx_hasher_get_hash (hasher, col, row);
-          if (new_hash && memcmp (new_hash, &fb->hashes[(row * CTX_HASH_COLS + col) *  20], 20))
+        static int fdowns = 0;
+        fdowns ++;
+        if (fdowns > 1) // the very first finger down from SDL seems to be
+                        // mirrored as mouse events, later ones not - at
+                        // least under wayland
+        {
+          ctx_pointer_press (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height, 
+          (event.tfinger.fingerId%10) + 4, 0);
+        }
+        }
+        break;
+      case SDL_FINGERUP:
+        ctx_pointer_release (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
+          (event.tfinger.fingerId%10) + 4, 0);
+        break;
+#if 1
+      case SDL_TEXTINPUT:
+    //  if (!active)
+    //    break;
+        if (!sdl->lctrl && !sdl->rctrl && !sdl->lalt 
+           //&& ( (vt && vt_keyrepeat (vt) ) || (key_repeat==0) )
+           )
           {
-            memcpy (&fb->hashes[(row * CTX_HASH_COLS +  col)*20], new_hash, 20);
-            fb->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-            dirty_tiles++;
+            const char *name = event.text.text;
+            int keycode = 0;
+            if (!strcmp (name, " ") ) { name = "space"; }
+            if (name[0] && name[1] == 0)
+            {
+              keycode = name[0];
+              keycode = toupper (keycode);
+              switch (keycode)
+              {
+                case '.':  keycode = 190; break;
+                case ';':  keycode = 59; break;
+                case ',':  keycode = 188; break;
+                case '/':  keycode = 191; break;
+                case '\'': keycode = 222; break;
+                case '`':  keycode = 192; break;
+                case '[':  keycode = 219; break;
+                case ']':  keycode = 221; break;
+                case '\\': keycode = 220; break;
+              }
+            }
+            ctx_key_press (ctx, keycode, name, 0);
+            //got_event = 1;
+          }
+        break;
+#endif
+      case SDL_KEYDOWN:
+        {
+          char buf[32] = "";
+          const char *name = buf;
+          if (!event.key.repeat)
+          {
+            sdl->key_balance ++;
+            sdl->key_repeat = 0;
+          }
+          else
+          {
+            sdl->key_repeat ++;
+          }
+          switch (event.key.keysym.sym)
+          {
+            case SDLK_LSHIFT: sdl->lshift = 1; break;
+            case SDLK_RSHIFT: sdl->rshift = 1; break;
+            case SDLK_LCTRL:  sdl->lctrl = 1; break;
+            case SDLK_LALT:   sdl->lalt = 1; break;
+            case SDLK_RCTRL:  sdl->rctrl = 1; break;
+          }
+          if (sdl->lshift | sdl->rshift | sdl->lctrl | sdl->lalt | sdl->rctrl)
+          {
+            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
+                                            CTX_MODIFIER_STATE_ALT|
+                                            CTX_MODIFIER_STATE_SHIFT);
+            if (sdl->lshift | sdl->rshift)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
+            if (sdl->lctrl | sdl->rctrl)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
+            if (sdl->lalt)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
+          }
+          int keycode;
+          name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
+          ctx_key_down (ctx, keycode, name, 0);
+
+          if (strlen (name)
+              &&(event.key.keysym.mod & (KMOD_CTRL) ||
+                 event.key.keysym.mod & (KMOD_ALT) ||
+                 strlen (name) >= 2))
+          {
+            if (event.key.keysym.mod & (KMOD_CTRL) )
+              {
+                static char buf[64] = "";
+                sprintf (buf, "control-%s", name);
+                name = buf;
+              }
+            if (event.key.keysym.mod & (KMOD_ALT) )
+              {
+                static char buf[128] = "";
+                sprintf (buf, "alt-%s", name);
+                name = buf;
+              }
+            if (event.key.keysym.mod & (KMOD_SHIFT) )
+              {
+                static char buf[196] = "";
+                sprintf (buf, "shift-%s", name);
+                name = buf;
+              }
+            if (strcmp (name, "space"))
+              {
+               ctx_key_press (ctx, keycode, name, 0);
+              }
           }
           else
           {
-            fb->tile_affinity[row * CTX_HASH_COLS + col] = -1;
+#if 0
+             ctx_key_press (ctx, 0, buf, 0);
+#endif
+          }
+        }
+        break;
+      case SDL_KEYUP:
+        {
+           sdl->key_balance --;
+           switch (event.key.keysym.sym)
+           {
+             case SDLK_LSHIFT: sdl->lshift = 0; break;
+             case SDLK_RSHIFT: sdl->rshift = 0; break;
+             case SDLK_LCTRL: sdl->lctrl = 0; break;
+             case SDLK_RCTRL: sdl->rctrl = 0; break;
+             case SDLK_LALT:  sdl->lalt  = 0; break;
+           }
+
+          {
+            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
+                                            CTX_MODIFIER_STATE_ALT|
+                                            CTX_MODIFIER_STATE_SHIFT);
+            if (sdl->lshift | sdl->rshift)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
+            if (sdl->lctrl | sdl->rctrl)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
+            if (sdl->lalt)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
           }
-        }
-      free (((CtxHasher*)(hasher->renderer))->hashes);
-      ctx_free (hasher);
-    }
-    else
-    {
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        fb->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-        dirty_tiles++;
-      }
-    }
 
-    int dirty_no = 0;
-    if (dirty_tiles)
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        if (fb->tile_affinity[row * CTX_HASH_COLS + col] != -1)
-        {
-          fb->tile_affinity[row * CTX_HASH_COLS + col] = dirty_no * (_ctx_max_threads) / dirty_tiles;
-          dirty_no++;
-          if (col > fb->max_col) fb->max_col = col;
-          if (col < fb->min_col) fb->min_col = col;
-          if (row > fb->max_row) fb->max_row = row;
-          if (row < fb->min_row) fb->min_row = row;
+           int keycode;
+           const char *name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
+           ctx_key_up (ctx, keycode, name, 0);
         }
-      }
+        break;
+      case SDL_QUIT:
+        ctx_quit (ctx);
+        break;
+      case SDL_WINDOWEVENT:
+        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
+        {
+          ctx_sdl_show_frame (sdl, 1);
+          int width = event.window.data1;
+          int height = event.window.data2;
+          SDL_DestroyTexture (sdl->texture);
+          sdl->texture = SDL_CreateTexture (sdl->renderer, SDL_PIXELFORMAT_ABGR8888,
+                          SDL_TEXTUREACCESS_STREAMING, width, height);
+          free (tiled->pixels);
+          tiled->pixels = calloc (4, width * height);
 
-#if CTX_DAMAGE_CONTROL
-    for (int i = 0; i < fb->width * fb->height; i++)
-    {
-      int new_ = (fb->scratch_fb[i*4+0]+ fb->scratch_fb[i*4+1]+ fb->scratch_fb[i*4+2])/3;
-      if (new_>1) new_--;
-      fb->scratch_fb[i*4]= (fb->scratch_fb[i*4] + new_)/2;
-      fb->scratch_fb[i*4+1]= (fb->scratch_fb[i*4+1] + new_)/2;
-      fb->scratch_fb[i*4+2]= (fb->scratch_fb[i*4+1] + new_)/2;
+          tiled->width  = width;
+          tiled->height = height;
+          ctx_set_size (tiled->ctx, width, height);
+          ctx_set_size (tiled->ctx_copy, width, height);
+        }
+        break;
     }
-#endif
-
-
-    fb->render_frame = ++fb->frame;
-
-    mtx_lock (&fb->mtx);
-    cnd_broadcast (&fb->cond);
-    mtx_unlock (&fb->mtx);
   }
+  return 1;
 }
-
-void ctx_fb_free (CtxFb *fb)
+#else
+void ctx_screenshot (Ctx *ctx, const char *path)
 {
-  mtx_lock (&fb->mtx);
-  cnd_broadcast (&fb->cond);
-  mtx_unlock (&fb->mtx);
-
-  memset (fb->fb, 0, fb->width * fb->height *  4);
-  for (int i = 0 ; i < _ctx_max_threads; i++)
-    ctx_free (fb->host[i]);
+}
+#endif
 
-  if (fb->is_drm)
-  {
-    ctx_fbdrm_close (fb);
-  }
+#if CTX_SDL
 
-  ioctl (0, KDSETMODE, KD_TEXT);
-  system("stty sane");
-  free (fb->scratch_fb);
-  //free (fb);
+static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
+{
+  if (text)
+    SDL_SetClipboardText (text);
 }
 
-static unsigned char *fb_icc = NULL;
-static long fb_icc_length = 0;
-
-static
-void fb_render_fun (void **data)
+static char *ctx_sdl_get_clipboard (CtxSDL *sdl)
 {
-  int      no = (size_t)data[0];
-  CtxFb *fb = data[1];
+  return SDL_GetClipboardText ();
+}
 
-  while (!fb->quit)
-  {
-    mtx_lock (&fb->mtx);
-    cnd_wait (&fb->cond, &fb->mtx);
-    mtx_unlock (&fb->mtx);
+inline static void ctx_sdl_reset (CtxSDL *sdl)
+{
+  ctx_sdl_show_frame (sdl, 1);
+}
 
-    if (fb->render_frame != fb->rendered_frame[no])
-    {
-      int hno = 0;
+inline static void ctx_sdl_flush (CtxSDL *sdl)
+{
+  ctx_tiled_flush ((void*)sdl);
+  //CtxTiled *tiled = (void*)sdl;
+}
 
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
-        {
-          if (fb->tile_affinity[hno]==no)
-          {
-            int x0 = ((fb->width)/CTX_HASH_COLS) * col;
-            int y0 = ((fb->height)/CTX_HASH_ROWS) * row;
-            int width = fb->width / CTX_HASH_COLS;
-            int height = fb->height / CTX_HASH_ROWS;
+void ctx_sdl_free (CtxSDL *sdl)
+{
 
-            Ctx *host = fb->host[no];
-            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
-      /* merge horizontally adjecant tiles of same affinity into one job
-       * this reduces redundant overhead and gets better cache behavior
-       *
-       * giving different threads more explicitly different rows
-       * could be a good idea.
-       */
-            while (col + 1 < CTX_HASH_COLS &&
-                   fb->tile_affinity[hno+1] == no)
-            {
-              width += fb->width / CTX_HASH_COLS;
-              col++;
-              hno++;
-            }
-            ctx_rasterizer_init (rasterizer,
-                                 host, fb->ctx, &host->state,
-                                 &fb->scratch_fb[fb->width * 4 * y0 + x0 * 4],
-                                 0, 0, width, height,
-                                 fb->width*4, CTX_FORMAT_RGBA8,
-                                 fb->antialias);
-                                              /* this is the format used */
-            if (fb->fb_bits == 32)
-              rasterizer->swap_red_green = 1; 
-            if (fb_icc_length)
-              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, fb_icc, fb_icc_length);
-            ctx_translate (host, -x0, -y0);
-            ctx_render_ctx (fb->ctx_copy, host);
-          }
-        }
-      fb->rendered_frame[no] = fb->render_frame;
+  if (sdl->texture)
+  SDL_DestroyTexture (sdl->texture);
+  if (sdl->renderer)
+  SDL_DestroyRenderer (sdl->renderer);
+  if (sdl->window)
+  SDL_DestroyWindow (sdl->window);
 
-      if (fb_render_threads_done (fb) == _ctx_max_threads)
-      {
-   //   ctx_render_stream (fb->ctx_copy, stdout, 1);
-   //   ctx_reset (fb->ctx_copy);
-      }
-    }
-  }
-  fb->thread_quit ++;
+  ctx_tiled_free ((CtxTiled*)sdl);
 }
 
-int ctx_renderer_is_fb (Ctx *ctx)
+
+int ctx_renderer_is_sdl (Ctx *ctx)
 {
   if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_fb_free)
+      ctx->renderer->free == (void*)ctx_sdl_free)
           return 1;
   return 0;
 }
 
-static CtxFb *ctx_fb = NULL;
-static void vt_switch_cb (int sig)
-{
-  if (sig == SIGUSR1)
-  {
-    if (ctx_fb->is_drm)
-      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-    ioctl (0, VT_RELDISP, 1);
-    ctx_fb->vt_active = 0;
-    ioctl (0, KDSETMODE, KD_TEXT);
-  }
-  else
-  {
-    ioctl (0, VT_RELDISP, VT_ACKACQ);
-    ctx_fb->vt_active = 1;
-    // queue draw
-    ctx_fb->render_frame = ++ctx_fb->frame;
-    ioctl (0, KDSETMODE, KD_GRAPHICS);
-    if (ctx_fb->is_drm)
-    {
-      ioctl(ctx_fb->fb_fd, DRM_IOCTL_SET_MASTER, 0);
-      ctx_fb_flip (ctx_fb);
-    }
-    else
-    {
-      ctx_fb->ctx->dirty=1;
-
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        ctx_fb->hashes[(row * CTX_HASH_COLS + col) *  20] += 1;
-      }
-    }
-
-  }
-}
 
-Ctx *ctx_new_fb (int width, int height, int drm)
+Ctx *ctx_new_sdl (int width, int height)
 {
 #if CTX_RASTERIZER
-  CtxFb *fb = calloc (sizeof (CtxFb), 1);
-
-  ctx_fb = fb;
-  if (drm)
-    fb->fb = ctx_fbdrm_new (fb, &fb->width, &fb->height);
-  if (fb->fb)
-  {
-    fb->is_drm         = 1;
-    width              = fb->width;
-    height             = fb->height;
-    fb->fb_mapped_size = fb->width * fb->height * 4;
-    fb->fb_bits        = 32;
-    fb->fb_bpp         = 4;
-  }
-  else
-  {
-  fb->fb_fd = open ("/dev/fb0", O_RDWR);
-  if (fb->fb_fd > 0)
-    fb->fb_path = strdup ("/dev/fb0");
-  else
-  {
-    fb->fb_fd = open ("/dev/graphics/fb0", O_RDWR);
-    if (fb->fb_fd > 0)
-    {
-      fb->fb_path = strdup ("/dev/graphics/fb0");
-    }
-    else
-    {
-      free (fb);
-      return NULL;
-    }
-  }
-
-  if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
-    {
-      fprintf (stderr, "error getting fbinfo\n");
-      close (fb->fb_fd);
-      free (fb->fb_path);
-      free (fb);
-      return NULL;
-    }
-
-   if (ioctl(fb->fb_fd, FBIOGET_VSCREENINFO, &fb->vinfo))
-     {
-       fprintf (stderr, "error getting fbinfo\n");
-      close (fb->fb_fd);
-      free (fb->fb_path);
-      free (fb);
-      return NULL;
-     }
-
-//fprintf (stderr, "%s\n", fb->fb_path);
-  width = fb->width = fb->vinfo.xres;
-  height = fb->height = fb->vinfo.yres;
-
-  fb->fb_bits = fb->vinfo.bits_per_pixel;
-//fprintf (stderr, "fb bits: %i\n", fb->fb_bits);
 
-  if (fb->fb_bits == 16)
-    fb->fb_bits =
-      fb->vinfo.red.length +
-      fb->vinfo.green.length +
-      fb->vinfo.blue.length;
+  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
+  CtxTiled *tiled = (void*)sdl;
 
-   else if (fb->fb_bits == 8)
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
+  if (width <= 0 || height <= 0)
   {
-    unsigned short red[256],  green[256],  blue[256];
-    unsigned short original_red[256];
-    unsigned short original_green[256];
-    unsigned short original_blue[256];
-    struct fb_cmap cmap = {0, 256, red, green, blue, NULL};
-    struct fb_cmap original_cmap = {0, 256, original_red, original_green, original_blue, NULL};
-    int i;
-
-    /* do we really need to restore it ? */
-    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
-    {
-      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
-    }
-
-    for (i = 0; i < 256; i++)
-    {
-      red[i]   = ((( i >> 5) & 0x7) << 5) << 8;
-      green[i] = ((( i >> 2) & 0x7) << 5) << 8;
-      blue[i]  = ((( i >> 0) & 0x3) << 6) << 8;
-    }
-
-    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &cmap) == -1)
-    {
-      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
-    }
+    width  = 1920;
+    height = 1080;
   }
-
-  fb->fb_bpp = fb->vinfo.bits_per_pixel / 8;
-  fb->fb_mapped_size = fb->finfo.smem_len;
-                                              
-  fb->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
+  sdl->window = SDL_CreateWindow("ctx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 
SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
+  //sdl->renderer = SDL_CreateRenderer (sdl->window, -1, SDL_RENDERER_SOFTWARE);
+  sdl->renderer = SDL_CreateRenderer (sdl->window, -1, 0);
+  if (!sdl->renderer)
+  {
+     ctx_free (tiled->ctx);
+     free (sdl);
+     return NULL;
   }
-  if (!fb->fb)
-    return NULL;
-  fb->scratch_fb = calloc (fb->fb_mapped_size, 1);
-  ctx_fb_events = 1;
-
 #if CTX_BABL
   babl_init ();
 #endif
 
-  _ctx_file_get_contents ("/tmp/ctx.icc", &fb_icc, &fb_icc_length);
+  ctx_sdl_events = 1;
+  sdl->texture = SDL_CreateTexture (sdl->renderer,
+        SDL_PIXELFORMAT_ABGR8888,
+        SDL_TEXTUREACCESS_STREAMING,
+        width, height);
 
-  fb->ctx      = ctx_new ();
-  fb->ctx_copy = ctx_new ();
-  fb->width    = width;
-  fb->height   = height;
+  SDL_StartTextInput ();
+  SDL_EnableScreenSaver ();
 
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
+  tiled->cols     = 80;
+  tiled->rows     = 20;
+  ctx_set_renderer (tiled->ctx, sdl);
+  ctx_set_renderer (tiled->ctx_copy, sdl);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
 
-  ctx_set_renderer (fb->ctx, fb);
-  ctx_set_renderer (fb->ctx_copy, fb);
+  tiled->pixels = (uint8_t*)malloc (width * height * 4);
 
-#if 0
-  if (fb_icc_length)
-  {
-    ctx_colorspace (fb->ctx, CTX_COLOR_SPACE_DEVICE_RGB, fb_icc, fb_icc_length);
-    ctx_colorspace (fb->ctx_copy, CTX_COLOR_SPACE_DEVICE_RGB, fb_icc, fb_icc_length);
-  }
-#endif
+  ctx_set_size (tiled->ctx,      width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
 
-  ctx_set_size (fb->ctx, width, height);
-  ctx_set_size (fb->ctx_copy, width, height);
-  fb->flush = (void*)ctx_fb_flush;
-  fb->reset = (void*)ctx_fb_reset;
-  fb->free  = (void*)ctx_fb_free;
-  fb->set_clipboard = (void*)ctx_fb_set_clipboard;
-  fb->get_clipboard = (void*)ctx_fb_get_clipboard;
+  tiled->flush = (void*)ctx_sdl_flush;
+  tiled->reset = (void*)ctx_sdl_reset;
+  tiled->free  = (void*)ctx_sdl_free;
+  tiled->set_clipboard = (void*)ctx_sdl_set_clipboard;
+  tiled->get_clipboard = (void*)ctx_sdl_get_clipboard;
 
   for (int i = 0; i < _ctx_max_threads; i++)
   {
-    fb->host[i] = ctx_new_for_framebuffer (fb->scratch_fb,
-                   fb->width/CTX_HASH_COLS, fb->height/CTX_HASH_ROWS,
-                   fb->width * 4, CTX_FORMAT_RGBA8); // this format
-                                  // is overriden in  thread
-    ctx_set_texture_source (fb->host[i], fb->ctx);
+    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
+                     tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
+                     tiled->width * 4, CTX_FORMAT_RGBA8);
+    ctx_set_texture_source (tiled->host[i], tiled->ctx);
   }
 
-
-  mtx_init (&fb->mtx, mtx_plain);
-  cnd_init (&fb->cond);
+  mtx_init (&tiled->mtx, mtx_plain);
+  cnd_init (&tiled->cond);
 
 #define start_thread(no)\
   if(_ctx_max_threads>no){ \
     static void *args[2]={(void*)no, };\
     thrd_t tid;\
-    args[1]=fb;\
-    thrd_create (&tid, (void*)fb_render_fun, args);\
+    args[1]=sdl;\
+    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
   }
   start_thread(0);
   start_thread(1);
@@ -20163,1126 +22823,1031 @@ Ctx *ctx_new_fb (int width, int height, int drm)
   start_thread(15);
 #undef start_thread
 
-  ctx_flush (fb->ctx);
-
-  EvSource *kb = evsource_kb_new ();
-  if (kb)
-  {
-    fb->evsource[fb->evsource_count++] = kb;
-    kb->priv = fb;
-  }
-  EvSource *mice  = evsource_mice_new ();
-  if (mice)
-  {
-    fb->evsource[fb->evsource_count++] = mice;
-    mice->priv = fb;
-  }
-
-  fb->vt_active = 1;
-  ioctl(0, KDSETMODE, KD_GRAPHICS);
-  signal (SIGUSR1, vt_switch_cb);
-  signal (SIGUSR2, vt_switch_cb);
-  struct vt_stat st;
-  if (ioctl (0, VT_GETSTATE, &st) == -1)
-  {
-    ctx_log ("VT_GET_MODE on vt %i failed\n", fb->vt);
-    return NULL;
-  }
-
-  fb->vt = st.v_active;
-
-  struct vt_mode mode;
-  mode.mode   = VT_PROCESS;
-  mode.relsig = SIGUSR1;
-  mode.acqsig = SIGUSR2;
-  if (ioctl (0, VT_SETMODE, &mode) < 0)
-  {
-    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
-    return NULL;
-  }
-
-  return fb->ctx;
+  ctx_flush (tiled->ctx);
+  return tiled->ctx;
 #else
   return NULL;
 #endif
 }
 #else
 
-int ctx_renderer_is_fb (Ctx *ctx)
+int ctx_renderer_is_sdl (Ctx *ctx)
 {
   return 0;
 }
 #endif
-#endif
 
 #if CTX_EVENTS
 
 #include <fcntl.h>
 #include <sys/ioctl.h>
 
-typedef struct _CtxBraille CtxBraille;
-struct _CtxBraille
-{
-   void (*render) (void *braille, CtxCommand *command);
-   void (*reset)  (void *braille);
-   void (*flush)  (void *braille);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)   (void *braille);
-   Ctx     *ctx;
-   int      width;
-   int      height;
-   int      cols;
-   int      rows;
-   int      was_down;
-   uint8_t *pixels;
-   Ctx     *host;
-};
-
-static inline int _ctx_rgba8_manhattan_diff (const uint8_t *a, const uint8_t *b)
-{
-  int c;
-  int diff = 0;
-  for (c = 0; c<3;c++)
-    diff += ctx_pow2(a[c]-b[c]);
-  return diff;
-}
-
-
-static inline void _ctx_utf8_output_buf (uint8_t *pixels,
-                          int format,
-                          int width,
-                          int height,
-                          int stride,
-                          int reverse)
-{
-  const char *utf8_gray_scale[]= {" ","░","▒","▓","█","█", NULL};
-  int no = 0;
-  printf ("\e[?25l"); // cursor off
-  switch (format)
-    {
-      case CTX_FORMAT_GRAY2:
-        {
-          for (int y= 0; y < height; y++)
-            {
-              no = y * stride;
-              for (int x = 0; x < width; x++)
-                {
-                  int val4= (pixels[no] & (3 << ( (x % 4) *2) ) ) >> ( (x%4) *2);
-                  int val = (int) CTX_CLAMP (5.0 * val4 / 3.0, 0, 5);
-                  if (!reverse)
-                  { val = 5-val; }
-                  printf ("%s", utf8_gray_scale[val]);
-                  if ( (x % 4) == 3)
-                    { no++; }
-                }
-              printf ("\n");
-            }
-        }
-        break;
-      case CTX_FORMAT_GRAY1:
-        for (int row = 0; row < height/4; row++)
-          {
-            for (int col = 0; col < width /2; col++)
-              {
-                int unicode = 0;
-                int bitno = 0;
-                for (int x = 0; x < 2; x++)
-                  for (int y = 0; y < 3; y++)
-                    {
-                      int no = (row * 4 + y) * stride + (col*2+x) /8;
-                      int set = pixels[no] & (1<< ( (col * 2 + x) % 8) );
-                      if (reverse) { set = !set; }
-                      if (set)
-                        { unicode |=  (1<< (bitno) ); }
-                      bitno++;
-                    }
-                {
-                  int x = 0;
-                  int y = 3;
-                  int no = (row * 4 + y) * stride + (col*2+x) /8;
-                  int setA = pixels[no] & (1<< ( (col * 2 + x) % 8) );
-                  no = (row * 4 + y) * stride + (col*2+x+1) /8;
-                  int setB = pixels[no] & (1<< (   (col * 2 + x + 1) % 8) );
-                  if (reverse) { setA = !setA; }
-                  if (reverse) { setB = !setB; }
-                  if (setA != 0 && setB==0)
-                    { unicode += 0x2840; }
-                  else if (setA == 0 && setB)
-                    { unicode += 0x2880; }
-                  else if ( (setA != 0) && (setB != 0) )
-                    { unicode += 0x28C0; }
-                  else
-                    { unicode += 0x2800; }
-                  uint8_t utf8[5];
-                  utf8[ctx_unichar_to_utf8 (unicode, utf8)]=0;
-                  printf ("%s", utf8);
-                }
-              }
-            printf ("\n");
-          }
-        break;
-      case CTX_FORMAT_RGBA8:
-        {
-        for (int row = 0; row < height/4; row++)
-          {
-            for (int col = 0; col < width /2; col++)
-              {
-                int unicode = 0;
-                int bitno = 0;
-
-                uint8_t rgba2[4] = {0,0,0,255};
-                uint8_t rgba1[4] = {0,0,0,255};
-                int     rgbasum[4] = {0,};
-                int     col_count = 0;
-
-                for (int xi = 0; xi < 2; xi++)
-                  for (int yi = 0; yi < 4; yi++)
-                      {
-                        int noi = (row * 4 + yi) * stride + (col*2+xi) * 4;
-                        int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba2);
-                        if (diff > 32*32)
-                        {
-                          for (int c = 0; c < 3; c++)
-                          {
-                            rgbasum[c] += pixels[noi+c];
-                          }
-                          col_count++;
-                        }
-                      }
-                if (col_count)
-                for (int c = 0; c < 3; c++)
-                {
-                  rgba1[c] = rgbasum[c] / col_count;
-                }
-
-
-
-                // to determine color .. find two most different
-                // colors in set.. and threshold between them..
-                // even better dither between them.
-                //
-  printf ("\e[38;2;%i;%i;%im", rgba1[0], rgba1[1], rgba1[2]);
-  //printf ("\e[48;2;%i;%i;%im", rgba2[0], rgba2[1], rgba2[2]);
-
-                for (int x = 0; x < 2; x++)
-                  for (int y = 0; y < 3; y++)
-                    {
-                      int no = (row * 4 + y) * stride + (col*2+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba1)< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba2))
-
-                      int set = CHECK_IS_SET;
-                      if (reverse) { set = !set; }
-                      if (set)
-                        { unicode |=  (1<< (bitno) ); }
-                      bitno++;
-                    }
-                {
-                  int x = 0;
-                  int y = 3;
-                  int no = (row * 4 + y) * stride + (col*2+x) * 4;
-                  int setA = CHECK_IS_SET;
-                  no = (row * 4 + y) * stride + (col*2+x+1) * 4;
-                  int setB = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                  if (reverse) { setA = !setA; }
-                  if (reverse) { setB = !setB; }
-                  if (setA != 0 && setB==0)
-                    { unicode += 0x2840; }
-                  else if (setA == 0 && setB)
-                    { unicode += 0x2880; }
-                  else if ( (setA != 0) && (setB != 0) )
-                    { unicode += 0x28C0; }
-                  else
-                    { unicode += 0x2800; }
-                  uint8_t utf8[5];
-                  utf8[ctx_unichar_to_utf8 (unicode, utf8)]=0;
-                  printf ("%s", utf8);
-                }
-              }
-            printf ("\n\r");
-          }
-          printf ("\e[38;2;%i;%i;%im", 255,255,255);
-        }
-        break;
-
-      case CTX_FORMAT_GRAY4:
-        {
-          int no = 0;
-          for (int y= 0; y < height; y++)
-            {
-              no = y * stride;
-              for (int x = 0; x < width; x++)
-                {
-                  int val = (pixels[no] & (15 << ( (x % 2) *4) ) ) >> ( (x%2) *4);
-                  val = val * 6 / 16;
-                  if (reverse) { val = 5-val; }
-                  val = CTX_CLAMP (val, 0, 4);
-                  printf ("%s", utf8_gray_scale[val]);
-                  if (x % 2 == 1)
-                    { no++; }
-                }
-              printf ("\n");
-            }
-        }
-        break;
-      case CTX_FORMAT_CMYK8:
-        {
-          for (int c = 0; c < 4; c++)
-            {
-              int no = 0;
-              for (int y= 0; y < height; y++)
-                {
-                  for (int x = 0; x < width; x++, no+=4)
-                    {
-                      int val = (int) CTX_CLAMP (pixels[no+c]/255.0*6.0, 0, 5);
-                      if (reverse)
-                        { val = 5-val; }
-                      printf ("%s", utf8_gray_scale[val]);
-                    }
-                  printf ("\n");
-                }
-            }
-        }
-        break;
-      case CTX_FORMAT_RGB8:
-        {
-          for (int c = 0; c < 3; c++)
-            {
-              int no = 0;
-              for (int y= 0; y < height; y++)
-                {
-                  for (int x = 0; x < width; x++, no+=3)
-                    {
-                      int val = (int) CTX_CLAMP (pixels[no+c]/255.0*6.0, 0, 5);
-                      if (reverse)
-                        { val = 5-val; }
-                      printf ("%s", utf8_gray_scale[val]);
-                    }
-                  printf ("\n");
-                }
-            }
-        }
-        break;
-      case CTX_FORMAT_CMYKAF:
-        {
-          for (int c = 0; c < 5; c++)
-            {
-              int no = 0;
-              for (int y= 0; y < height; y++)
-                {
-                  for (int x = 0; x < width; x++, no+=5)
-                    {
-                      int val = (int) CTX_CLAMP ( (pixels[no+c]*6.0), 0, 5);
-                      if (reverse)
-                        { val = 5-val; }
-                      printf ("%s", utf8_gray_scale[val]);
-                    }
-                  printf ("\n");
-                }
-            }
-        }
-    }
-  printf ("\e[?25h"); // cursor on
-}
-
-inline static void ctx_braille_flush (CtxBraille *braille)
+typedef struct CtxTermCell
 {
-  int width =  braille->width;
-  int height = braille->height;
-  ctx_render_ctx (braille->ctx, braille->host);
-  printf ("\e[H");
-  _ctx_utf8_output_buf (braille->pixels,
-                        CTX_FORMAT_RGBA8,
-                        width, height, width * 4, 0);
-#if CTX_BRAILLE_TEXT
-  CtxRasterizer *rasterizer = (CtxRasterizer*)(braille->host->renderer);
-  // XXX instead sort and inject along with braille
-  for (CtxList *l = rasterizer->glyphs; l; l = l->next)
-  {
-      CtxTermGlyph *glyph = l->data;
-      printf ("\e[0m\e[%i;%iH%c", glyph->row, glyph->col, glyph->unichar);
-      free (glyph);
-  }
-  while (rasterizer->glyphs)
-    ctx_list_remove (&rasterizer->glyphs, rasterizer->glyphs->data);
-#endif
-}
+  char    utf8[5];
+  uint8_t fg[4];
+  uint8_t bg[4];
 
-void ctx_braille_free (CtxBraille *braille)
-{
-  nc_at_exit ();
-  free (braille->pixels);
-  ctx_free (braille->host);
-  free (braille);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
-}
+  char    prev_utf8[5];
+  uint8_t prev_fg[4];
+  uint8_t prev_bg[4];
+} CtxTermCell;
 
-int ctx_renderer_is_braille (Ctx *ctx)
+typedef struct CtxTermLine
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_braille_free)
-          return 1;
-  return 0;
-}
+  CtxTermCell *cells;
+  int maxcol;
+  int size;
+} CtxTermLine;
 
-Ctx *ctx_new_braille (int width, int height)
+typedef enum
 {
-  Ctx *ctx = ctx_new ();
-#if CTX_RASTERIZER
-  fprintf (stdout, "\e[?1049h");
-  CtxBraille *braille = (CtxBraille*)calloc (sizeof (CtxBraille), 1);
-  int maxwidth = ctx_terminal_cols  () * 2;
-  int maxheight = (ctx_terminal_rows ()-1) * 4;
-  if (width <= 0 || height <= 0)
-  {
-    width = maxwidth;
-    height = maxheight;
-  }
-  if (width > maxwidth) width = maxwidth;
-  if (height > maxheight) height = maxheight;
-  braille->ctx = ctx;
-  braille->width  = width;
-  braille->height = height;
-  braille->cols = (width + 1) / 2;
-  braille->rows = (height + 3) / 4;
-  braille->pixels = (uint8_t*)malloc (width * height * 4);
-  braille->host = ctx_new_for_framebuffer (braille->pixels,
-                                           width, height,
-                                           width * 4, CTX_FORMAT_RGBA8);
-#if CTX_BRAILLE_TEXT
-  ((CtxRasterizer*)braille->host->renderer)->term_glyphs=1;
-#endif
-  _ctx_mouse (ctx, NC_MOUSE_DRAG);
-  ctx_set_renderer (ctx, braille);
-  ctx_set_size (ctx, width, height);
-  ctx_font_size (ctx, 4.0f);
-  braille->flush = (void(*)(void*))ctx_braille_flush;
-  braille->free  = (void(*)(void*))ctx_braille_free;
-#endif
-
-
-  return ctx;
-}
-
-#endif
-
-#if CTX_SDL
-#include <threads.h>
+  CTX_TERM_ASCII,
+  CTX_TERM_ASCII_MONO,
+  CTX_TERM_SEXTANT,
+  CTX_TERM_BRAILLE_MONO,
+  CTX_TERM_BRAILLE,
+  CTX_TERM_QUARTER,
+} CtxTermMode;
 
-typedef struct _CtxSDL CtxSDL;
-struct _CtxSDL
+typedef struct _CtxTerm CtxTerm;
+struct _CtxTerm
 {
-   void (*render)    (void *braille, CtxCommand *command);
-   void (*reset)     (void *braille);
-   void (*flush)     (void *braille);
+   void (*render) (void *term, CtxCommand *command);
+   void (*reset)  (void *term);
+   void (*flush)  (void *term);
    char *(*get_clipboard) (void *ctxctx);
    void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)      (void *braille);
-   Ctx          *ctx;
-   int           width;
-   int           height;
-   int           cols;
-   int           rows;
-   int           was_down;
-   uint8_t      *pixels;
-   Ctx          *ctx_copy;
-   Ctx          *host[CTX_MAX_THREADS];
-   CtxAntialias  antialias;
-   int           quit;
-   _Atomic int   thread_quit;
-   int           shown_frame;
-   int           render_frame;
-   int           rendered_frame[CTX_MAX_THREADS];
-   int           frame;
-   int       min_col; // hasher cols and rows
-   int       min_row;
-   int       max_col;
-   int       max_row;
-   uint8_t  hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
-   int8_t    tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
-                                                           // responsible for a tile
-                                                           //
-
-   int           pointer_down[3];
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
-
-   CtxCursor     shown_cursor;
-
-   /* where we diverge from fb*/
-   SDL_Window   *window;
-   SDL_Renderer *renderer;
-   SDL_Texture  *texture;
-
-   cnd_t  cond;
-   mtx_t  mtx;
+   void (*free)   (void *term);
+   Ctx      *ctx;
+   int       width;
+   int       height;
+   int       cols;
+   int       rows;
+   int       was_down;
+
+   uint8_t  *pixels;
+
+   Ctx      *host;
+   CtxList  *lines;
+   CtxTermMode mode;
 };
 
-#include "stb_image_write.h"
-
-void ctx_screenshot (Ctx *ctx, const char *output_path)
-{
-#if CTX_SCREENSHOT
-  int valid = 0;
-  CtxSDL *sdl = (void*)ctx->renderer;
-
-  if (ctx_renderer_is_sdl (ctx)) valid = 1;
-#if CTX_FB
-  if (ctx_renderer_is_fb  (ctx)) valid = 1;
-#endif
-
-  if (!valid)
-    return;
-
-#if CTX_FB
-  for (int i = 0; i < sdl->width * sdl->height; i++)
-  {
-    int tmp = sdl->pixels[i*4];
-    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
-    sdl->pixels[i*4 + 2] = tmp;
-  }
-#endif
-
-  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
+static int ctx_term_ch = 8;
+static int ctx_term_cw = 8;
 
-#if CTX_FB
-  for (int i = 0; i < sdl->width * sdl->height; i++)
+void ctx_term_set (CtxTerm *term,
+                      int col, int row, const char *utf8,
+                      uint8_t *fg, uint8_t *bg)
+{
+  if (col < 1 || row < 1 || col > term->cols  || row > term->rows) return;
+  while (ctx_list_length (term->lines) < row)
   {
-    int tmp = sdl->pixels[i*4];
-    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
-    sdl->pixels[i*4 + 2] = tmp;
+    ctx_list_append (&term->lines, calloc (sizeof (CtxTermLine), 1));
   }
-#endif
-#endif
+  CtxTermLine *line = ctx_list_nth_data (term->lines, row-1);
+  assert (line);
+  if (line->size < col)
+  {
+     int new_size = ((col + 128)/128)*128;
+     line->cells = realloc (line->cells, sizeof (CtxTermCell) * new_size);
+     memset (&line->cells[line->size], 0, sizeof (CtxTermCell) * (new_size - line->size) );
+     line->size = new_size;
+  }
+  if (col > line->maxcol) line->maxcol = col;
+  strncpy (line->cells[col-1].utf8, (char*)utf8, 4);
+  memcpy  (line->cells[col-1].fg, fg, 4);
+  memcpy  (line->cells[col-1].bg, bg, 4);
 }
 
-void ctx_sdl_set_title (void *self, const char *new_title)
+static int _ctx_term256 = 0; // XXX TODO implement autodetect for this
+static long _ctx_curfg = -1;
+static long _ctx_curbg = -1;
+
+static long ctx_rgb_to_long (int r,int g, int b)
 {
-   CtxSDL *sdl = self;
-   SDL_SetWindowTitle (sdl->window, new_title);
+  return r * 256 * 256 + g * 256 + b;
 }
 
-static inline int
-sdl_render_threads_done (CtxSDL *sdl)
+
+static void ctx_term_set_fg (int red, int green, int blue)
 {
-  int sum = 0;
-  for (int i = 0; i < _ctx_max_threads; i++)
+  long lc = ctx_rgb_to_long (red, green, blue);
+  if (lc == _ctx_curfg)
+    return;
+  _ctx_curfg=lc;
+  if (_ctx_term256 == 0)
   {
-     if (sdl->rendered_frame[i] == sdl->render_frame)
-       sum ++;
+    printf("\e[38;2;%i;%i;%im", red,green,blue);
+  }
+  else
+  {
+    int gray = (green /255.0) * 24 + 0.5;
+    int r    = (red/255.0)    * 6 + 0.5;
+    int g    = (green/255.0)  * 6 + 0.5;
+    int b    = (blue/255.0)   * 6 + 0.5;
+    if (gray > 23) gray = 23;
+
+    if (r > 5) r = 5;
+    if (g > 5) g = 5;
+    if (b > 5) b = 5;
+
+    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
+    {
+      printf("\e[38;5;%im", 16 + 216 + gray);
+    }
+    else
+      printf("\e[38;5;%im", 16 + r * 6 * 6 + g * 6  + b);
   }
-  return sum;
 }
 
-static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
+static void ctx_term_set_bg(int red, int green, int blue)
 {
-  if (sdl->shown_cursor != sdl->ctx->cursor)
+  long lc = ctx_rgb_to_long (red, green, blue);
+//if (lc == _ctx_curbg)
+//  return;
+  _ctx_curbg=lc;
+  if (_ctx_term256 == 0)
   {
-    sdl->shown_cursor = sdl->ctx->cursor;
-    SDL_Cursor *new_cursor =  NULL;
-    switch (sdl->shown_cursor)
-    {
-      case CTX_CURSOR_UNSET: // XXX: document how this differs from none
-                             //      perhaps falling back to arrow?
-        break;
-      case CTX_CURSOR_NONE:
-        new_cursor = NULL;
-        break;
-      case CTX_CURSOR_ARROW:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
-        break;
-      case CTX_CURSOR_CROSSHAIR:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
-        break;
-      case CTX_CURSOR_WAIT:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
-        break;
-      case CTX_CURSOR_HAND:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
-        break;
-      case CTX_CURSOR_IBEAM:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
-        break;
-      case CTX_CURSOR_MOVE:
-      case CTX_CURSOR_RESIZE_ALL:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
-        break;
-      case CTX_CURSOR_RESIZE_N:
-      case CTX_CURSOR_RESIZE_S:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
-        break;
-      case CTX_CURSOR_RESIZE_E:
-      case CTX_CURSOR_RESIZE_W:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
-        break;
-      case CTX_CURSOR_RESIZE_NE:
-      case CTX_CURSOR_RESIZE_SW:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
-        break;
-      case CTX_CURSOR_RESIZE_NW:
-      case CTX_CURSOR_RESIZE_SE:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
-        break;
-    }
-    if (new_cursor)
+    printf("\e[48;2;%i;%i;%im", red,green,blue);
+  }
+  else
+  {
+    int gray = (green /255.0) * 24 + 0.5;
+    int r    = (red/255.0)    * 6 + 0.5;
+    int g    = (green/255.0)  * 6 + 0.5;
+    int b    = (blue/255.0)   * 6 + 0.5;
+    if (gray > 23) gray = 23;
+
+    if (r > 5) r = 5;
+    if (g > 5) g = 5;
+    if (b > 5) b = 5;
+
+    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
     {
-      SDL_Cursor *old_cursor = SDL_GetCursor();
-      SDL_SetCursor (new_cursor);
-      SDL_ShowCursor (1);
-      if (old_cursor)
-        SDL_FreeCursor (old_cursor);
+      printf("\e[48;5;%im", 16 + 216 + gray);
     }
     else
-    {
-      SDL_ShowCursor (0);
-    }
+      printf("\e[48;5;%im", 16 + r * 6 * 6 + g * 6  + b);
   }
+}
 
-  if (sdl->shown_frame == sdl->render_frame)
-  {
-    return;
-  }
+static int _ctx_term_force_full = 0;
 
-  if (block)
+void ctx_term_scanout (CtxTerm *term)
+{
+  int row = 1;
+  printf ("\e[H");
+//  printf ("\e[?25l");
+  printf ("\e[0m");
+  for (CtxList *l = term->lines; l; l = l->next)
   {
-    int count = 0;
-    while (sdl_render_threads_done (sdl) != _ctx_max_threads)
+    CtxTermLine *line = l->data;
+    for (int col = 1; col <= line->maxcol; col++)
     {
-      usleep (50);
-      count ++;
-      if (count > 2000)
+      CtxTermCell *cell = &line->cells[col-1];
+
+      if (strcmp(cell->utf8, cell->prev_utf8) ||
+          memcmp(cell->fg, cell->prev_fg, 3) ||
+          memcmp(cell->bg, cell->prev_bg, 3) || _ctx_term_force_full)
       {
-        sdl->shown_frame = sdl->render_frame;
-        return;
+        ctx_term_set_fg (cell->fg[0], cell->fg[1], cell->fg[2]);
+        ctx_term_set_bg (cell->bg[0], cell->bg[1], cell->bg[2]);
+        printf ("%s", cell->utf8);
+      }
+      else
+      {
+        // TODO: accumulate succesive such, and compress them
+        // into one
+        printf ("\e[C");
       }
+      strcpy (cell->prev_utf8, cell->utf8);
+      memcpy (cell->prev_fg, cell->fg, 3);
+      memcpy (cell->prev_bg, cell->bg, 3);
     }
+    if (row != term->rows)
+      printf ("\n\r");
+    row ++;
   }
-  else
-  {
-    if (sdl_render_threads_done (sdl) != _ctx_max_threads)
-      return;
-  }
-
-  if (sdl->min_row == 100)
-  {
-  }
-  else
-  {
-#if 0
-    int x = sdl->min_col * sdl->width/CTX_HASH_COLS;
-    int y = sdl->min_row * sdl->height/CTX_HASH_ROWS;
-    int x1 = (sdl->max_col+1) * sdl->width/CTX_HASH_COLS;
-    int y1 = (sdl->max_row+1) * sdl->height/CTX_HASH_ROWS;
-    int width = x1 - x;
-    int height = y1 - y;
-#endif
-    sdl->min_row = 100;
-    sdl->max_row = 0;
-    sdl->min_col = 100;
-    sdl->max_col = 0;
+  printf ("\e[0m");
+  //printf ("\e[?25h");
+  //
+}
 
-    //SDL_Rect r = {x, y, width, height};
-    SDL_UpdateTexture (sdl->texture, NULL, //&r,
-                      (void*)sdl->pixels,
-                      //(void*)(sdl->pixels + y * sdl->width * 4 + x * 4),
-                      
-                      sdl->width * sizeof (Uint32));
-    SDL_RenderClear (sdl->renderer);
-    SDL_RenderCopy (sdl->renderer, sdl->texture, NULL, NULL);
-    SDL_RenderPresent (sdl->renderer);
-  }
+// xx
+// xx
+// xx
+//
 
-  sdl->shown_frame = sdl->render_frame;
+static inline int _ctx_rgba8_manhattan_diff (const uint8_t *a, const uint8_t *b)
+{
+  int c;
+  int diff = 0;
+  for (c = 0; c<3;c++)
+    diff += ctx_pow2(a[c]-b[c]);
+  return sqrtf(diff);
+  return diff;
 }
 
-int ctx_sdl_consume_events (Ctx *ctx)
+static void ctx_term_output_buf_half (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-  CtxSDL *sdl = (void*)ctx->renderer;
-  SDL_Event event;
-  int got_events = 0;
-
-  ctx_sdl_show_frame (sdl, 0);
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█",
 
-  while (SDL_PollEvent (&event))
-  {
-    got_events ++;
-    switch (event.type)
+  };
+  for (int row = 0; row < height/2; row++)
     {
-      case SDL_MOUSEBUTTONDOWN:
-        SDL_CaptureMouse (1);
-        ctx_pointer_press (ctx, event.button.x, event.button.y, event.button.button, 0);
-        break;
-      case SDL_MOUSEBUTTONUP:
-        SDL_CaptureMouse (0);
-        ctx_pointer_release (ctx, event.button.x, event.button.y, event.button.button, 0);
-        break;
-      case SDL_MOUSEMOTION:
-        //  XXX : look at mask and generate motion for each pressed
-        //        button
-        ctx_pointer_motion (ctx, event.motion.x, event.motion.y, 1, 0);
-        break;
-      case SDL_FINGERMOTION:
-        ctx_pointer_motion (ctx, event.tfinger.x * sdl->width, event.tfinger.y * sdl->height,
-            (event.tfinger.fingerId%10) + 4, 0);
-        break;
-      case SDL_FINGERDOWN:
-        {
-        static int fdowns = 0;
-        fdowns ++;
-        if (fdowns > 1) // the very first finger down from SDL seems to be
-                        // mirrored as mouse events, later ones not - at
-                        // least under wayland
+      for (int col = 0; col < width-3; col++)
         {
-          ctx_pointer_press (ctx, event.tfinger.x *sdl->width, event.tfinger.y * sdl->height, 
-          (event.tfinger.fingerId%10) + 4, 0);
-        }
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+          int i = 0;
+
+          int  rgbasum[2][4] = {0,};
+          int  sumcount[2];
+
+          int curdiff = 0;
+          /* first find starting point colors */
+          for (int yi = 0; yi < ctx_term_ch; yi++)
+            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
+                {
+                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
+
+                  if (rgba[0][3] == 0)
+                  {
+                    for (int c = 0; c < 3; c++)
+                      rgba[0][c] = pixels[noi + c];
+                    rgba[0][3] = 255; // used only as mark of in-use
+                  }
+                  else
+                  {
+                    int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                    if (diff > curdiff)
+                    {
+                      curdiff = diff;
+                      for (int c = 0; c < 3; c++)
+                        rgba[1][c] = pixels[noi + c];
+                    }
+                  }
+
+                }
+
+          for (int iters = 0; iters < 1; iters++)
+          {
+                  i= 0;
+          for (int i = 0; i < 4; i ++)
+             rgbasum[0][i] = rgbasum[1][i]=0;
+          sumcount[0] = sumcount[1] = 0;
+
+          for (int yi = 0; yi < ctx_term_ch; yi++)
+            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
+                {
+                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
+
+                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
+                  int cluster = 0;
+                  if (diff1 <= diff2)
+                    cluster = 0;
+                  else
+                    cluster = 1;
+                  sumcount[cluster]++;
+                  for (int c = 0; c < 3; c++)
+                    rgbasum[cluster][c] += pixels[noi+c];
+                }
+
+
+          if (sumcount[0])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[0][c] = rgbasum[0][c] / sumcount[0];
+          }
+          if (sumcount[1])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[1][c] = rgbasum[1][c] / sumcount[1];
+          }
+          }
+
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
+              {
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
+           if (pixels_set == 4)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
         }
-        break;
-      case SDL_FINGERUP:
-        ctx_pointer_release (ctx, event.tfinger.x * sdl->width, event.tfinger.y * sdl->height,
-          (event.tfinger.fingerId%10) + 4, 0);
-        break;
-      case SDL_KEYUP:
+    }
+}
+
+void ctx_term_find_color_pair (CtxTerm *term, int x0, int y0, int w, int h,
+                uint8_t rgba[2][4])
+        //uint8_t *rgba0, uint8_t *rgba1)
+{
+int curdiff = 0;
+int stride = term->width * 4;
+uint8_t *pixels = term->pixels;
+/* first find starting point colors */
+for (int y = y0; y < y0 + h; y++)
+  for (int x = x0; x < x0 + w; x++)
+      {
+        int noi = (y) * stride + (x) * 4;
+
+        if (rgba[0][3] == 0)
         {
-           sdl->key_balance --;
-           switch (event.key.keysym.sym)
-           {
-             case SDLK_LCTRL: sdl->lctrl = 0; break;
-             case SDLK_RCTRL: sdl->rctrl = 0; break;
-             case SDLK_LALT:  sdl->lalt  = 0; break;
-           }
+          for (int c = 0; c < 3; c++)
+            rgba[0][c] = pixels[noi + c];
+          rgba[0][3] = 255; // used only as mark of in-use
         }
-        break;
-#if 1
-      case SDL_TEXTINPUT:
-    //  if (!active)
-    //    break;
-        if (!sdl->lctrl && !sdl->rctrl && !sdl->lalt 
-           //&& ( (vt && vt_keyrepeat (vt) ) || (key_repeat==0) )
-           )
+        else
+        {
+          int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], &rgba[0][0]);
+          if (diff > curdiff)
           {
-            const char *name = event.text.text;
-            if (!strcmp (name, " ") ) { name = "space"; }
-            ctx_key_press (ctx, 0, name, 0);
-            //got_event = 1;
+            curdiff = diff;
+            for (int c = 0; c < 3; c++)
+              rgba[1][c] = pixels[noi + c];
           }
-        break;
-#endif
-      case SDL_KEYDOWN:
-        {
-          char buf[32] = "";
-          char *name = buf;
-          if (!event.key.repeat)
+        }
+      }
+          int  rgbasum[2][4] = {0,};
+          int  sumcount[2];
+
+          for (int iters = 0; iters < 1; iters++)
+          {
+          for (int i = 0; i < 4; i ++)
+             rgbasum[0][i] = rgbasum[1][i]=0;
+          sumcount[0] = sumcount[1] = 0;
+
+          for (int y = y0; y < y0 + h; y++)
+            for (int x = x0; x < x0 + w; x++)
+                {
+                  int noi = (y) * stride + (x) * 4;
+
+                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
+                  int cluster = 0;
+                  if (diff1 <= diff2)
+                    cluster = 0;
+                  else
+                    cluster = 1;
+                  sumcount[cluster]++;
+                  for (int c = 0; c < 3; c++)
+                    rgbasum[cluster][c] += pixels[noi+c];
+                }
+
+
+          if (sumcount[0])
+          for (int c = 0; c < 3; c++)
           {
-            sdl->key_balance ++;
-            sdl->key_repeat = 0;
+            rgba[0][c] = rgbasum[0][c] / sumcount[0];
           }
-          else
+          if (sumcount[1])
+          for (int c = 0; c < 3; c++)
           {
-            sdl->key_repeat ++;
+            rgba[1][c] = rgbasum[1][c] / sumcount[1];
           }
-          buf[ctx_unichar_to_utf8 (event.key.keysym.sym, (void*)buf)]=0;
-          switch (event.key.keysym.sym)
-          {
-            case SDLK_LCTRL: sdl->lctrl = 1; break;
-            case SDLK_LALT:  sdl->lalt = 1; break;
-            case SDLK_RCTRL: sdl->rctrl = 1; break;
-            case SDLK_F1: name = "F1"; break;
-            case SDLK_F2: name = "F2"; break;
-            case SDLK_F3: name = "F3"; break;
-            case SDLK_F4: name = "F4"; break;
-            case SDLK_F5: name = "F5"; break;
-            case SDLK_F6: name = "F6"; break;
-            case SDLK_F7: name = "F7"; break;
-            case SDLK_F8: name = "F8"; break;
-            case SDLK_F9: name = "F9"; break;
-            case SDLK_F10: name = "F10"; break;
-            case SDLK_F11: name = "F11"; break;
-            case SDLK_F12: name = "F12"; break;
-            case SDLK_ESCAPE: name = "escape"; break;
-            case SDLK_DOWN: name = "down"; break;
-            case SDLK_LEFT: name = "left"; break;
-            case SDLK_UP: name = "up"; break;
-            case SDLK_RIGHT: name = "right"; break;
-            case SDLK_BACKSPACE: name = "backspace"; break;
-            case SDLK_SPACE: name = "space"; break;
-            case SDLK_TAB: name = "tab"; break;
-            case SDLK_DELETE: name = "delete"; break;
-            case SDLK_INSERT: name = "insert"; break;
-            case SDLK_RETURN:
-              //if (key_repeat == 0) // return never should repeat
-              name = "return";   // on a DEC like terminal
-              break;
-            case SDLK_HOME: name = "home"; break;
-            case SDLK_END: name = "end"; break;
-            case SDLK_PAGEDOWN: name = "page-down"; break;
-            case SDLK_PAGEUP: name = "page-up"; break;
-            default:
-              ;
           }
-          if (strlen (name)
-              &&(event.key.keysym.mod & (KMOD_CTRL) ||
-                 event.key.keysym.mod & (KMOD_ALT) ||
-                 strlen (name) >= 2))
-          {
-            if (event.key.keysym.mod & (KMOD_CTRL) )
+
+}
+
+
+
+static void ctx_term_output_buf_quarter (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
+{
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█"
+
+  };
+  for (int row = 0; row < height/ctx_term_ch; row++)
+    {
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
+
+          int pixels_set = 0;
+          for (int y = 0; y < 2; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
               {
-                static char buf[64] = "";
-                sprintf (buf, "control-%s", name);
-                name = buf;
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
               }
-            if (event.key.keysym.mod & (KMOD_ALT) )
+           if (pixels_set == 4)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
+        }
+    }
+}
+
+
+static void ctx_term_output_buf_sextant (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
+{
+  int stride = width * 4;
+
+  const char *sextants[]={
+   " 
","🬀","🬁","🬂","🬃","🬄","🬅","🬆","🬇","🬈","🬉","🬊","🬋","🬌","🬍","🬎","🬏","🬐","🬑","🬒","🬓","▌","🬔","🬕","🬖","🬗","🬘","🬙","🬚","🬛","🬜","🬝","🬞","🬟","🬠","🬡","🬢","🬣","🬤","🬥","🬦","🬧","▐","🬨","🬩","🬪","🬫","🬬","🬭","🬮","🬯","🬰","🬱","🬲","🬳","🬴","🬵","🬶","🬷","🬸","🬹","🬺","🬻","█"
+  };
+
+  for (int row = 0; row < height/ctx_term_ch; row++)
+    {
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
+
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
               {
-                static char buf[128] = "";
-                sprintf (buf, "alt-%s", name);
-                name = buf;
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
               }
-            if (event.key.keysym.mod & (KMOD_SHIFT) )
+
+          if (pixels_set == 6)
+            ctx_term_set (term, col +1, row + 1, " ",
+                          rgba[1], rgba[0]);
+          else
+            ctx_term_set (term, col +1, row + 1, sextants[unicode], rgba[0], rgba[1]);
+        }
+    }
+}
+
+static void ctx_term_output_buf_ascii (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term,
+                          int mono)
+{
+  /* this is a crude ascii-mode built on a quick mapping of sexels to ascii */
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","`","'","^","🬃","`","~","\"","-","\"","'","\"","-","\"","~","^",",",";",
+   "=","/","i","[","p","P","z",")","/","7","f",">","/","F",",","\\",":",":",
+   "\\","\\","(","T","j","T","]","?","s","\\","<","q","_","=","=","=","c","L",
+   "Q","C","a","b","J","]","m","b","d","@"
+  };
+  uint8_t black[4] = {0,0,0,255};
+  for (int row = 0; row < height/ctx_term_ch; row++)
+    {
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
+
+
+          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
+              _ctx_rgba8_manhattan_diff (black, rgba[0]))
+          {
+            for (int c = 0; c < 4; c ++)
+            {
+              int tmp = rgba[0][c];
+              rgba[0][c] = rgba[1][c];
+              rgba[1][c] = tmp;
+            }
+          }
+          if (mono)
+          {
+            rgba[1][0] = 0;
+            rgba[1][1] = 0;
+            rgba[1][2] = 0;
+          }
+
+
+          int brightest_dark_diff = _ctx_rgba8_manhattan_diff (black, rgba[0]);
+
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
               {
-                static char buf[196] = "";
-                sprintf (buf, "shift-%s", name);
-                name = buf;
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
               }
-            if (strcmp (name, "space"))
+
+
+           if (pixels_set == 6 && brightest_dark_diff < 40)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
+        }
+    }
+}
+
+static void ctx_term_output_buf_braille (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term,
+                          int mono)
+{
+  int reverse = 0;
+  int stride = width * 4;
+  uint8_t black[4] = {0,0,0,255};
+  for (int row = 0; row < height/ctx_term_ch; row++)
+    {
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
+
+
+          /* make darkest consistently be background  */
+          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
+              _ctx_rgba8_manhattan_diff (black, rgba[0]))
+          {
+            for (int c = 0; c < 4; c ++)
+            {
+              int tmp = rgba[0][c];
+              rgba[0][c] = rgba[1][c];
+              rgba[1][c] = tmp;
+            }
+          }
+          if (mono)
+          {
+            rgba[1][0] = 0;
+            rgba[1][1] = 0;
+            rgba[1][2] = 0;
+          }
+
+          int pixels_set = 0;
+          for (int x = 0; x < 2; x++)
+            for (int y = 0; y < 3; y++)
               {
-               ctx_key_press (ctx, 0, name, 0);
+                int no = (row * 4 + y) * stride + (col*2+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+                if (reverse) { set = !set; }
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
               }
-          }
-          else
           {
+            int x = 0;
+            int y = 3;
+            int no = (row * 4 + y) * stride + (col*2+x) * 4;
+            int setA = CHECK_IS_SET;
+            no = (row * 4 + y) * stride + (col*2+x+1) * 4;
+            int setB = CHECK_IS_SET;
+
+            pixels_set += setA;
+            pixels_set += setB;
+#undef CHECK_IS_SET
+            if (reverse) { setA = !setA; }
+            if (reverse) { setB = !setB; }
+            if (setA != 0 && setB==0)
+              { unicode += 0x2840; }
+            else if (setA == 0 && setB)
+              { unicode += 0x2880; }
+            else if ( (setA != 0) && (setB != 0) )
+              { unicode += 0x28C0; }
+            else
+              { unicode += 0x2800; }
+            char utf8[5];
+            utf8[ctx_unichar_to_utf8 (unicode, (uint8_t*)utf8)]=0;
+
 #if 0
-             ctx_key_press (ctx, 0, buf, 0);
+            if (pixels_set == 8)
+            {
+              if (rgba[0][0] < 32 && rgba[0][1] < 32 && rgba[0][2] < 32)
+              {
+                ctx_term_set (term, col +1, row + 1, " ",
+                                 rgba[1], rgba[0]);
+                continue;
+              }
+            }
 #endif
+            {
+              ctx_term_set (term, col +1, row + 1, utf8,
+                               rgba[0], rgba[1]);
+            }
           }
         }
-        break;
-      case SDL_QUIT:
-        ctx_quit (ctx);
-        break;
-      case SDL_WINDOWEVENT:
-        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
-        {
-          ctx_sdl_show_frame (sdl, 1);
-          int width = event.window.data1;
-          int height = event.window.data2;
-          SDL_DestroyTexture (sdl->texture);
-          sdl->texture = SDL_CreateTexture (sdl->renderer, SDL_PIXELFORMAT_ABGR8888,
-                          SDL_TEXTUREACCESS_STREAMING, width, height);
-          free (sdl->pixels);
-          sdl->pixels = calloc (4, width * height);
-
-          sdl->width  = width;
-          sdl->height = height;
-          ctx_set_size (sdl->ctx, width, height);
-          ctx_set_size (sdl->ctx_copy, width, height);
-        }
-        break;
     }
-  }
-  return 1;
 }
-#else
-void ctx_screenshot (Ctx *ctx, const char *path)
+
+
+inline static void ctx_term_render (void *ctx,
+                                       CtxCommand *command)
 {
+  CtxTerm *term = (void*)ctx;
+  /* directly forward */
+  ctx_process (term->host, &command->entry);
 }
+
+inline static void ctx_term_flush (CtxTerm *term)
+{
+  int width =  term->width;
+  int height = term->height;
+  switch (term->mode)
+  {
+    case CTX_TERM_QUARTER:
+       ctx_term_output_buf_quarter (term->pixels,
+                                width, height, term);
+       break;
+    case CTX_TERM_ASCII:
+       ctx_term_output_buf_ascii (term->pixels,
+                                width, height, term, 0);
+       break;
+    case CTX_TERM_ASCII_MONO:
+       ctx_term_output_buf_ascii (term->pixels,
+                                width, height, term, 1);
+       break;
+    case CTX_TERM_SEXTANT:
+       ctx_term_output_buf_sextant (term->pixels,
+                                width, height, term);
+       break;
+    case CTX_TERM_BRAILLE:
+       ctx_term_output_buf_braille (term->pixels,
+                                width, height, term, 0);
+       break;
+    case CTX_TERM_BRAILLE_MONO:
+       ctx_term_output_buf_braille (term->pixels,
+                                width, height, term, 1);
+       break;
+  }
+#if CTX_BRAILLE_TEXT
+  CtxRasterizer *rasterizer = (CtxRasterizer*)(term->host->renderer);
+  // XXX instead sort and inject along with braille
+  //
+
+  //uint8_t rgba_bg[4]={0,0,0,0};
+  //uint8_t rgba_fg[4]={255,0,255,255};
+
+  for (CtxList *l = rasterizer->glyphs; l; l = l->next)
+  {
+    CtxTermGlyph *glyph = l->data;
+
+    uint8_t *pixels = term->pixels;
+    long rgb_sum[4]={0,0,0};
+    for (int v = 0; v <  ctx_term_ch; v ++)
+    for (int u = 0; u <  ctx_term_cw; u ++)
+    {
+      int i = ((glyph->row-1) * ctx_term_ch + v) * rasterizer->blit_width + 
+              ((glyph->col-1) * ctx_term_cw + u);
+      for (int c = 0; c < 3; c ++)
+        rgb_sum[c] += pixels[i*4+c];
+    }
+    for (int c = 0; c < 3; c ++)
+      glyph->rgba_bg[c] = rgb_sum[c] / (ctx_term_ch * ctx_term_cw);
+    char utf8[8];
+    utf8[ctx_unichar_to_utf8(glyph->unichar, (uint8_t*)utf8)]=0;
+    ctx_term_set (term, glyph->col, glyph->row, 
+                     utf8, glyph->rgba_fg, glyph->rgba_bg);
+    free (glyph);
+  }
+
+  printf ("\e[H");
+  printf ("\e[0m");
+  ctx_term_scanout (term);
+  printf ("\e[0m");
+  fflush(NULL);
+  while (rasterizer->glyphs)
+    ctx_list_remove (&rasterizer->glyphs, rasterizer->glyphs->data);
 #endif
+}
 
-#if CTX_SDL
+void ctx_term_free (CtxTerm *term)
+{
+  while (term->lines)
+  {
+    free (term->lines->data);
+    ctx_list_remove (&term->lines, term->lines->data);
+  }
+  printf ("\e[?25h"); // cursor on
+  nc_at_exit ();
+  free (term->pixels);
+  ctx_free (term->host);
+  free (term);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
+}
 
-static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
+int ctx_renderer_is_term (Ctx *ctx)
 {
-  if (text)
-    SDL_SetClipboardText (text);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_term_free)
+          return 1;
+  return 0;
 }
 
-static char *ctx_sdl_get_clipboard (CtxSDL *sdl)
+float ctx_term_get_cell_width (Ctx *ctx)
 {
-  return SDL_GetClipboardText ();
+  return ctx_term_cw;
 }
 
-inline static void ctx_sdl_reset (CtxSDL *sdl)
+float ctx_term_get_cell_height (Ctx *ctx)
 {
-  ctx_sdl_show_frame (sdl, 1);
+  return ctx_term_ch;
 }
 
-inline static void ctx_sdl_flush (CtxSDL *sdl)
+Ctx *ctx_new_term (int width, int height)
 {
-  if (sdl->shown_frame == sdl->render_frame)
+  Ctx *ctx = ctx_new ();
+#if CTX_RASTERIZER
+  CtxTerm *term = (CtxTerm*)calloc (sizeof (CtxTerm), 1);
+ 
+  const char *mode = getenv ("CTX_TERM_MODE");
+  ctx_term_cw = 2;
+  ctx_term_ch = 3;
+
+  if (!mode) term->mode = CTX_TERM_SEXTANT;
+  else if (!strcmp (mode, "sextant")) term->mode = CTX_TERM_SEXTANT;
+  else if (!strcmp (mode, "ascii")) term->mode = CTX_TERM_ASCII;
+  else if (!strcmp (mode, "ascii-mono")) term->mode = CTX_TERM_ASCII_MONO;
+  else if (!strcmp (mode, "quarter")) term->mode = CTX_TERM_QUARTER;
+  else if (!strcmp (mode, "braille")){
+    term->mode = CTX_TERM_BRAILLE;
+    ctx_term_ch = 4;
+  }
+  else if (!strcmp (mode, "braille-mono")){
+    term->mode = CTX_TERM_BRAILLE_MONO;
+    ctx_term_ch = 4;
+  }
+  else {
+    fprintf (stderr, "recognized values for CTX_TERM_MODE:\n"
+                    " sextant ascii quarter braille braille-mono\n");
+    exit (1);
+  }
+
+  mode = getenv ("CTX_TERM_FORCE_FULL");
+  if (mode && strcmp (mode, "0") && strcmp (mode, "no"))
+    _ctx_term_force_full = 1;
+
+  fprintf (stdout, "\e[?1049h");
+  fprintf (stdout, "\e[?25l"); // cursor off
+
+  int maxwidth = ctx_terminal_cols  () * ctx_term_cw;
+  int maxheight = (ctx_terminal_rows ()) * ctx_term_ch;
+  if (width <= 0 || height <= 0)
   {
-    int dirty_tiles = 0;
-    ctx_set_drawlist (sdl->ctx_copy, &sdl->ctx->drawlist.entries[0],
-                                         sdl->ctx->drawlist.count * 9);
-    if (_ctx_enable_hash_cache)
-    {
-      Ctx *hasher = ctx_hasher_new (sdl->width, sdl->height,
-                        CTX_HASH_COLS, CTX_HASH_ROWS);
-      ctx_render_ctx (sdl->ctx_copy, hasher);
+    width = maxwidth;
+    height = maxheight;
+  }
+  if (width > maxwidth) width = maxwidth;
+  if (height > maxheight) height = maxheight;
+  term->ctx = ctx;
+  term->width  = width;
+  term->height = height;
+
+  term->cols = (width + 1) / ctx_term_cw;
+  term->rows = (height + 2) / ctx_term_ch;
+  term->lines = 0;
+  term->pixels = (uint8_t*)malloc (width * height * 4);
+  term->host = ctx_new_for_framebuffer (term->pixels,
+                                           width, height,
+                                           width * 4, CTX_FORMAT_RGBA8);
+#if CTX_BRAILLE_TEXT
+  ((CtxRasterizer*)term->host->renderer)->term_glyphs=1;
+#endif
+  _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, term);
+  ctx_set_size (ctx, width, height);
+  ctx_font_size (ctx, ctx_term_ch); 
+  term->render = ctx_term_render;
+  term->flush = (void(*)(void*))ctx_term_flush;
+  term->free  = (void(*)(void*))ctx_term_free;
+#endif
 
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++)
-        {
-          uint8_t *new_hash = ctx_hasher_get_hash (hasher, col, row);
-          if (new_hash && memcmp (new_hash, &sdl->hashes[(row * CTX_HASH_COLS + col) *  20], 20))
-          {
-            memcpy (&sdl->hashes[(row * CTX_HASH_COLS +  col)*20], new_hash, 20);
-            sdl->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-            dirty_tiles++;
-          }
-          else
-          {
-            sdl->tile_affinity[row * CTX_HASH_COLS + col] = -1;
-          }
-        }
-      free (((CtxHasher*)(hasher->renderer))->hashes);
-      ctx_free (hasher);
-    }
-    else
-    {
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-        {
-          sdl->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-          dirty_tiles++;
-        }
-    }
-    int dirty_no = 0;
-    if (dirty_tiles)
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        if (sdl->tile_affinity[row * CTX_HASH_COLS + col] != -1)
-        {
-          sdl->tile_affinity[row * CTX_HASH_COLS + col] = dirty_no * (_ctx_max_threads) / dirty_tiles;
-          dirty_no++;
-          if (col > sdl->max_col) sdl->max_col = col;
-          if (col < sdl->min_col) sdl->min_col = col;
-          if (row > sdl->max_row) sdl->max_row = row;
-          if (row < sdl->min_row) sdl->min_row = row;
-        }
-      }
 
-#if CTX_DAMAGE_CONTROL
-    for (int i = 0; i < sdl->width * sdl->height; i++)
-    {
-      int new_ = (sdl->pixels[i*4+0]+ sdl->pixels[i*4+1]+ sdl->pixels[i*4+2])/3;
-      //if (new_>1) new_--;
-      sdl->pixels[i*4]  = (sdl->pixels[i*4] + 255)/2;
-      sdl->pixels[i*4+1]= (sdl->pixels[i*4+1] + new_)/2;
-      sdl->pixels[i*4+2]= (sdl->pixels[i*4+1] + new_)/2;
-    }
+  return ctx;
+}
+
 #endif
 
-    sdl->render_frame = ++sdl->frame;
+#if CTX_EVENTS
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
 
-    mtx_lock (&sdl->mtx);
-    cnd_broadcast (&sdl->cond);
-    mtx_unlock (&sdl->mtx);
+typedef struct _CtxTermImg CtxTermImg;
+struct _CtxTermImg
+{
+   void (*render)         (void *termimg, CtxCommand *command);
+   void (*reset)          (void *termimg);
+   void (*flush)          (void *termimg);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard)  (void *ctxctx, const char *text);
+   void (*free)           (void *termimg);
+   Ctx      *ctx;
+   int       width;
+   int       height;
+   int       cols;
+   int       rows;
+   int       was_down;
+   // we need to have the above members in that order up to here
+   uint8_t  *pixels;
+   Ctx      *host;
+   CtxList  *lines;
+};
 
-  }
+inline static void ctx_termimg_render (void       *ctx,
+                                       CtxCommand *command)
+{
+  CtxTermImg *termimg = (void*)ctx;
+  /* directly forward */
+  ctx_process (termimg->host, &command->entry);
 }
 
-void ctx_sdl_free (CtxSDL *sdl)
+inline static void ctx_termimg_flush (CtxTermImg *termimg)
 {
-  sdl->quit = 1;
-  mtx_lock (&sdl->mtx);
-  cnd_broadcast (&sdl->cond);
-  mtx_unlock (&sdl->mtx);
+  int width =  termimg->width;
+  int height = termimg->height;
+  if (!termimg->pixels) return;
+  char *encoded = malloc (width * height * 3 * 3);
+  ctx_bin2base64 (termimg->pixels, width * height * 3,
+                  encoded);
+  int encoded_len = strlen (encoded);
 
-  while (sdl->thread_quit < _ctx_max_threads)
-    usleep (1000); // XXX : properly wait for threads instead
-  if (sdl->pixels)
-  {
-    free (sdl->pixels);
-  sdl->pixels = NULL;
-  for (int i = 0 ; i < _ctx_max_threads; i++)
+  int i = 0;
+
+  printf ("\e[H");
+  printf ("\e_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\e\\", width, height);
+  while (i <  encoded_len)
   {
-    ctx_free (sdl->host[i]);
-    sdl->host[i]=NULL;
-  }
-  SDL_DestroyTexture (sdl->texture);
-  SDL_DestroyRenderer (sdl->renderer);
-  SDL_DestroyWindow (sdl->window);
-  ctx_free (sdl->ctx_copy);
+     if (i + 4096 <  encoded_len)
+     {
+       printf  ("\e_Gm=1;");
+     }
+     else
+     {
+       printf  ("\e_Gm=0;");
+     }
+     for (int n = 0; n < 4000 && i < encoded_len; n++)
+     {
+       printf ("%c", encoded[i]);
+       i++;
+     }
+     printf ("\e\\");
   }
-  //free (sdl); // kept alive for threads quit check..
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
+  free (encoded);
+  
+  fflush (NULL);
 }
 
-static unsigned char *sdl_icc = NULL;
-static long sdl_icc_length = 0;
-
-static
-void sdl_render_fun (void **data)
+void ctx_termimg_free (CtxTermImg *termimg)
 {
-  int      no = (size_t)data[0];
-  CtxSDL *sdl = data[1];
-
-  while (!sdl->quit)
+  while (termimg->lines)
   {
-    Ctx *host = sdl->host[no];
-
-    mtx_lock (&sdl->mtx);
-    cnd_wait(&sdl->cond, &sdl->mtx);
-    mtx_unlock (&sdl->mtx);
-
-    if (sdl->render_frame != sdl->rendered_frame[no])
-    {
-      int hno = 0;
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
-        {
-          if (sdl->tile_affinity[hno]==no)
-          {
-            int x0 = ((sdl->width)/CTX_HASH_COLS) * col;
-            int y0 = ((sdl->height)/CTX_HASH_ROWS) * row;
-            int width = sdl->width / CTX_HASH_COLS;
-            int height = sdl->height / CTX_HASH_ROWS;
-
-            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
-#if 1 // merge horizontally adjecant tiles of same affinity into one job
-            while (col + 1 < CTX_HASH_COLS &&
-                   sdl->tile_affinity[hno+1] == no)
-            {
-              width += sdl->width / CTX_HASH_COLS;
-              col++;
-              hno++;
-            }
-#endif
-            ctx_rasterizer_init (rasterizer,
-                                 host, sdl->ctx, &host->state,
-                                 &sdl->pixels[sdl->width * 4 * y0 + x0 * 4],
-                                 0, 0, width, height,
-                                 sdl->width*4, CTX_FORMAT_RGBA8,
-                                 sdl->antialias);
-            if (sdl_icc_length)
-              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
-
-            ctx_translate (host, -x0, -y0);
-            ctx_render_ctx (sdl->ctx_copy, host);
-          }
-        }
-      sdl->rendered_frame[no] = sdl->render_frame;
-    }
+    free (termimg->lines->data);
+    ctx_list_remove (&termimg->lines, termimg->lines->data);
   }
-
-  sdl->thread_quit++; // need atomic?
+  printf ("\e[?25h"); // cursor on
+  nc_at_exit ();
+  free (termimg->pixels);
+  ctx_free (termimg->host);
+  free (termimg);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-int ctx_renderer_is_sdl (Ctx *ctx)
+int ctx_renderer_is_termimg (Ctx *ctx)
 {
   if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_sdl_free)
+      ctx->renderer->free == (void*)ctx_termimg_free)
           return 1;
   return 0;
 }
 
-
-Ctx *ctx_new_sdl (int width, int height)
+Ctx *ctx_new_termimg (int width, int height)
 {
+  Ctx *ctx = ctx_new ();
 #if CTX_RASTERIZER
+  fprintf (stdout, "\e[?1049h");
+  fprintf (stdout, "\e[?25l"); // cursor off
+  CtxTermImg *termimg = (CtxTermImg*)calloc (sizeof (CtxTermImg), 1);
 
-  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
-  _ctx_file_get_contents ("/tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
-  if (width <= 0 || height <= 0)
-  {
-    width  = 1920;
-    height = 1080;
-  }
-  sdl->window = SDL_CreateWindow("ctx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 
SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
-  //sdl->renderer = SDL_CreateRenderer (sdl->window, -1, SDL_RENDERER_SOFTWARE);
-  sdl->renderer = SDL_CreateRenderer (sdl->window, -1, 0);
-  if (!sdl->renderer)
-  {
-     ctx_free (sdl->ctx);
-     free (sdl);
-     return NULL;
-  }
-#if CTX_BABL
-  babl_init ();
-#endif
-
-  ctx_sdl_events = 1;
-  sdl->texture = SDL_CreateTexture (sdl->renderer,
-        SDL_PIXELFORMAT_ABGR8888,
-        SDL_TEXTUREACCESS_STREAMING,
-        width, height);
 
-  SDL_StartTextInput ();
-  SDL_EnableScreenSaver ();
+  int maxwidth = ctx_terminal_width ();
 
-  sdl->ctx = ctx_new ();
-  sdl->ctx_copy = ctx_new ();
-  sdl->width  = width;
-  sdl->height = height;
-  sdl->cols = 80;
-  sdl->rows = 20;
-  ctx_set_renderer (sdl->ctx, sdl);
-  ctx_set_renderer (sdl->ctx_copy, sdl);
-
-  sdl->pixels = (uint8_t*)malloc (width * height * 4);
-
-  ctx_set_size (sdl->ctx,      width, height);
-  ctx_set_size (sdl->ctx_copy, width, height);
-  sdl->flush = (void*)ctx_sdl_flush;
-  sdl->reset = (void*)ctx_sdl_reset;
-  sdl->free  = (void*)ctx_sdl_free;
-  sdl->set_clipboard = (void*)ctx_sdl_set_clipboard;
-  sdl->get_clipboard = (void*)ctx_sdl_get_clipboard;
+  int colwidth = maxwidth/ctx_terminal_cols ();
+  maxwidth-=colwidth;
 
-  for (int i = 0; i < _ctx_max_threads; i++)
+  int maxheight = ctx_terminal_height ();
+  if (width <= 0 || height <= 0)
   {
-    sdl->host[i] = ctx_new_for_framebuffer (sdl->pixels,
-                     sdl->width/CTX_HASH_COLS, sdl->height/CTX_HASH_ROWS,
-                     sdl->width * 4, CTX_FORMAT_RGBA8);
-    ctx_set_texture_source (sdl->host[i], sdl->ctx);
-  }
-
-  mtx_init (&sdl->mtx, mtx_plain);
-  cnd_init (&sdl->cond);
-
-#define start_thread(no)\
-  if(_ctx_max_threads>no){ \
-    static void *args[2]={(void*)no, };\
-    thrd_t tid;\
-    args[1]=sdl;\
-    thrd_create (&tid, (void*)sdl_render_fun, args);\
+    width  = maxwidth;
+    height = maxheight;
   }
-  start_thread(0);
-  start_thread(1);
-  start_thread(2);
-  start_thread(3);
-  start_thread(4);
-  start_thread(5);
-  start_thread(6);
-  start_thread(7);
-  start_thread(8);
-  start_thread(9);
-  start_thread(10);
-  start_thread(11);
-  start_thread(12);
-  start_thread(13);
-  start_thread(14);
-  start_thread(15);
-#undef start_thread
-
-  ctx_flush (sdl->ctx);
-  return sdl->ctx;
-#else
-  return NULL;
+  if (width > maxwidth) width = maxwidth;
+  if (height > maxheight) height = maxheight;
+  termimg->ctx = ctx;
+  termimg->width  = width;
+  termimg->height = height;
+  termimg->lines = 0;
+  termimg->pixels = (uint8_t*)malloc (width * height * 3);
+  termimg->host = ctx_new_for_framebuffer (termimg->pixels,
+                                           width, height,
+                                           width * 3, CTX_FORMAT_RGB8);
+  _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, termimg);
+  ctx_set_size (ctx, width, height);
+  ctx_font_size (ctx, 14.0f);
+  termimg->render = ctx_termimg_render;
+  termimg->flush = (void(*)(void*))ctx_termimg_flush;
+  termimg->free  = (void(*)(void*))ctx_termimg_free;
 #endif
-}
-#else
 
-int ctx_renderer_is_sdl (Ctx *ctx)
-{
-  return 0;
+  return ctx;
 }
+
 #endif
 
 #if CTX_FORMATTER
@@ -21376,7 +23941,7 @@ const char *_ctx_code_to_name (int code)
           case CTX_SET_PIXEL:            return "setPixel"; break;
           case CTX_GLOBAL_ALPHA:         return "globalAlpha"; break;
           case CTX_TEXT:                 return "text"; break;
-          case CTX_TEXT_STROKE:          return "textStroke"; break;
+          case CTX_STROKE_TEXT:          return "strokeText"; break;
           case CTX_SAVE:                 return "save"; break;
           case CTX_RESTORE:              return "restore"; break;
           case CTX_NEW_PAGE:             return "newPage"; break;
@@ -21399,6 +23964,7 @@ const char *_ctx_code_to_name (int code)
           case CTX_REL_ARC_TO:           return "relArcTo"; break;
           case CTX_GLYPH:                return "glyph"; break;
           case CTX_TEXTURE:              return "texture"; break;
+          case CTX_DEFINE_TEXTURE:       return "defineTexture"; break;
           case CTX_IDENTITY:             return "identity"; break;
           case CTX_CLOSE_PATH:           return "closePath"; break;
           case CTX_PRESERVE:             return "preserve"; break;
@@ -21434,6 +24000,8 @@ const char *_ctx_code_to_name (int code)
           case CTX_LINE_JOIN:            return "lineJoin"; break;
           case CTX_LINE_CAP:             return "lineCap"; break;
           case CTX_LINE_WIDTH:           return "lineWidth"; break;
+          case CTX_LINE_DASH_OFFSET:     return "lineDashOffset"; break;
+          case CTX_IMAGE_SMOOTHING:      return "imageSmoothing"; break;
           case CTX_SHADOW_BLUR:          return "shadowBlur";  break;
           case CTX_FILL_RULE:            return "fillRule"; break;
         }
@@ -21479,6 +24047,8 @@ static void _ctx_print_name (CtxFormatter *formatter, int code)
         case CTX_LINE_JOIN:         name[1]='j'; break;
         case CTX_LINE_CAP:          name[1]='c'; break;
         case CTX_LINE_WIDTH:        name[1]='w'; break;
+        case CTX_LINE_DASH_OFFSET:  name[1]='D'; break;
+        case CTX_IMAGE_SMOOTHING:   name[1]='S'; break;
         case CTX_SHADOW_BLUR:       name[1]='s'; break;
         case CTX_SHADOW_COLOR:      name[1]='C'; break;
         case CTX_SHADOW_OFFSET_X:   name[1]='x'; break;
@@ -21616,6 +24186,18 @@ ctx_print_entry_enum (CtxFormatter *formatter, CtxEntry *entry, int args)
   _ctx_print_endcmd (formatter);
 }
 
+
+static void
+ctx_print_a85 (CtxFormatter *formatter, uint8_t *data, int length)
+{
+  char *tmp = malloc (ctx_a85enc_len (length));
+  ctx_a85enc (data, tmp, length);
+  ctx_formatter_addstr (formatter, " ~", 2);
+  ctx_formatter_addstr (formatter, tmp, -1);
+  ctx_formatter_addstr (formatter, "~ ", 2);
+  free (tmp);
+}
+
 static void
 ctx_print_escaped_string (CtxFormatter *formatter, const char *string)
 {
@@ -21659,6 +24241,12 @@ ctx_print_float (CtxFormatter *formatter, float val)
   ctx_formatter_addstr (formatter, temp, -1);
 }
 
+static void
+ctx_print_int (CtxFormatter *formatter, int val)
+{
+  ctx_formatter_addstrf (formatter, "%i", val);
+}
+
 static void
 ctx_print_entry (CtxFormatter *formatter, CtxEntry *entry, int args)
 {
@@ -21718,7 +24306,43 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
         ctx_print_entry (formatter, entry, 2);
         break;
       case CTX_TEXTURE:
-        ctx_print_entry (formatter, entry, 3);
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, c->texture.eid);
+        ctx_formatter_addstrf (formatter, "\", ");
+        ctx_print_float (formatter, c->texture.x);
+        ctx_formatter_addstrf (formatter, ", ");
+        ctx_print_float (formatter, c->texture.y);
+        ctx_formatter_addstrf (formatter, " ");
+        _ctx_print_endcmd (formatter);
+        break;
+
+      case CTX_DEFINE_TEXTURE:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, c->define_texture.eid);
+        ctx_formatter_addstrf (formatter, "\", ");
+        ctx_print_int (formatter, c->define_texture.width);
+        ctx_formatter_addstrf (formatter, ", ");
+        ctx_print_int (formatter, c->define_texture.height);
+        ctx_formatter_addstrf (formatter, ",%i, ", c->define_texture.format);
+
+        uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
+#if 1
+
+        int stride = ctx_pixel_format_get_stride (c->define_texture.format, c->define_texture.width);
+        //fprintf (stderr, "encoding %i bytes\n", c->define_texture.height *stride);
+        ctx_print_a85 (formatter, pixel_data, c->define_texture.height * stride);
+#else
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, pixel_data);
+        ctx_formatter_addstrf (formatter, "\" ");
+
+#endif
+
+        _ctx_print_endcmd (formatter);
+        break;
+
         break;
       case CTX_REL_ARC_TO:
       case CTX_ARC_TO:
@@ -21745,6 +24369,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
       case CTX_MITER_LIMIT:
       case CTX_ROTATE:
       case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
       case CTX_GLOBAL_ALPHA:
       case CTX_SHADOW_BLUR:
       case CTX_SHADOW_OFFSET_X:
@@ -21777,14 +24402,21 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
         if (formatter->longform ||  1)
           {
             _ctx_indent (formatter);
-            switch ( (int) c->set_color.model)
+            int model = (int) c->set_color.model;
+            const char *suffix="";
+            if (model & 512)
+            {
+              model = model & 511;
+              suffix = "S";
+            }
+            switch (model)
               {
                 case CTX_GRAY:
-                  ctx_formatter_addstrf (formatter, "gray ");
+                  ctx_formatter_addstrf (formatter, "gray%s ", suffix);
                   ctx_print_float (formatter, c->graya.g);
                   break;
                 case CTX_GRAYA:
-                  ctx_formatter_addstrf (formatter, "graya ");
+                  ctx_formatter_addstrf (formatter, "graya%s ", suffix);
                   ctx_print_float (formatter, c->graya.g);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->graya.a);
@@ -21792,7 +24424,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                 case CTX_RGBA:
                   if (c->rgba.a != 1.0)
                   {
-                    ctx_formatter_addstrf (formatter, "rgba ");
+                    ctx_formatter_addstrf (formatter, "rgba%s ", suffix);
                     ctx_print_float (formatter, c->rgba.r);
                     ctx_formatter_addstrf (formatter, " ");
                     ctx_print_float (formatter, c->rgba.g);
@@ -21806,12 +24438,12 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                 case CTX_RGB:
                   if (c->rgba.r == c->rgba.g && c->rgba.g == c->rgba.b)
                   {
-                    ctx_formatter_addstrf (formatter, "gray ");
+                    ctx_formatter_addstrf (formatter, "gray%s ", suffix);
                     ctx_print_float (formatter, c->rgba.r);
                     ctx_formatter_addstrf (formatter, " ");
                     break;
                   }
-                  ctx_formatter_addstrf (formatter, "rgb ");
+                  ctx_formatter_addstrf (formatter, "rgb%s ", suffix);
                   ctx_print_float (formatter, c->rgba.r);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->rgba.g);
@@ -21819,7 +24451,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->rgba.b);
                   break;
                 case CTX_DRGB:
-                  ctx_formatter_addstrf (formatter, "drgb ");
+                  ctx_formatter_addstrf (formatter, "drgb%s ", suffix);
                   ctx_print_float (formatter, c->rgba.r);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->rgba.g);
@@ -21827,7 +24459,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->rgba.b);
                   break;
                 case CTX_DRGBA:
-                  ctx_formatter_addstrf (formatter, "drgba ");
+                  ctx_formatter_addstrf (formatter, "drgba%s ", suffix);
                   ctx_print_float (formatter, c->rgba.r);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->rgba.g);
@@ -21837,7 +24469,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->rgba.a);
                   break;
                 case CTX_CMYK:
-                  ctx_formatter_addstrf (formatter, "cmyk ");
+                  ctx_formatter_addstrf (formatter, "cmyk%s ", suffix);
                   ctx_print_float (formatter, c->cmyka.c);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->cmyka.m);
@@ -21847,7 +24479,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->cmyka.k);
                   break;
                 case CTX_CMYKA:
-                  ctx_formatter_addstrf (formatter, "cmyka ");
+                  ctx_formatter_addstrf (formatter, "cmyka%s ", suffix);
                   ctx_print_float (formatter, c->cmyka.c);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->cmyka.m);
@@ -21859,7 +24491,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->cmyka.a);
                   break;
                 case CTX_DCMYK:
-                  ctx_formatter_addstrf (formatter, "dcmyk ");
+                  ctx_formatter_addstrf (formatter, "dcmyk%s ", suffix);
                   ctx_print_float (formatter, c->cmyka.c);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->cmyka.m);
@@ -21869,7 +24501,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
                   ctx_print_float (formatter, c->cmyka.k);
                   break;
                 case CTX_DCMYKA:
-                  ctx_formatter_addstrf (formatter, "dcmyka ");
+                  ctx_formatter_addstrf (formatter, "dcmyka%s ", suffix);
                   ctx_print_float (formatter, c->cmyka.c);
                   ctx_formatter_addstrf (formatter, " ");
                   ctx_print_float (formatter, c->cmyka.m);
@@ -21943,6 +24575,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
       case CTX_LINE_JOIN:
       case CTX_COMPOSITING_MODE:
       case CTX_BLEND_MODE:
+      case CTX_IMAGE_SMOOTHING:
         ctx_print_entry_enum (formatter, entry, 1);
         break;
       case CTX_GRADIENT_STOP:
@@ -21956,7 +24589,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c)
         _ctx_print_endcmd (formatter);
         break;
       case CTX_TEXT:
-      case CTX_TEXT_STROKE:
+      case CTX_STROKE_TEXT:
       case CTX_FONT:
         _ctx_print_name (formatter, entry->code);
         ctx_formatter_addstrf (formatter, "\"");
@@ -22094,7 +24727,6 @@ void ctx_dirty_rect (Ctx *ctx, int *x, int *y, int *width, int *height)
   if (y) { *y = ctx->state.min_y; }
   if (width) { *width = ctx->state.max_x - ctx->state.min_x; }
   if (height) { *height = ctx->state.max_y - ctx->state.min_y; }
-  //fprintf (stderr, "%i %i %ix%i\n", *x, *y, *width, *height);
 }
 
 #if CTX_CURRENT_PATH
@@ -22170,93 +24802,482 @@ ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
      }
     if (got_coord)
     {
-      minx = ctx_minf (minx, x);
-      miny = ctx_minf (miny, y);
-      maxx = ctx_maxf (maxx, x);
-      maxy = ctx_maxf (maxy, y);
+      minx = ctx_minf (minx, x);
+      miny = ctx_minf (miny, y);
+      maxx = ctx_maxf (maxx, x);
+      maxy = ctx_maxf (maxy, y);
+    }
+  }
+
+  if (ex1) *ex1 = minx;
+  if (ey1) *ey1 = miny;
+  if (ex2) *ex2 = maxx;
+  if (ey2) *ey2 = maxy;
+}
+
+#else
+void
+ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
+{
+}
+#endif
+
+
+static void
+ctx_gstate_push (CtxState *state)
+{
+  if (state->gstate_no + 1 >= CTX_MAX_STATES)
+    { return; }
+  state->gstate_stack[state->gstate_no] = state->gstate;
+  state->gstate_no++;
+  ctx_state_set (state, CTX_new_state, 0.0);
+  state->has_clipped=0;
+}
+
+static void
+ctx_gstate_pop (CtxState *state)
+{
+  if (state->gstate_no <= 0)
+    { return; }
+  state->gstate = state->gstate_stack[state->gstate_no-1];
+  state->gstate_no--;
+}
+
+void
+ctx_close_path (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
+}
+
+int _ctx_is_rasterizer (Ctx *ctx);
+
+void
+ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
+                    CtxPixelFormat format, int dst_stride,
+                    uint8_t *dst_data)
+{
+   if (0)
+   {
+   }
+#if CTX_RASTERIZER
+   else if (_ctx_is_rasterizer (ctx))
+   {
+     CtxRasterizer *rasterizer = (CtxRasterizer*)ctx->renderer;
+     if (rasterizer->format->pixel_format == format)
+     {
+       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
+       int bytes_per_pix = rasterizer->format->bpp/8;
+       int y = 0;
+       for (int v = sy; v < sy + sh; v++, y++)
+       {
+         int x = 0;
+         for (int u = sx; u < sx + sw; u++, x++)
+         {
+            uint8_t* src_buf = (uint8_t*)rasterizer->buf;
+            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * rasterizer->blit_stride + u 
* bytes_per_pix], bytes_per_pix);
+         }
+       }
+       return;
+     }
+   }
+#endif
+#if CTX_FB
+   else if (format == CTX_FORMAT_RGBA8 &&
+                   (
+                   ctx_renderer_is_fb (ctx)
+#if CTX_SDL
+                   || ctx_renderer_is_sdl (ctx)
+#endif
+                   ))
+   {
+     CtxTiled *tiled = (CtxTiled*)ctx->renderer;
+     {
+       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
+       int bytes_per_pix = 4;
+       int y = 0;
+       for (int v = sy; v < sy + sh; v++, y++)
+       {
+         int x = 0;
+         for (int u = sx; u < sx + sw; u++, x++)
+         {
+            uint8_t* src_buf = (uint8_t*)tiled->pixels;
+            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * tiled->width * bytes_per_pix 
+ u * bytes_per_pix], bytes_per_pix);
+         }
+       }
+       return;
+     }
+   }
+#endif
+}
+
+void
+ctx_put_image_data (Ctx *ctx, int w, int h, int stride, int format,
+                    uint8_t *data,
+                    int ox, int oy,
+                    int dirtyX, int dirtyY,
+                    int dirtyWidth, int dirtyHeight)
+{
+   char eid[65]="";
+   ctx_save (ctx);
+   ctx_identity (ctx);
+   ctx_define_texture (ctx, NULL, w, h, stride, format, data, eid);
+   if (eid[0])
+   {
+     // XXX set compositor to source
+     ctx_compositing_mode (ctx, CTX_COMPOSITE_COPY);
+     ctx_draw_texture_clipped (ctx, eid, ox, oy, w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+   }
+   ctx_restore (ctx);
+}
+
+static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h)
+{
+  ctx = ctx->texture_cache;
+  CtxList *to_remove = NULL;
+  int ret = 0;
+  for (CtxList *l = ctx->eid_db; l; l = l->next)
+  {
+    CtxEidInfo *eid_info = (CtxEidInfo*)l->data;
+    if (ctx->frame - eid_info->frame >= 2)
+    {
+      ctx_list_prepend (&to_remove, eid_info);
+    }
+    else if (!strcmp (eid_info->eid, eid) &&
+             ctx->frame - eid_info->frame < 2)
+    {
+    //FILE  *f  = fopen ("/tmp/l", "a");
+    //  fprintf (f, "%i good:%i %i %s\n", getpid(), ctx->frame, eid_info->frame, eid);
+    //fclose (f);
+      eid_info->frame = ctx->frame;
+      if (w) *w = eid_info->width;
+      if (h) *h = eid_info->height;
+      ret = 1;
     }
   }
-
-  if (ex1) *ex1 = minx;
-  if (ey1) *ey1 = miny;
-  if (ex2) *ex2 = maxx;
-  if (ey2) *ey2 = maxy;
+  while (to_remove)
+  {
+    CtxEidInfo *eid_info = (CtxEidInfo*)to_remove->data;
+    //FILE  *f  = fopen ("/tmp/l", "a");
+    //fprintf (f, "%i client removing %s\n", getpid(), eid_info->eid);
+    //fclose (f);
+    free (eid_info->eid);
+    free (eid_info);
+    ctx_list_remove (&ctx->eid_db, eid_info);
+    ctx_list_remove (&to_remove, eid_info);
+  }
+  return ret;
 }
 
-#else
-void
-ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
+void ctx_texture (Ctx *ctx, const char *eid, float x, float y)
 {
-}
-#endif
-
+  int eid_len = strlen (eid);
+  char ascii[41]="";
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid=ascii;
+  }
 
-static void
-ctx_gstate_push (CtxState *state)
+    //FILE  *f = fopen ("/tmp/l", "a");
+  if (ctx_eid_valid (ctx, eid, 0, 0))
+  {
+    ctx_process_cmd_str_float (ctx, CTX_TEXTURE, eid, x, y);
+    //fprintf (f, "setting texture eid %s\n", eid);
+  }
+  else
+  {
+    //fprintf (f, "tried setting invalid texture eid %s\n", eid);
+  }
+    //fclose (f);
+}
+int
+_ctx_frame (Ctx *ctx)
 {
-  if (state->gstate_no + 1 >= CTX_MAX_STATES)
-    { return; }
-  state->gstate_stack[state->gstate_no] = state->gstate;
-  state->gstate_no++;
-  ctx_state_set (state, CTX_new_state, 0.0);
-  state->has_clipped=0;
+        return ctx->frame;
 }
 
-static void
-ctx_gstate_pop (CtxState *state)
+void ctx_define_texture (Ctx *ctx, const char *eid, int width, int height, int stride, int format, void 
*data, char *ret_eid)
 {
-  if (state->gstate_no <= 0)
-    { return; }
-  state->gstate = state->gstate_stack[state->gstate_no-1];
-  state->gstate_no--;
-}
+  uint8_t hash[20]="";
+  char ascii[41]="";
+  int dst_stride = width;
+
+  dst_stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
+  if (stride <= 0)
+    stride = dst_stride;
+
+  int data_len = height * dst_stride;
+
+  if (eid == NULL)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+
+    {
+      uint8_t *src = (uint8_t*)data;
+      for (int y = 0; y < height; y++)
+      {
+         ctx_sha1_process (sha1, src, dst_stride);
+         src += stride;
+      }
+    }
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+  }
+
+  int eid_len = strlen (eid);
+
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+    eid_len = 40;
+  }
+
+  // we now have eid
+
+  if (ctx_eid_valid (ctx, eid, 0, 0))
+  {
+    ctx_texture (ctx, eid, 0.0, 0.0);
+  }
+  else
+
+  {
+    CtxEntry *commands;
+    int command_size = 1 + (data_len+1+1)/9 + 1 + (eid_len+1+1)/9 + 1 +   8;
+    if (ctx->renderer && ctx->renderer->process)
+    {
+       commands = (CtxEntry*)calloc (sizeof (CtxEntry), command_size);
+    }
+    else
+    {
+       commands = NULL;
+       ctx_drawlist_resize (&ctx->drawlist, ctx->drawlist.count + command_size);
+       commands = &(ctx->drawlist.entries[ctx->drawlist.count]);
+       memset (commands, 0, sizeof (CtxEntry) * command_size);
+    }
+    /* bottleneck,  we can avoid copying sometimes - and even when copying
+     * we should cut this down to one copy, direct to the drawlist.
+     *
+     */
+    commands[0] = ctx_u32 (CTX_DEFINE_TEXTURE, width, height);
+    commands[1].data.u16[0] = format;
+
+    int pos = 2;
+
+    commands[pos].code        = CTX_DATA;
+    commands[pos].data.u32[0] = eid_len;
+    commands[pos].data.u32[1] = (eid_len+1+1)/9 + 1;
+    memcpy ((char *) &commands[pos+1].data.u8[0], eid, eid_len);
+    ((char *) &commands[pos+1].data.u8[0])[eid_len]=0;
+
+    pos = 2 + 1 + ctx_conts_for_entry (&commands[2]);
+    commands[pos].code        = CTX_DATA;
+    commands[pos].data.u32[0] = data_len;
+    commands[pos].data.u32[1] = (data_len+1+1)/9 + 1;
+    {
+      uint8_t *src = (uint8_t*)data;
+      uint8_t *dst = &commands[pos+1].data.u8[0];
+      for (int y = 0; y < height; y++)
+      {
+         memcpy (dst, src, dst_stride);
+         src += stride;
+         dst += dst_stride;
+      }
+    }
+    ((char *) &commands[pos+1].data.u8[0])[data_len]=0;
 
+    if (ctx->renderer && ctx->renderer->process)
+    {
+      ctx_process (ctx, commands);
+      free (commands);
+    }
+    else
+    {
+       ctx->drawlist.count += ctx_conts_for_entry (commands) + 1;
+    }
 
+    CtxEidInfo *eid_info = (CtxEidInfo*)calloc (sizeof (CtxEidInfo), 1);
+    eid_info->eid        = strdup (eid);
+    eid_info->width      = width;
+    eid_info->height     = height;
+    eid_info->frame      = ctx->texture_cache->frame;
+    //fprintf (stderr, "%i\n", eid_info->frame);
+    ctx_list_prepend (&ctx->texture_cache->eid_db, eid_info);
+  }
 
+  if (ret_eid)
+  {
+    strcpy (ret_eid, eid);
+    ret_eid[64]=0;
+  }
+}
 
 void
-ctx_close_path (Ctx *ctx)
+ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid)
 {
-  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
-}
+  const char *eid = path;
+  char ascii[41]="";
+  int eid_len = strlen (eid);
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+  }
 
+  if (ctx_eid_valid (ctx, eid , tw, th))
+  {
+     if (reid)
+     {
+       strcpy (reid, eid);
+     }
+     return;
+  }
 
-uint8_t *
-ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh, int format, int stride)
-{
-   // NYI
-   return NULL;
+#ifdef STBI_INCLUDE_STB_IMAGE_H
+    CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
+  int w, h, components;
+  unsigned char *pixels = NULL;
+
+  if (!strncmp (path, "file://", 7))
+  {
+    pixels = stbi_load (path + 7, &w, &h, &components, 0);
+  }
+  else
+  {
+    unsigned char *data = NULL;
+    long length = 0;
+    ctx_get_contents (path, &data, &length);
+    if (data)
+    {
+       pixels = stbi_load_from_memory (data, length, &w, &h, &components, 0);
+       free (data);
+    }
+  }
+
+  if (pixels)
+  {
+    switch (components)
+    {
+      case 1: pixel_format = CTX_FORMAT_GRAY8;  break;
+      case 2: pixel_format = CTX_FORMAT_GRAYA8; break;
+      case 3: pixel_format = CTX_FORMAT_RGB8;   break;
+      case 4: pixel_format = CTX_FORMAT_RGBA8;  break;
+    }
+    if (tw) *tw = w;
+    if (th) *th = h;
+    ctx_define_texture (ctx, eid, w, h, w * components, pixel_format, pixels, 
+                             reid);
+    free (pixels);
+  }
+  else
+  {
+    fprintf (stderr, "texture loading problem for %s\n", path);
+  }
+#endif
 }
 
 void
-ctx_put_image_data (Ctx *ctx, uint8_t *data, int w, int h, int format, int stride,
-                    int dx, int dy, int dirtyX, int dirtyY,
-                    int dirtyWidth, int dirtyHeight)
+ctx_draw_texture_clipped  (Ctx *ctx, const char *eid,
+                           float x, float y,
+                           float width, float height,
+                           float clip_x, float clip_y,
+                           float clip_width, float clip_height)
+{
+  int tex_width  = 0;
+  int tex_height = 0;
+  if (ctx_eid_valid (ctx, eid , &tex_width, &tex_height))
+  {
+    if (width > 0.0 && height > 0.0)
+    {
+      ctx_save (ctx);
+#if 0
+      if (clip_width > 0.0f)
+      {
+        ctx_rectangle (ctx, clip_x, clip_y, clip_width, clip_height);
+        ctx_clip (ctx);
+      }
+#endif
+      ctx_rectangle (ctx, x, y, width, height);
+      if (clip_width > 0.0f)
+      {
+        ctx_translate (ctx, -clip_x, -clip_y);
+        ctx_scale (ctx, width/clip_width, height/clip_height);
+      }
+      else
+      {
+        ctx_scale (ctx, width/tex_width, height/tex_height);
+      }
+      //ctx_texture (ctx, eid, x / (width/tex_width), y / (height/tex_height));
+      ctx_texture (ctx, eid, x, y);// / (width/tex_width), y / (height/tex_height));
+      ctx_fill (ctx);
+      ctx_restore (ctx);
+    }
+  }
+}
+
+void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, float h)
 {
-   // NYI
+  ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0);
 }
-                    
 
-void ctx_texture (Ctx *ctx, int id, float x, float y)
+void ctx_draw_image_clipped (Ctx *ctx, const char *path, float x, float y, float w, float h, float sx, float 
sy, float swidth, float sheight)
 {
-  CtxEntry commands[2];
-  if (id < 0) { return; }
-  commands[0] = ctx_u32 (CTX_TEXTURE, id, 0);
-  commands[1] = ctx_f   (CTX_CONT, x, y);
-  ctx_process (ctx, commands);
+  char reteid[65];
+  int width, height;
+  ctx_texture_load (ctx, path, &width, &height, reteid);
+  if (reteid[0])
+  {
+    ctx_draw_texture_clipped (ctx, reteid, x, y, w, h, sx, sy, swidth, sheight);
+  }
 }
 
 void
-ctx_image_path (Ctx *ctx, const char *path, float x, float y)
+ctx_draw_image (Ctx *ctx, const char *path, float x, float y, float w, float h)
 {
-  int id = ctx_texture_load (ctx, -1, path, NULL, NULL);
-  ctx_texture (ctx, id, x, y);
-
-  // query if image exists .. 
-  //   if it doesnt load, decode, encode, upload to path/
+  ctx_draw_image_clipped (ctx, path, x, y, w, h, 0,0,0,0);
 }
 
-
 void
 ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
@@ -22266,7 +25287,6 @@ ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_
   ctx_process (ctx, &command);
 }
 
-
 void
 ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1)
 {
@@ -22290,30 +25310,6 @@ ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1,
   ctx_process (ctx, command);
 }
 
-void ctx_gradient_add_stop_u8
-(Ctx *ctx, float pos, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
-  CtxEntry entry = ctx_f (CTX_GRADIENT_STOP, pos, 0);
-  entry.data.u8[4+0] = r;
-  entry.data.u8[4+1] = g;
-  entry.data.u8[4+2] = b;
-  entry.data.u8[4+3] = a;
-  ctx_process (ctx, &entry);
-}
-
-void ctx_gradient_add_stop
-(Ctx *ctx, float pos, float r, float g, float b, float a)
-{
-  int ir = r * 255;
-  int ig = g * 255;
-  int ib = b * 255;
-  int ia = a * 255;
-  ir = CTX_CLAMP (ir, 0,255);
-  ig = CTX_CLAMP (ig, 0,255);
-  ib = CTX_CLAMP (ib, 0,255);
-  ia = CTX_CLAMP (ia, 0,255);
-  ctx_gradient_add_stop_u8 (ctx, pos, ir, ig, ib, ia);
-}
 
 void ctx_preserve (Ctx *ctx)
 {
@@ -22359,16 +25355,18 @@ ctx_collect_events (CtxEvent *event, void *data, void *data2)
 {
   Ctx *ctx = (Ctx*)data;
   CtxEvent *copy;
-  if (event->type == CTX_KEY_DOWN && !strcmp (event->string, "idle"))
+  if (event->type == CTX_KEY_PRESS && !strcmp (event->string, "idle"))
     return;
   copy = (CtxEvent*)malloc (sizeof (CtxEvent));
   *copy = *event;
+  if (copy->string)
+    copy->string = strdup (event->string);
   ctx_list_append_full (&ctx->events.events, copy, ctx_event_free, NULL);
 }
 #endif
 
 #if CTX_EVENTS
-static void _ctx_bindings_key_down (CtxEvent *event, void *data1, void *data2);
+static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2);
 #endif
 
 void ctx_reset (Ctx *ctx)
@@ -22376,7 +25374,7 @@ void ctx_reset (Ctx *ctx)
         /* we do the callback reset first - maybe we need two cbs,
          * one for before and one after default impl?
          *
-         * threaded fb and sdl needs to sync
+         * tiled fb and sdl needs to sync
          */
   if (ctx->renderer && ctx->renderer->reset)
     ctx->renderer->reset (ctx->renderer);
@@ -22393,19 +25391,16 @@ void ctx_reset (Ctx *ctx)
   if (ctx->events.ctx_get_event_enabled)
   {
     ctx_clear_bindings (ctx);
+    ctx_listen_full (ctx, 0,0,0,0,
+                     CTX_KEY_PRESS, _ctx_bindings_key_press, ctx, ctx,
+                     NULL, NULL);
 
     ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_DOWN, _ctx_bindings_key_down, ctx, ctx,
+                     CTX_KEY_UP, ctx_collect_events, ctx, ctx,
                      NULL, NULL);
-#if 0
-    // should be enabled if a mask of CTX_KEY_DOWN|UP is passed to get_event?
-    ctx_listen_full (ctx, 0, 0, 0,0,
+    ctx_listen_full (ctx, 0,0,0,0,
                      CTX_KEY_DOWN, ctx_collect_events, ctx, ctx,
                      NULL, NULL);
-    ctx_listen_full (ctx, 0, 0, 0,0,
-                     CTX_KEY_UP, ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-#endif
 
     ctx_listen_full (ctx, 0, 0, ctx->events.width, ctx->events.height,
                      (CtxEventType)(CTX_PRESS|CTX_RELEASE|CTX_MOTION), ctx_collect_events, ctx, ctx,
@@ -22425,7 +25420,7 @@ void ctx_clip (Ctx *ctx)
 }
 
 void
-ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len);
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
 
 void ctx_save (Ctx *ctx)
 {
@@ -22452,6 +25447,34 @@ void ctx_line_width (Ctx *ctx, float x)
     CTX_PROCESS_F1 (CTX_LINE_WIDTH, x);
 }
 
+float ctx_get_miter_limit (Ctx *ctx)
+{
+  return ctx->state.gstate.miter_limit;
+}
+
+float ctx_get_line_dash_offset (Ctx *ctx)
+{
+  return ctx->state.gstate.line_dash_offset;
+}
+
+void ctx_line_dash_offset (Ctx *ctx, float x)
+{
+  if (ctx->state.gstate.line_dash_offset != x)
+    CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x);
+}
+
+int ctx_get_image_smoothing (Ctx *ctx)
+{
+  return ctx->state.gstate.image_smoothing;
+}
+
+void ctx_image_smoothing (Ctx *ctx, int enabled)
+{
+  if (ctx_get_image_smoothing (ctx) != enabled)
+    CTX_PROCESS_U8 (CTX_IMAGE_SMOOTHING, enabled);
+}
+
+
 void ctx_line_dash (Ctx *ctx, float *dashes, int count)
 {
   ctx_process_cmd_str_with_len (ctx, CTX_LINE_DASH, (char*)(dashes), count, 0, count * 4);
@@ -22535,7 +25558,7 @@ _ctx_font (Ctx *ctx, const char *name)
 
 #if 0
 void
-ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len)
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len)
 {
   if (len <= 0) len = strlen (string);
   ctx_process_cmd_str (ctx, CTX_SET, string, key_hash, len);
@@ -22558,15 +25581,29 @@ ctx_get (Ctx *ctx, const char *key)
 #endif
 
 void
-ctx_font (Ctx *ctx, const char *name)
+ctx_font_family (Ctx *ctx, const char *name)
 {
 #if CTX_BACKEND_TEXT
   ctx_process_cmd_str (ctx, CTX_FONT, name, 0, 0);
+  _ctx_font (ctx, name);
 #else
   _ctx_font (ctx, name);
 #endif
 }
 
+void
+ctx_font (Ctx *ctx, const char *family_name)
+{
+  // should also parse size
+  ctx_font_family (ctx, family_name);
+}
+
+const char *
+ctx_get_font (Ctx *ctx)
+{
+  return ctx_fonts[ctx->state.gstate.font].name;
+}
+
 void ctx_line_to (Ctx *ctx, float x, float y)
 {
   if (!ctx->state.has_moved)
@@ -22648,11 +25685,47 @@ void ctx_rel_move_to (Ctx *ctx, float x, float y)
   CTX_PROCESS_F (CTX_REL_MOVE_TO,x,y);
 }
 
+CtxLineJoin ctx_get_line_join (Ctx *ctx)
+{
+  return ctx->state.gstate.line_join;
+}
+
+CtxCompositingMode ctx_get_compositing_mode (Ctx *ctx)
+{
+  return ctx->state.gstate.compositing_mode;
+}
+
+CtxBlend ctx_get_blend_mode (Ctx *ctx)
+{
+  return ctx->state.gstate.blend_mode;
+}
+
+CtxTextAlign ctx_get_text_align  (Ctx *ctx)
+{
+  return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_text_align);
+}
+
+CtxTextBaseline ctx_get_text_baseline (Ctx *ctx)
+{
+  return (CtxTextBaseline)ctx_state_get (&ctx->state, CTX_text_baseline);
+}
+
+CtxLineCap ctx_get_line_cap (Ctx *ctx)
+{
+  return ctx->state.gstate.line_cap;
+}
+
+CtxFillRule ctx_get_fill_rule (Ctx *ctx)
+{
+  return ctx->state.gstate.fill_rule;
+}
+
 void ctx_line_cap (Ctx *ctx, CtxLineCap cap)
 {
   if (ctx->state.gstate.line_cap != cap)
     CTX_PROCESS_U8 (CTX_LINE_CAP, cap);
 }
+
 void ctx_fill_rule (Ctx *ctx, CtxFillRule fill_rule)
 {
   if (ctx->state.gstate.fill_rule != fill_rule)
@@ -22665,11 +25738,13 @@ void ctx_line_join (Ctx *ctx, CtxLineJoin join)
 }
 void ctx_blend_mode (Ctx *ctx, CtxBlend mode)
 {
-  CTX_PROCESS_U8 (CTX_BLEND_MODE, mode);
+  if (ctx->state.gstate.blend_mode != mode)
+    CTX_PROCESS_U8 (CTX_BLEND_MODE, mode);
 }
 void ctx_compositing_mode (Ctx *ctx, CtxCompositingMode mode)
 {
-  CTX_PROCESS_U8 (CTX_COMPOSITING_MODE, mode);
+  if (ctx->state.gstate.compositing_mode != mode)
+    CTX_PROCESS_U8 (CTX_COMPOSITING_MODE, mode);
 }
 void ctx_text_align (Ctx *ctx, CtxTextAlign text_align)
 {
@@ -22884,6 +25959,9 @@ ctx_flush (Ctx *ctx)
 #endif
   if (ctx->renderer && ctx->renderer->flush)
     ctx->renderer->flush (ctx->renderer);
+  ctx->frame++;
+  if (ctx->texture_cache != ctx)
+    ctx->texture_cache->frame++;
   ctx->drawlist.count = 0;
   ctx_state_init (&ctx->state);
 }
@@ -22896,6 +25974,9 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
   CtxCommand *c = (CtxCommand *) entry;
   switch (entry->code)
     {
+      case CTX_LINE_DASH_OFFSET:
+        state->gstate.line_dash_offset = ctx_arg_float (0);
+        break;
       case CTX_LINE_WIDTH:
         state->gstate.line_width = ctx_arg_float (0);
         break;
@@ -22950,12 +26031,25 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
                                               (char*)c->colorspace.data,
                                               c->colorspace.data_len);
         break;
+      case CTX_IMAGE_SMOOTHING:
+        state->gstate.image_smoothing = c->entry.data.u8[0];
+        break;
 
       case CTX_COLOR:
         {
-          CtxColor *color = &state->gstate.source.color;
-          state->gstate.source.type = CTX_SOURCE_COLOR;
-          switch ( (int) ctx_arg_float (0) )
+          CtxColor *color = &state->gstate.source_fill.color;
+
+          if ( ((int) ctx_arg_float (0)) & 512)
+          {
+             color = &state->gstate.source_stroke.color;
+             state->gstate.source_stroke.type = CTX_SOURCE_COLOR;
+          }
+          else
+          {
+             state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+          }
+          //float components[5]={c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a};
+          switch ( ((int) ctx_arg_float (0)) & 511)
             {
               case CTX_RGB:
                 ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
@@ -22991,8 +26085,8 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
         break;
       case CTX_SET_RGBA_U8:
         //ctx_source_deinit (&state->gstate.source);
-        state->gstate.source.type = CTX_SOURCE_COLOR;
-        ctx_color_set_RGBA8 (state, &state->gstate.source.color,
+        state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+        ctx_color_set_RGBA8 (state, &state->gstate.source_fill.color,
                              ctx_arg_u8 (0),
                              ctx_arg_u8 (1),
                              ctx_arg_u8 (2),
@@ -23024,15 +26118,15 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
           dy = (y1-y0) / length;
           start = (x0 * dx + y0 * dy) / length;
           end =   (x1 * dx + y1 * dy) / length;
-          state->gstate.source.linear_gradient.length = length;
-          state->gstate.source.linear_gradient.dx = dx;
-          state->gstate.source.linear_gradient.dy = dy;
-          state->gstate.source.linear_gradient.start = start;
-          state->gstate.source.linear_gradient.end = end;
-          state->gstate.source.linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
-          state->gstate.source.type = CTX_SOURCE_LINEAR_GRADIENT;
-          state->gstate.source.transform = state->gstate.transform;
-          ctx_matrix_invert (&state->gstate.source.transform);
+          state->gstate.source_fill.linear_gradient.length = length;
+          state->gstate.source_fill.linear_gradient.dx = dx;
+          state->gstate.source_fill.linear_gradient.dy = dy;
+          state->gstate.source_fill.linear_gradient.start = start;
+          state->gstate.source_fill.linear_gradient.end = end;
+          state->gstate.source_fill.linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
+          state->gstate.source_fill.type = CTX_SOURCE_LINEAR_GRADIENT;
+          state->gstate.source_fill.transform = state->gstate.transform;
+          ctx_matrix_invert (&state->gstate.source_fill.transform);
         }
         break;
       case CTX_RADIAL_GRADIENT:
@@ -23043,16 +26137,16 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
           float x1 = ctx_arg_float (3);
           float y1 = ctx_arg_float (4);
           float r1 = ctx_arg_float (5);
-          state->gstate.source.radial_gradient.x0 = x0;
-          state->gstate.source.radial_gradient.y0 = y0;
-          state->gstate.source.radial_gradient.r0 = r0;
-          state->gstate.source.radial_gradient.x1 = x1;
-          state->gstate.source.radial_gradient.y1 = y1;
-          state->gstate.source.radial_gradient.r1 = r1;
-          state->gstate.source.radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
-          state->gstate.source.type      = CTX_SOURCE_RADIAL_GRADIENT;
-          state->gstate.source.transform = state->gstate.transform;
-          ctx_matrix_invert (&state->gstate.source.transform);
+          state->gstate.source_fill.radial_gradient.x0 = x0;
+          state->gstate.source_fill.radial_gradient.y0 = y0;
+          state->gstate.source_fill.radial_gradient.r0 = r0;
+          state->gstate.source_fill.radial_gradient.x1 = x1;
+          state->gstate.source_fill.radial_gradient.y1 = y1;
+          state->gstate.source_fill.radial_gradient.r1 = r1;
+          state->gstate.source_fill.radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
+          state->gstate.source_fill.type      = CTX_SOURCE_RADIAL_GRADIENT;
+          state->gstate.source_fill.transform = state->gstate.transform;
+          ctx_matrix_invert (&state->gstate.source_fill.transform);
         }
         break;
     }
@@ -23372,6 +26466,7 @@ ctx_state_init (CtxState *state)
   state->gstate.global_alpha_f  = 1.0;
   state->gstate.font_size       = 12;
   state->gstate.line_width      = 2.0;
+  state->gstate.image_smoothing = 1;
   ctx_state_set (state, CTX_line_spacing, 1.0f);
   state->min_x                  = 8192;
   state->min_y                  = 8192;
@@ -23408,6 +26503,7 @@ _ctx_init (Ctx *ctx)
 #if CTX_BITPACK
   ctx->drawlist.flags |= CTX_TRANSFORMATION_BITPACK;
 #endif
+  ctx->texture_cache = ctx;
 }
 
 static void ctx_setup ();
@@ -23512,7 +26608,21 @@ ctx_render_ctx (Ctx *ctx, Ctx *d_ctx)
   ctx_iterator_init (&iterator, &ctx->drawlist, 0,
                      CTX_ITERATOR_EXPAND_BITPACK);
   while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_process (d_ctx, &command->entry); }
+    {
+#if 0
+     //  if (command->entry.code == 'i' ||
+     //      command->entry.code == 'I')
+       {
+
+       if (command->entry.code < 32 ||
+           command->entry.code > '~')
+       fprintf (stderr, "[%i]", command->entry.code);
+       else
+       fprintf (stderr, "%c", command->entry.code);
+       }
+#endif
+       ctx_process (d_ctx, &command->entry);
+    }
 }
 
 void ctx_quit (Ctx *ctx)
@@ -23531,13 +26641,33 @@ int  ctx_has_quit (Ctx *ctx)
 #endif
 }
 
+int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format)
+{
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->bpp;
+  return -1;
+}
 
-int ctx_pixel_format_bpp (CtxPixelFormat format)
+int ctx_pixel_format_get_stride (CtxPixelFormat format, int width)
 {
   CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
   if (info)
-    return info->bpp;
-  return -1;
+  {
+    switch (info->bpp)
+    {
+      case 0:
+      case 1:
+        return (width + 7)/8;
+      case 2:
+        return (width + 3)/4;
+      case 4:
+        return (width + 1)/2;
+      default:
+        return width * (info->bpp / 8);
+    }
+  }
+  return width;
 }
 
 int ctx_pixel_format_ebpp (CtxPixelFormat format)
@@ -23593,6 +26723,107 @@ void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source)
   ((CtxRasterizer*)ctx->renderer)->texture_source = texture_source;
 }
 
+void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache)
+{
+  ctx->texture_cache = texture_cache;
+}
+
+void ctx_set_transform (Ctx *ctx, float a, float b, float c, float d, float e, float f)
+{
+  ctx_identity (ctx);
+  ctx_apply_transform (ctx, a, b, c, d, e, f);
+}
+#ifndef NO_LIBCURL
+#include <curl/curl.h>
+static size_t
+ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *userp)
+{
+  CtxString *string = (CtxString*)userp;
+  ctx_string_append_data ((CtxString*)string, contents, size * nmemb);
+  return size * nmemb;
+}
+
+#endif
+
+int
+ctx_get_contents (const char     *uri,
+                  unsigned char **contents,
+                  long           *length)
+{
+  char temp_uri[PATH_MAX]; // XXX XXX breaks with data uri's
+  if (uri[0] == '/')
+  {
+    snprintf (temp_uri, sizeof (temp_uri)-1, "file://%s", uri);
+    uri = temp_uri;
+  }
+  else
+  {
+    snprintf (temp_uri, sizeof (temp_uri)-1, uri);
+    uri = temp_uri;
+  }
+
+  if (strchr (uri, '#'))
+   strchr (uri, '#')[0]=0;
+
+  for (CtxList *l = registered_contents; l; l = l->next)
+  {
+    CtxFileContent *c = l->data;
+    if (!strcmp (c->path, uri))
+    {
+      contents = malloc (c->length+1);
+      contents[c->length]=0;
+      if (length) *length = c->length;
+      return 0;
+    }
+  }
+
+  if (!strncmp (uri, "file://", 5))
+  {
+    if (strchr (uri, '?'))
+     strchr (uri, '?')[0]=0;
+  }
+
+  if (!strncmp (uri, "file://", 7))
+    return __ctx_file_get_contents (uri + 7, contents, length);
+  else
+  {
+#ifndef NO_LIBCURL
+  CURL *curl = curl_easy_init ();
+  CURLcode res;
+
+  curl_easy_setopt(curl, CURLOPT_URL, uri);
+  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+    CtxString *string = ctx_string_new ("");
+
+      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ctx_string_append_callback);
+   /* we pass our 'chunk' struct to the callback function */
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)string);
+
+  curl_easy_setopt(curl, CURLOPT_USERAGENT, "ctx/0.0");
+
+   res = curl_easy_perform(curl);
+  /* check for errors */
+  if(res != CURLE_OK) {
+          fprintf(stderr, "curl_easy_perform() failed: %s\n",
+            curl_easy_strerror(res));
+     curl_easy_cleanup (curl);
+  }
+  else
+  {
+     *contents = (unsigned char*)string->str;
+     *length = string->length;
+     ctx_string_free (string, 0);
+     curl_easy_cleanup (curl);
+     return 0;
+  }
+#else
+    return __ctx_file_get_contents (uri, contents, length);
+#endif
+  }
+  return -1;
+}
+
+
 #endif
 
 #endif // CTX_IMPLEMENTATION
@@ -23716,7 +26947,6 @@ ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uin
 }
 #endif
 
-
 CTX_INLINE static void
 ctx_RGBA8_associate_alpha (uint8_t *u8)
 {
@@ -23739,7 +26969,6 @@ ctx_RGBA8_associate_alpha (uint8_t *u8)
   }
 }
 
-
 CTX_INLINE static void
 ctx_u8_associate_alpha (int components, uint8_t *u8)
 {
@@ -23883,13 +27112,13 @@ static void
 ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
-  CtxBuffer *buffer = g->image.buffer;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
   ctx_assert (rasterizer);
   ctx_assert (g);
   ctx_assert (buffer);
-  int u = x - g->image.x0;
-  int v = y - g->image.y0;
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
   if ( u < 0 || v < 0 ||
        u >= buffer->width ||
        v >= buffer->height)
@@ -23899,6 +27128,57 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out
   else
     {
       int bpp = buffer->format->bpp/8;
+      if (rasterizer->state->gstate.image_smoothing)
+      {
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      src00 += v * buffer->stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < buffer->width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < buffer->height)
+      {
+        src10 = src00 + buffer->stride;
+        src11 = src01 + buffer->stride;
+      }
+      float dx = (x-(int)(x)) * 255.9;
+      float dy = (y-(int)(y)) * 255.9;
+
+      switch (bpp)
+      {
+      case 1:
+        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
+                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
+        rgba[3] = 255;
+        break;
+      case 2:
+        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
+                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
+        rgba[3] = ctx_lerp_u8 (ctx_lerp_u8 (src00[1], src01[1], dx),
+                               ctx_lerp_u8 (src10[1], src11[1], dx), dy);
+        break;
+      case 3:
+      for (int c = 0; c < bpp; c++)
+        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+                
+        }
+        rgba[3]=255;
+        break;
+      break;
+      case 4:
+      for (int c = 0; c < bpp; c++)
+        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+                
+        }
+      }
+      }
+      else
+      {
       uint8_t *src = (uint8_t *) buffer->data;
       src += v * buffer->stride + u * bpp;
       switch (bpp)
@@ -23923,132 +27203,450 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out
               { rgba[c] = src[c]; }
             break;
         }
+      }
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+}
+
+#if CTX_DITHER
+static inline int ctx_dither_mask_a (int x, int y, int c, int divisor)
+{
+  /* https://pippin.gimp.org/a_dither/ */
+  return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor;
+}
+
+inline static void
+ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+{
+  if (dither_red_blue == 0)
+    { return; }
+  for (int c = 0; c < 3; c ++)
+    {
+      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue);
+      rgba[c] = CTX_CLAMP (val, 0, 255);
+    }
+}
+
+inline static void
+ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+{
+  if (dither_red_blue == 0)
+    { return; }
+  for (int c = 0; c < 1; c ++)
+    {
+      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue);
+      rgba[c] = CTX_CLAMP (val, 0, 255);
+    }
+}
+#endif
+
+CTX_INLINE static void
+ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out)
+{
+    uint32_t val = *((uint32_t*)(in));
+    int a = val >> CTX_RGBA8_A_SHIFT;
+    if (a)
+    {
+    if (a ==255)
+    {
+      *((uint32_t*)(out)) = val;
+    } else
+    {
+      uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK;
+      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK;
+      *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
+    }
+    }
+    else
+    {
+      *((uint32_t*)(out)) = 0;
+    }
+}
+
+CTX_INLINE static void
+ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out)
+{
+  if (in[components-1])
+  {
+    if (in[components-1] != 255)
+    for (int c = 0; c < components-1; c++)
+      out[c] = (in[c] * 255) / in[components-1];
+    else
+    for (int c = 0; c < components-1; c++)
+      out[c] = in[c];
+    out[components-1] = in[components-1];
+  }
+  else
+  {
+  for (int c = 0; c < components; c++)
+    out[c] = 0;
+  }
+}
+
+CTX_INLINE static void
+ctx_float_associate_alpha (int components, float *rgba)
+{
+  float alpha = rgba[components-1];
+  for (int c = 0; c < components-1; c++)
+    rgba[c] *= alpha;
+}
+
+CTX_INLINE static void
+ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
+{
+  float ralpha = rgba[components-1];
+  if (ralpha != 0.0) ralpha = 1.0/ralpha;
+
+  for (int c = 0; c < components-1; c++)
+    dst[c] = (rgba[c] * ralpha);
+  dst[components-1] = rgba[components-1];
+}
+
+CTX_INLINE static void
+ctx_RGBAF_associate_alpha (float *rgba)
+{
+  ctx_float_associate_alpha (4, rgba);
+}
+
+CTX_INLINE static void
+ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
+{
+  ctx_float_deassociate_alpha (4, rgba, dst);
+}
+
+/**** rgb8 ***/
+
+static void
+ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out)
+{
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+    }
+  else
+    {
+      int bpp = 3;
+      rgba[3]=255;
+      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+          int dim = (1.0 / factor) / 2;
+          uint64_t sum[4]={0,0,0,0};
+          int count = 0;
+          for (int ou = - dim; ou < dim; ou++)
+          for (int ov = - dim; ov < dim; ov++)
+          {
+            uint8_t *src = (uint8_t *) buffer->data;
+            int o = (v+ov) * buffer->width + (u + ou);
+
+            if (o>=0 && o < buffer->width * buffer->height)
+            {
+              src += o * bpp;
+
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
+            }
+          }
+          if (count)
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c]/count;
+
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
+}
+
+static void
+ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out)
+{
+  uint8_t *rgba = (uint8_t *) out;
+
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+    }
+  else
+    {
+      int bpp = 3;
+      rgba[3]=255;
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      src00 += v * buffer->stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < buffer->width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < buffer->height)
+      {
+        src10 = src00 + buffer->stride;
+        src11 = src01 + buffer->stride;
+      }
+      float dx = (x-(int)(x)) * 255.9;
+      float dy = (y-(int)(y)) * 255.9;
+      for (int c = 0; c < bpp; c++)
+      {
+        rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                               ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+      }
+
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
+}
+
+static void
+ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                       float x,
+                                       float y,
+                                       void *out)
+{
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  x += 0.5;
+  y += 0.5;
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+    }
+  else
+    {
+      int bpp = 3;
+      {
+        uint8_t *src00 = (uint8_t *) buffer->data;
+        src00 += v * buffer->stride + u * bpp;
+        for (int c = 0; c < bpp; c++)
+          { rgba[c] = src00[c]; }
+      }
       if (rasterizer->swap_red_green)
       {
         uint8_t tmp = rgba[0];
         rgba[0] = rgba[2];
         rgba[2] = tmp;
       }
+      rgba[3]=255;
     }
-}
-
 #if CTX_DITHER
-static inline int ctx_dither_mask_a (int x, int y, int c, int divisor)
-{
-  /* https://pippin.gimp.org/a_dither/ */
-  return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor;
-}
-
-inline static void
-ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
-{
-  if (dither_red_blue == 0)
-    { return; }
-  for (int c = 0; c < 3; c ++)
-    {
-      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue);
-      rgba[c] = CTX_CLAMP (val, 0, 255);
-    }
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
+#endif
 }
 
-inline static void
-ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+static void
+ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer,
+                               float x,
+                               float y,
+                               void *out)
 {
-  if (dither_red_blue == 0)
-    { return; }
-  for (int c = 0; c < 1; c ++)
+  if (rasterizer->state->gstate.image_smoothing)
+  {
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor < 0.5)
     {
-      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue);
-      rgba[c] = CTX_CLAMP (val, 0, 255);
+      ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out);
     }
-}
-#endif
-
-CTX_INLINE static void
-ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out)
-{
-    uint32_t val = *((uint32_t*)(in));
-    int a = val >> CTX_RGBA8_A_SHIFT;
-    if (a)
-    {
-    if (a ==255)
-    {
-      *((uint32_t*)(out)) = val;
-    } else
+    else if (factor > 0.99 && factor < 1.01)
     {
-      uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK;
-      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK;
-      *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
-    }
+      // XXX missing translate test
+      ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out);
     }
     else
     {
-      *((uint32_t*)(out)) = 0;
+      ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out);
     }
-}
-
-CTX_INLINE static void
-ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out)
-{
-  if (in[components-1])
-  {
-    if (in[components-1] != 255)
-    for (int c = 0; c < components-1; c++)
-      out[c] = (in[c] * 255) / in[components-1];
-    else
-    for (int c = 0; c < components-1; c++)
-      out[c] = in[c];
-    out[components-1] = in[components-1];
   }
   else
   {
-  for (int c = 0; c < components; c++)
-    out[c] = 0;
+    ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out);
   }
+#if CTX_DITHER
+  uint8_t *rgba = out;
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
 }
 
-CTX_INLINE static void
-ctx_float_associate_alpha (int components, float *rgba)
-{
-  float alpha = rgba[components-1];
-  for (int c = 0; c < components-1; c++)
-    rgba[c] *= alpha;
-}
 
-CTX_INLINE static void
-ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
+/************** rgba8 */
+
+static void
+ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out)
 {
-  float ralpha = rgba[components-1];
-  if (ralpha != 0.0) ralpha = 1.0/ralpha;
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
 
-  for (int c = 0; c < components-1; c++)
-    dst[c] = (rgba[c] * ralpha);
-  dst[components-1] = rgba[components-1];
-}
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+    }
+  else
+    {
+      int bpp = 4;
+      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+          int dim = (1.0 / factor) / 2;
+          uint64_t sum[4]={0,0,0,0};
+          int count = 0;
+          for (int ou = - dim; ou < dim; ou++)
+          for (int ov = - dim; ov < dim; ov++)
+          {
+            uint8_t *src = (uint8_t *) buffer->data;
+            int o = (v+ov) * buffer->width + (u + ou);
 
-CTX_INLINE static void
-ctx_RGBAF_associate_alpha (float *rgba)
-{
-  ctx_float_associate_alpha (4, rgba);
+            if (o>=0 && o < buffer->width * buffer->height)
+            {
+              src += o * bpp;
+
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
+            }
+          }
+          if (count)
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c]/count;
+
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-CTX_INLINE static void
-ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
+static void
+ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out)
 {
-  ctx_float_deassociate_alpha (4, rgba, dst);
+  uint8_t *rgba = (uint8_t *) out;
+
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+    }
+  else
+    {
+      int bpp = 4;
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      src00 += v * buffer->stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < buffer->width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < buffer->height)
+      {
+        src10 = src00 + buffer->stride;
+        src11 = src01 + buffer->stride;
+      }
+      float dx = (x-(int)(x)) * 255.9;
+      float dy = (y-(int)(y)) * 255.9;
+      for (int c = 0; c < bpp; c++)
+      {
+        rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                               ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+      }
+
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
 static void
-ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
+ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                        float x,
+                                        float y,
+                                        void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
-  CtxBuffer *buffer = g->image.buffer;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
-  int u = x - g->image.x0;
-  int v = y - g->image.y0;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  x += 0.5;
+  y += 0.5;
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
   if ( u < 0 || v < 0 ||
        u >= buffer->width ||
        v >= buffer->height)
@@ -24058,19 +27656,54 @@ ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, float x, float y, voi
   else
     {
       int bpp = 4;
-      uint8_t *src = (uint8_t *) buffer->data;
-      src += v * buffer->stride + u * bpp;
-      for (int c = 0; c < 4; c++)
-        { rgba[c] = src[c]; }
+      {
+        uint8_t *src00 = (uint8_t *) buffer->data;
+        src00 += v * buffer->stride + u * bpp;
+        for (int c = 0; c < bpp; c++)
+          { rgba[c] = src00[c]; }
+      }
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+#if CTX_DITHER
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
+#endif
+}
 
-  if (rasterizer->swap_red_green)
+static void
+ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer,
+                                float x,
+                                float y,
+                                void *out)
+{
+  if (rasterizer->state->gstate.image_smoothing)
   {
-    uint8_t tmp = rgba[0];
-    rgba[0] = rgba[2];
-    rgba[2] = tmp;
-  }
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor < 0.5)
+    {
+      ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out);
+    }
+    else if (factor > 0.99 && factor < 1.01)
+    {
+      // XXX: also verify translate == 0 for this fast path to be valid
+      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out);
+    }
+    else
+    {
+      ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out);
     }
+  }
+  else
+  {
+    ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out);
+  }
 #if CTX_DITHER
+  uint8_t *rgba = out;
   ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
                       rasterizer->format->dither_green);
 #endif
@@ -24080,13 +27713,13 @@ static void
 ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
-  CtxBuffer *buffer = g->image.buffer;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
   ctx_assert (rasterizer);
   ctx_assert (g);
   ctx_assert (buffer);
-  int u = x - g->image.x0;
-  int v = y - g->image.y0;
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
   if ( u < 0 || v < 0 ||
        u >= buffer->width ||
        v >= buffer->height)
@@ -24104,55 +27737,19 @@ ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, voi
       else
         {
           for (int c = 0; c < 4; c++)
-            { rgba[c] = g->image.rgba[c]; }
+            { rgba[c] = 255;
+            }//g->texture.rgba[c];
+            //}
         }
     }
 }
 
-static void
-ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
-  CtxBuffer *buffer = g->image.buffer;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
-  int u = x - g->image.x0;
-  int v = y - g->image.y0;
-  if ( (u < 0) || (v < 0) ||
-       (u >= buffer->width-1) ||
-       (v >= buffer->height-1) )
-    {
-      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
-    }
-  else
-    {
-      int bpp = 3;
-      uint8_t *src = (uint8_t *) buffer->data;
-      src += v * buffer->stride + u * bpp;
-      for (int c = 0; c < 3; c++)
-        { rgba[c] = src[c]; }
-      rgba[3] = 255;
-  if (rasterizer->swap_red_green)
-  {
-    uint8_t tmp = rgba[0];
-    rgba[0] = rgba[2];
-    rgba[2] = tmp;
-  }
-    }
-#if CTX_DITHER
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
-}
-
 #if CTX_GRADIENTS
 static void
 ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
               g->radial_gradient.r0) * (g->radial_gradient.rdelta);
 #if CTX_GRADIENT_CACHE
@@ -24171,7 +27768,7 @@ static void
 ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
                 g->linear_gradient.length) -
               g->linear_gradient.start) * (g->linear_gradient.rdelta);
@@ -24193,7 +27790,7 @@ static void
 ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba);
   if (rasterizer->swap_red_green)
   {
@@ -24209,7 +27806,7 @@ static void
 ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
                 g->linear_gradient.length) -
               g->linear_gradient.start) * (g->linear_gradient.rdelta);
@@ -24220,7 +27817,7 @@ static void
 ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
         v = (v - g->radial_gradient.r0) * (g->radial_gradient.rdelta);
   ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba);
@@ -24232,7 +27829,7 @@ static void
 ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   ctx_color_get_rgba (rasterizer->state, &g->color, rgba);
 }
 
@@ -24241,7 +27838,7 @@ static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float
   float *outf = (float *) out;
   uint8_t rgba[4];
   CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source.image.buffer;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
   switch (buffer->format->bpp)
     {
       case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba); break;
@@ -24255,9 +27852,9 @@ static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float
 static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
     {
-      case CTX_SOURCE_IMAGE:           return ctx_fragment_image_RGBAF;
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_RGBAF;
       case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBAF;
 #if CTX_GRADIENTS
       case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBAF;
@@ -24271,15 +27868,48 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer)
 static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source.image.buffer;
-  switch (gstate->source.type)
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (gstate->source_fill.type)
     {
-      case CTX_SOURCE_IMAGE:
+      case CTX_SOURCE_TEXTURE:
         switch (buffer->format->bpp)
           {
             case 1:  return ctx_fragment_image_gray1_RGBA8;
-            case 24: return ctx_fragment_image_rgb8_RGBA8;
-            case 32: return ctx_fragment_image_rgba8_RGBA8;
+            case 24: 
+              {
+                if (gstate->image_smoothing)
+                {
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                  if (factor < 0.5)
+                    return ctx_fragment_image_rgb8_RGBA8_box;
+                  else if (factor > 0.99 && factor < 1.01)
+                    return ctx_fragment_image_rgb8_RGBA8_nearest;
+                  else
+                    return ctx_fragment_image_rgb8_RGBA8_bi;
+                }
+                else
+                {
+                  return ctx_fragment_image_rgb8_RGBA8_nearest;
+                }
+              }
+              break;
+            case 32:
+              {
+                if (gstate->image_smoothing)
+                {
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                  if (factor < 0.5)
+                    return ctx_fragment_image_rgba8_RGBA8_box;
+                  else if (factor > 0.99 && factor < 1.01)
+                    return ctx_fragment_image_rgba8_RGBA8_nearest;
+                  else
+                    return ctx_fragment_image_rgba8_RGBA8_bi;
+                }
+                else
+                {
+                  return ctx_fragment_image_rgba8_RGBA8_nearest;
+                }
+              }
             default: return ctx_fragment_image_RGBA8;
           }
       case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBA8;
@@ -24302,14 +27932,14 @@ ctx_init_uv (CtxRasterizer *rasterizer,
   float u1 = *u0 + count;
   float v1 = *v0;
 
-  ctx_matrix_apply_transform (&gstate->source.transform, u0, v0);
-  ctx_matrix_apply_transform (&gstate->source.transform, &u1, &v1);
+  ctx_matrix_apply_transform (&gstate->source_fill.transform, u0, v0);
+  ctx_matrix_apply_transform (&gstate->source_fill.transform, &u1, &v1);
 
   *ud = (u1-*u0) / (count);
   *vd = (v1-*v0) / (count);
 }
 
-#if 0
+#if 1
 static void
 ctx_u8_source_over_normal_opaque_color (int components, CTX_COMPOSITE_ARGUMENTS)
 {
@@ -24491,7 +28121,7 @@ ctx_porter_duff_factors(mode, foo, bar)\
   }\
 }
 
-#if 0
+#if 1
 static void
 ctx_u8_source_over_normal_color (int components,
                                  CtxRasterizer         *rasterizer,
@@ -24542,7 +28172,7 @@ ctx_u8_source_over_normal_color (int components,
 static void
 ctx_RGBA8_source_over_normal_linear_gradient (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float u0 = 0; float v0 = 0;
   float ud = 0; float vd = 0;
   ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
@@ -24760,7 +28390,7 @@ ctx_RGBA8_source_over_normal_linear_gradient (CTX_COMPOSITE_ARGUMENTS)
 static void
 ctx_RGBA8_source_over_normal_radial_gradient (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float u0 = 0; float v0 = 0;
   float ud = 0; float vd = 0;
   ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
@@ -26102,6 +29732,7 @@ CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop) (CTX_COMPOSITE_ARGUMENTS)
 {
 }
 
+
 static void
 ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
 {
@@ -26109,6 +29740,8 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
   int components = 4;
   rasterizer->fragment = ctx_rasterizer_get_fragment_RGBA8 (rasterizer);
   rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_generic);
+
+
 #if 1
   if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
   {
@@ -26118,14 +29751,14 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
 #endif
 #if CTX_INLINED_GRADIENTS
 #if CTX_GRADIENTS
-  if (gstate->source.type == CTX_SOURCE_LINEAR_GRADIENT &&
+  if (gstate->source_fill.type == CTX_SOURCE_LINEAR_GRADIENT &&
       gstate->blend_mode == CTX_BLEND_NORMAL &&
       gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
   {
      rasterizer->comp_op = ctx_RGBA8_source_over_normal_linear_gradient;
      return;
   }
-  if (gstate->source.type == CTX_SOURCE_RADIAL_GRADIENT &&
+  if (gstate->source_fill.type == CTX_SOURCE_RADIAL_GRADIENT &&
       gstate->blend_mode == CTX_BLEND_NORMAL &&
       gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
   {
@@ -26135,9 +29768,9 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
 #endif
 #endif
 
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
-      ctx_color_get_rgba8 (rasterizer->state, &gstate->source.color, rasterizer->color);
+      ctx_color_get_rgba8 (rasterizer->state, &gstate->source_fill.color, rasterizer->color);
       if (gstate->global_alpha_u8 != 255)
         rasterizer->color[components-1] = (rasterizer->color[components-1] * gstate->global_alpha_u8)/255;
       if (rasterizer->swap_red_green)
@@ -26181,7 +29814,7 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
 #if CTX_INLINED_NORMAL
     if (gstate->blend_mode == CTX_BLEND_NORMAL)
     {
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             return; // exhaustively handled above;
@@ -26193,10 +29826,10 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
             rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_radial_gradient_normal);
             break;
 #endif
-          case CTX_SOURCE_IMAGE:
+          case CTX_SOURCE_TEXTURE:
             {
-               CtxSource *g = &rasterizer->state->gstate.source;
-               switch (g->image.buffer->format->bpp)
+               CtxSource *g = &rasterizer->state->gstate.source_fill;
+               switch (g->texture.buffer->format->bpp)
                {
                  case 32:
                    rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_image_rgba8_normal);
@@ -26811,11 +30444,11 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   int components = 4;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
       rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
       rasterizer->fragment = NULL;
-      ctx_color_get_rgba (rasterizer->state, &gstate->source.color, (float*)rasterizer->color);
+      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
       if (gstate->global_alpha_u8 != 255)
         for (int c = 0; c < components; c ++)
           ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
@@ -26843,7 +30476,7 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer)
           rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
         }
         else
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
@@ -26870,7 +30503,7 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer)
             rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal;
             break;
 #endif
-          case CTX_SOURCE_IMAGE:
+          case CTX_SOURCE_TEXTURE:
             rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal;
             break;
           default:
@@ -26879,7 +30512,7 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer)
         }
         break;
       default:
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
@@ -26893,7 +30526,7 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer)
             rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient;
             break;
 #endif
-          case CTX_SOURCE_IMAGE:
+          case CTX_SOURCE_TEXTURE:
             rasterizer->comp_op = ctx_RGBAF_porter_duff_image;
             break;
           default:
@@ -26913,7 +30546,7 @@ static void
 ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
                 g->linear_gradient.length) -
               g->linear_gradient.start) * (g->linear_gradient.rdelta);
@@ -26926,7 +30559,7 @@ static void
 ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = 0.0f;
   if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f)
     {
@@ -26942,7 +30575,7 @@ ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y
 static void
 ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   ctx_color_get_graya (rasterizer->state, &g->color, (float*)out);
 }
 
@@ -26951,7 +30584,7 @@ static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float
   uint8_t rgba[4];
   float rgbaf[4];
   CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source.image.buffer;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
   switch (buffer->format->bpp)
     {
       case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba); break;
@@ -26967,9 +30600,9 @@ static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float
 static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
     {
-      case CTX_SOURCE_IMAGE:           return ctx_fragment_image_GRAYAF;
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYAF;
       case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYAF;
 #if CTX_GRADIENTS
       case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYAF;
@@ -27011,11 +30644,11 @@ ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   int components = 2;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
       rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
       rasterizer->fragment = NULL;
-      ctx_color_get_rgba (rasterizer->state, &gstate->source.color, (float*)rasterizer->color);
+      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
       if (gstate->global_alpha_u8 != 255)
         for (int c = 0; c < components; c ++)
           ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
@@ -27040,7 +30673,7 @@ ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
         else if (gstate->global_alpha_u8 == 0)
           rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
         else
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
@@ -27065,7 +30698,7 @@ ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
         }
         break;
       default:
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
@@ -27158,9 +30791,9 @@ ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *ou
   float *cmyka = (float*)out;
   float rgba[4];
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
     {
-      case CTX_SOURCE_IMAGE:
+      case CTX_SOURCE_TEXTURE:
         ctx_fragment_image_RGBAF (rasterizer, x, y, rgba);
         break;
       case CTX_SOURCE_COLOR:
@@ -27187,7 +30820,7 @@ ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *ou
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   float *cmyka = (float*)out;
-  ctx_color_get_cmyka (rasterizer->state, &gstate->source.color, cmyka);
+  ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka);
   // RGBW instead of CMYK
   for (int i = 0; i < 4; i ++)
     {
@@ -27198,7 +30831,7 @@ ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *ou
 static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
     {
       case CTX_SOURCE_COLOR:
         return ctx_fragment_color_CMYKAF;
@@ -27238,11 +30871,11 @@ ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   int components = 5;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
       rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
       rasterizer->fragment = NULL;
-      ctx_color_get_cmyka (rasterizer->state, &gstate->source.color, (float*)rasterizer->color);
+      ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
       if (gstate->global_alpha_u8 != 255)
         ((float*)rasterizer->color)[components-1] *= gstate->global_alpha_f;
     }
@@ -27267,7 +30900,7 @@ ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
         else if (gstate->global_alpha_u8 == 0)
           rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
         else
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
@@ -27292,7 +30925,7 @@ ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
         }
         break;
       default:
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
@@ -27750,12 +31383,9 @@ inline static void
 ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
   uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  for (int i = 0; i < count; i ++)
     {
-      pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      // for internal uses... using only green would work
-      pixel+=1;
-      rgba +=4;
+      pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4);
     }
 }
 #endif
@@ -27802,7 +31432,7 @@ CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8
 static void
 ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
                 g->linear_gradient.length) -
               g->linear_gradient.start) * (g->linear_gradient.rdelta);
@@ -27818,7 +31448,7 @@ static void
 ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
   uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
               g->radial_gradient.r0) * (g->radial_gradient.rdelta);
   ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
@@ -27833,7 +31463,7 @@ ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y,
 static void
 ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
               g->radial_gradient.r0) * (g->radial_gradient.rdelta);
   ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, (uint8_t*)out);
@@ -27847,7 +31477,7 @@ ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y
 static void
 ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  CtxSource *g = &rasterizer->state->gstate.source;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
   ctx_color_get_graya_u8 (rasterizer->state, &g->color, out);
 }
 
@@ -27855,7 +31485,7 @@ static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float
 {
   uint8_t rgba[4];
   CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source.image.buffer;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
   switch (buffer->format->bpp)
     {
       case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba); break;
@@ -27869,9 +31499,9 @@ static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float
 static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
     {
-      case CTX_SOURCE_IMAGE:           return ctx_fragment_image_GRAYA8;
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYA8;
 #if CTX_GRADIENTS
       case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYA8;
       case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8;
@@ -27902,13 +31532,13 @@ ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 }
 
 static void
-ctx_GRAYA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
+CTX_COMPOSITE_SUFFIX (ctx_GRAYA8_source_over_normal_color) (CTX_COMPOSITE_ARGUMENTS)
 {
   ctx_u8_source_over_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
 
 static void
-ctx_GRAYA8_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
+CTX_COMPOSITE_SUFFIX (ctx_GRAYA8_source_over_normal_opaque_color) (CTX_COMPOSITE_ARGUMENTS)
 {
   ctx_u8_source_over_normal_opaque_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
@@ -27920,10 +31550,10 @@ ctx_is_opaque_color (CtxRasterizer *rasterizer)
   CtxGState *gstate = &rasterizer->state->gstate;
   if (gstate->global_alpha_u8 != 255)
     return 0;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
   {
     uint8_t ga[2];
-    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source.color, ga);
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
     return ga[1] == 255;
   }
   return 0;
@@ -27934,11 +31564,11 @@ ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   int components = 2;
-  if (gstate->source.type == CTX_SOURCE_COLOR)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
-      rasterizer->comp_op = ctx_GRAYA8_porter_duff_color;
+      rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
       rasterizer->fragment = NULL;
-      ctx_color_get_rgba8 (rasterizer->state, &gstate->source.color, rasterizer->color);
+      ctx_color_get_rgba8 (rasterizer->state, &gstate->source_fill.color, rasterizer->color);
       if (gstate->global_alpha_u8 != 255)
         for (int c = 0; c < components; c ++)
           rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255;
@@ -27948,7 +31578,7 @@ ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
   else
   {
     rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer);
-    rasterizer->comp_op  = ctx_GRAYA8_porter_duff_generic;
+    rasterizer->comp_op  = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
   }
 
 #if CTX_INLINED_NORMAL
@@ -27965,7 +31595,7 @@ ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
         else if (gstate->global_alpha_u8 == 0)
           rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
         else
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
             if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
@@ -27973,31 +31603,31 @@ ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
               if (rasterizer->color[components-1] == 0)
                 rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
               else if (rasterizer->color[components-1] == 255)
-                rasterizer->comp_op = ctx_GRAYA8_source_over_normal_opaque_color;
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_opaque_color);
               else
-                rasterizer->comp_op = ctx_GRAYA8_source_over_normal_color;
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_color);
               rasterizer->fragment = NULL;
             }
             else
             {
-              rasterizer->comp_op = ctx_GRAYA8_porter_duff_color_normal;
+              rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color_normal);
               rasterizer->fragment = NULL;
             }
             break;
           default:
-            rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal;
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic_normal);
             break;
         }
         break;
       default:
-        switch (gstate->source.type)
+        switch (gstate->source_fill.type)
         {
           case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_GRAYA8_porter_duff_color;
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
             rasterizer->fragment = NULL;
             break;
           default:
-            rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic;
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
             break;
         }
         break;
@@ -28314,6 +31944,13 @@ CtxPixelFormatInfo CTX_COMPOSITE_SUFFIX(ctx_pixel_formats)[]=
 void
 CTX_COMPOSITE_SUFFIX(ctx_compositor_setup) (CtxRasterizer *rasterizer)
 {
+  if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_TEXTURE)
+  {
+    if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed)
+      _ctx_texture_prepare_color_management (rasterizer,
+        rasterizer->state->gstate.source_fill.texture.buffer);
+  }
+
   if (rasterizer->format->setup)
   {
     // event if _default is used we get to work
@@ -28322,7 +31959,7 @@ CTX_COMPOSITE_SUFFIX(ctx_compositor_setup) (CtxRasterizer *rasterizer)
 #if CTX_GRADIENTS
 #if CTX_GRADIENT_CACHE
   CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source.type)
+  switch (gstate->source_fill.type)
   {
     case CTX_SOURCE_LINEAR_GRADIENT:
     case CTX_SOURCE_RADIAL_GRADIENT:
diff --git a/gegl/ctx/meson.build b/gegl/ctx/meson.build
index f2e9a6d86..bd9175039 100644
--- a/gegl/ctx/meson.build
+++ b/gegl/ctx/meson.build
@@ -3,3 +3,6 @@ gegl_sources += files(
 )
 
 gegl_headers += files('ctx.h')
+
+gegl_cflags += '-DNO_LIBCURL'
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]