[gegl] ctx: update from upstream, with associated alpha produced by ctx_fragment_color_RGBAF



commit 80cc2de9a77476a511e6ac360bbed7475cc462d3
Author: Øyvind Kolås <pippin gimp org>
Date:   Sat Sep 4 22:14:59 2021 +0200

    ctx: update from upstream, with associated alpha produced by ctx_fragment_color_RGBAF

 gegl/ctx/ctx.h                        | 53352 ++++++++++++++++++++------------
 operations/common/vector-fill.c       |     2 +-
 tests/compositions/reference/gegl.png |   Bin 110907 -> 110474 bytes
 3 files changed, 33526 insertions(+), 19828 deletions(-)
---
diff --git a/gegl/ctx/ctx.h b/gegl/ctx/ctx.h
index 24ca7e99b..975e241e2 100644
--- a/gegl/ctx/ctx.h
+++ b/gegl/ctx/ctx.h
@@ -1,3 +1,4 @@
+/* ctx git commit: 4d729cd */
 /* 
  * ctx.h is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -74,6 +75,8 @@ enum _CtxPixelFormat
   CTX_FORMAT_CMYK8,  //15
   CTX_FORMAT_CMYKA8, //16 
   CTX_FORMAT_CMYKAF, //17
+  CTX_FORMAT_YUV420, //18
+  CTX_FORMAT_RGBA8_SEPARATE_ALPHA, // 19
 };
 typedef enum   _CtxPixelFormat CtxPixelFormat;
 
@@ -295,6 +298,14 @@ void ctx_define_texture (Ctx *ctx,
                          void       *data,
                          char       *ret_eid);
 
+void
+ctx_source_transform (Ctx *ctx, float a, float b,  // hscale, hskew
+                      float c, float d,  // vskew,  vscale
+                      float e, float f);  // htran,  vtran
+typedef struct _CtxMatrix     CtxMatrix;
+void
+ctx_source_transform_matrix (Ctx *ctx, CtxMatrix *matrix);
+
 void
 ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
                     CtxPixelFormat format, int dst_stride,
@@ -428,25 +439,43 @@ int      ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
 
 typedef enum
 {
-  CTX_FILL_RULE_EVEN_ODD,
-  CTX_FILL_RULE_WINDING
+  CTX_FILL_RULE_WINDING,
+  CTX_FILL_RULE_EVEN_ODD
 } CtxFillRule;
 
 typedef enum
 {
-  CTX_COMPOSITE_SOURCE_OVER,
-  CTX_COMPOSITE_COPY,
-  CTX_COMPOSITE_SOURCE_IN,
-  CTX_COMPOSITE_SOURCE_OUT,
-  CTX_COMPOSITE_SOURCE_ATOP,
-  CTX_COMPOSITE_CLEAR,
-
-  CTX_COMPOSITE_DESTINATION_OVER,
-  CTX_COMPOSITE_DESTINATION,
-  CTX_COMPOSITE_DESTINATION_IN,
-  CTX_COMPOSITE_DESTINATION_OUT,
-  CTX_COMPOSITE_DESTINATION_ATOP,
-  CTX_COMPOSITE_XOR,
+#if 0
+  CTX_COMPOSITE_SOURCE_OVER      = 0,
+  CTX_COMPOSITE_COPY             = 32,
+  CTX_COMPOSITE_SOURCE_IN        = 64,
+  CTX_COMPOSITE_SOURCE_OUT       = 96,
+  CTX_COMPOSITE_SOURCE_ATOP      = 128,
+  CTX_COMPOSITE_CLEAR            = 160,
+
+  CTX_COMPOSITE_DESTINATION_OVER = 192,
+  CTX_COMPOSITE_DESTINATION      = 224,
+  CTX_COMPOSITE_DESTINATION_IN   = 256,
+  CTX_COMPOSITE_DESTINATION_OUT  = 288,
+  CTX_COMPOSITE_DESTINATION_ATOP = 320,
+  CTX_COMPOSITE_XOR              = 352,
+
+  CTX_COMPOSITE_ALL              = (32+64+128+256)
+#else
+  CTX_COMPOSITE_SOURCE_OVER      =0,
+  CTX_COMPOSITE_COPY             ,
+  CTX_COMPOSITE_SOURCE_IN        ,
+  CTX_COMPOSITE_SOURCE_OUT       ,
+  CTX_COMPOSITE_SOURCE_ATOP      ,
+  CTX_COMPOSITE_CLEAR            ,
+
+  CTX_COMPOSITE_DESTINATION_OVER ,
+  CTX_COMPOSITE_DESTINATION      ,
+  CTX_COMPOSITE_DESTINATION_IN   ,
+  CTX_COMPOSITE_DESTINATION_OUT  ,
+  CTX_COMPOSITE_DESTINATION_ATOP ,
+  CTX_COMPOSITE_XOR              ,
+#endif
 } CtxCompositingMode;
 
 typedef enum
@@ -918,7 +947,6 @@ typedef enum
                          //    data can be a string with a name,
                          //    icc data or perhaps our own serialization
                          //    of profile data
-  CTX_EDGE_FLIPPED     = '`', // x0 y0 x1 y1 - s16
   CTX_REL_ARC_TO       = 'a', // x1 y1 x2 y2 radius
   CTX_CLIP             = 'b',
   CTX_REL_CURVE_TO     = 'c', // cx1 cy1 cx2 cy2 x y
@@ -948,13 +976,16 @@ typedef enum
   CTX_CLOSE_PATH       = 'z', //
   CTX_START_GROUP      = '{',
   CTX_END_GROUP        = '}',
-  CTX_EDGE             = ',',
+  CTX_SOURCE_TRANSFORM = '`',
+
+  CTX_EDGE             = '&',                        // will not occur in commandstream
+  CTX_EDGE_FLIPPED     = '^', // x0 y0 x1 y1 - s16   // thus these use reserved entries as code
 
   /* though expressed as two chars in serialization we have
    * dedicated byte commands for the setters to keep the dispatch
    * simpler. There is no need for these to be human readable thus we go >128
    *
-   * unused:        !&<=>?:.=/\
+   * unused:        !&<=>?:.=/\`,
    * reserved:      '"&   #  %^@
    */
 
@@ -1613,11 +1644,11 @@ typedef enum
 
 enum _CtxAntialias
 {
-  CTX_ANTIALIAS_DEFAULT,
+  CTX_ANTIALIAS_DEFAULT, // fast - suitable for realtime UI
   CTX_ANTIALIAS_NONE, // non-antialiased
-  CTX_ANTIALIAS_FAST, // aa 3
-  CTX_ANTIALIAS_GOOD, // aa 5
-  CTX_ANTIALIAS_BEST  // aa 17
+  CTX_ANTIALIAS_FAST, // aa 3    // deprected or is default equal to this now?
+  CTX_ANTIALIAS_GOOD, // aa 5    // this should perhaps still be 5?
+  CTX_ANTIALIAS_BEST  // aa 17   // accurate-suitable for saved assets
 };
 typedef enum _CtxAntialias CtxAntialias;
 
@@ -1709,7 +1740,7 @@ ctx_parser_set_size (CtxParser *parser,
                      float      cell_width,
                      float      cell_height);
 
-void ctx_parser_feed_byte (CtxParser *parser, int byte);
+void ctx_parser_feed_bytes (CtxParser *parser, const char *data, int count);
 
 int
 ctx_get_contents (const char     *path,
@@ -1727,9 +1758,63 @@ int
 ctx_base642bin (const char    *ascii,
                 int           *length,
                 unsigned char *bin);
+
+
+struct
+  _CtxMatrix
+{
+  float m[3][2];
+};
+
+void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix);
+void ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y);
+void ctx_matrix_invert (CtxMatrix *m);
+void ctx_matrix_identity (CtxMatrix *matrix);
+void ctx_matrix_scale (CtxMatrix *matrix, float x, float y);
+void ctx_matrix_rotate (CtxMatrix *matrix, float angle);
+void ctx_matrix_multiply (CtxMatrix       *result,
+                          const CtxMatrix *t,
+                          const CtxMatrix *s);
+
+
+
+
 float ctx_term_get_cell_width (Ctx *ctx);
 float ctx_term_get_cell_height (Ctx *ctx);
 
+
+
+
+
+#if 1 // CTX_VT
+
+typedef struct _VT VT;
+void vt_feed_keystring  (VT *vt, CtxEvent *event, const char *str);
+void vt_paste           (VT *vt, const char *str);
+char *vt_get_selection  (VT *vt);
+long vt_rev             (VT *vt);
+int  vt_has_blink       (VT *vt);
+
+int  ctx_clients_need_redraw   (Ctx *ctx);
+void ctx_clients_handle_events (Ctx *ctx);
+
+
+typedef struct _CtxBuffer CtxBuffer;
+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);
+
+
+
+
+
+
+
+#endif
+
+
 #ifndef CTX_CODEC_CHAR
 //#define CTX_CODEC_CHAR '\035'
 //#define CTX_CODEC_CHAR 'a'
@@ -1747,6 +1832,13 @@ float ctx_term_get_cell_height (Ctx *ctx);
 #endif
 #ifndef __CTX_H__
 #define __CTX_H__
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600
+#endif
+
 #ifndef CTX_STRING_H
 #define CTX_STRING_H
 
@@ -1762,6 +1854,7 @@ struct _CtxString
 
 CtxString   *ctx_string_new_with_size  (const char *initial, int initial_size);
 CtxString   *ctx_string_new            (const char *initial);
+char       *ctx_string_dissolve       (CtxString *string);
 void        ctx_string_free           (CtxString *string, int freealloc);
 const char *ctx_string_get            (CtxString *string);
 uint32_t    ctx_string_get_unichar    (CtxString *string, int pos);
@@ -4171,9 +4264,12 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT3    (13424/CTX_SUBDIV/15)
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT5    (20425/CTX_SUBDIV/15)
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT15   (50425/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT3          (65536/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA  (65536/CTX_SUBDIV/15)
+//#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA (120536/CTX_SUBDIV/15)
+//#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA (105000/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT5         (140425/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT15        (260425/CTX_SUBDIV/15)
 
 /* subpixel-aa coordinates used in BITPACKing of drawlist
  *
@@ -4181,7 +4277,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
  */
 #ifndef CTX_SUBDIV
 #define CTX_SUBDIV   8  //  max framebufer width 4095
-//#define CTX_SUBDIV    10  //  max framebufer width 3250
+//#define CTX_SUBDIV  10  //  max framebufer width 3250
 //#define CTX_SUBDIV  16  //  max framebufer width 2047
 //#define CTX_SUBDIV  24  //  max framebufer width 1350
 //#define CTX_SUBDIV  32  //  max framebufer width 1023
@@ -4217,11 +4313,11 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 /* size (in pixels, w*h) that we cache rasterization for
  */
 #ifndef CTX_SHAPE_CACHE_DIM
-#define CTX_SHAPE_CACHE_DIM      (16*16)
+#define CTX_SHAPE_CACHE_DIM      (16*8)
 #endif
 
 #ifndef CTX_SHAPE_CACHE_MAX_DIM
-#define CTX_SHAPE_CACHE_MAX_DIM  32
+#define CTX_SHAPE_CACHE_MAX_DIM  20
 #endif
 
 /* maximum number of entries in shape cache
@@ -4276,12 +4372,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_GRADIENTS             1
 #endif
 
-/* some optinal micro-optimizations that are known to increase code size
- */
-#ifndef CTX_SORTING_NETWORKS
-#define CTX_SORTING_NETWORKS         1
-#endif
-
 #ifndef CTX_ALIGNED_STRUCTS
 #define CTX_ALIGNED_STRUCTS       1
 #endif
@@ -4291,7 +4381,15 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_FONTS_FROM_FILE
-#define CTX_FONTS_FROM_FILE 1
+#define CTX_FONTS_FROM_FILE  0
+#endif
+
+#ifndef CTX_GET_CONTENTS
+#if CTX_FONTS_FROM_FILE
+#define CTX_GET_CONTENTS    1
+#else
+#define CTX_GET_CONTENTS    0
+#endif
 #endif
 
 #ifndef CTX_FORMATTER
@@ -4310,6 +4408,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_XML             1
 #endif
 
+#ifndef CTX_VT
+#define CTX_VT              0
+#endif
+
 /* when ctx_math is defined, which it is by default, we use ctx' own
  * implementations of math functions, instead of relying on math.h
  * the possible inlining gives us a slight speed-gain, and on
@@ -4369,7 +4471,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 /* whether we dither or not for gradients
  */
 #ifndef CTX_DITHER
-#define CTX_DITHER 1
+#define CTX_DITHER 0
 #endif
 
 /*  only source-over clear and copy will work, the API still
@@ -4387,7 +4489,8 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_FORCE_INLINES               1
 #endif
 
-/* create one-off inlined inner loop for normal blend mode
+/* create one-off inlined inner loop for normal blend mode (for floating point,
+ * for RGBA8 manual loops overrrides
  */
 #ifndef CTX_INLINED_NORMAL     
 #define CTX_INLINED_NORMAL      1
@@ -4401,24 +4504,11 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_BRAILLE_TEXT        0
 #endif
 
-/* including immintrin.h triggers building of AVX2 code paths, if - like
- * sometimes when including SDL one does want it at all do a
- * #define CTX_AVX2 0  before including ctx.h for implementation.
- */
-#ifndef CTX_AVX2
-#ifdef _IMMINTRIN_H_INCLUDED
-#define CTX_AVX2         1
-#else
-#define CTX_AVX2         0
-#endif
-#endif
-
-/* Build code paths for grayscale rasterization, normally this is handled
- * by the RGBA8 codepaths; on microcontrollers with eink this might be
- * a better option.
+/* Build code paths for grayscale rasterization, this makes clipping
+ * faster.
  */
 #ifndef CTX_NATIVE_GRAYA8
-#define CTX_NATIVE_GRAYA8       0
+#define CTX_NATIVE_GRAYA8       1
 #endif
 
 /* enable CMYK rasterization targets
@@ -4443,7 +4533,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_ENABLE_FLOAT
-#define CTX_ENABLE_FLOAT 0
+#define CTX_ENABLE_FLOAT        0
 #endif
 
 /* by default ctx includes all pixel formats, on microcontrollers
@@ -4452,6 +4542,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
  * manually add CTX_ENABLE_ flags for each of them.
  */
 #if CTX_LIMIT_FORMATS
+#if CTX_NATIVE_GRAYA8
+#define CTX_ENABLE_GRAYA8               1
+#define CTX_ENABLE_GRAY8                1
+#endif
 #else
 
 #define CTX_ENABLE_GRAY1                1
@@ -4473,6 +4567,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #undef CTX_ENABLE_FLOAT
 #endif
 #define CTX_ENABLE_FLOAT                1
+#define CTX_ENABLE_YUV420               1
 
 #if CTX_ENABLE_CMYK
 #define CTX_ENABLE_CMYK8                1
@@ -4523,6 +4618,9 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_BABL 0
 #endif
 
+#ifndef CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
+#define CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 1
+#endif
 
 /* force add format if we have shape cache */
 #if CTX_SHAPE_CACHE
@@ -4616,7 +4714,11 @@ 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  120
+#define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS  400
+#endif
+
+#ifndef CTX_MAX_FRAMEBUFFER_WIDTH
+#define CTX_MAX_FRAMEBUFFER_WIDTH 2560
 #endif
 
 #ifndef CTX_MAX_FONTS
@@ -4658,11 +4760,9 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 
 
 #define CTX_RASTERIZER_EDGE_MULTIPLIER  1024
-
-#ifndef CTX_COMPOSITE_SUFFIX
-#define CTX_COMPOSITE_SUFFIX(symbol)     symbol##_default
-#endif
-
+                                        // increasing this to 2048
+                                        // removes artifacts in top half of res-diagram -
+                                        // but reduces maximum available buffer width
 #ifndef CTX_IMPLEMENTATION
 #define CTX_IMPLEMENTATION 0
 #else
@@ -4716,7 +4816,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_AUDIO
-#define CTX_AUDIO 1
+#define CTX_AUDIO 0
 #endif
 
 #ifndef CTX_TILED
@@ -4727,13 +4827,17 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 #endif
 
+#ifndef CTX_THREADS
 #if CTX_FB
 #define CTX_THREADS 1
 #else
 #define CTX_THREADS 0
 #endif
+#endif
 
-
+#if CTX_THREADS
+#include <threads.h>
+#endif
  /* Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
  */
 
@@ -4755,10 +4859,10 @@ int ctx_a85len (const char *src, int count);
 
 
 #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; }
+static inline int   ctx_mini (int a, int b)     { return (a < b) * a + (a >= b) * b; }
+static inline float ctx_minf (float a, float b) { return (a < b) * a + (a >= b) * b; }
+static inline int   ctx_maxi (int a, int b)     { return (a > b) * a + (a <= b) * b; }
+static inline float ctx_maxf (float a, float b) { return (a > b) * a + (a <= b) * b; }
 
 
 typedef enum CtxOutputmode
@@ -4811,6 +4915,39 @@ ctx_invsqrtf (float x)
   return x;
 }
 
+static inline float
+ctx_invsqrtf_fast (float x)
+{
+  void *foo = &x;
+//float xhalf = 0.5f * x;
+  int i=* (int *) foo;
+  void *bar = &i;
+  i = 0x5f3759df - (i >> 1);
+  x = * (float *) bar;
+//x *= (1.5f - xhalf * x * x);
+  return x;
+}
+
+CTX_INLINE static float ctx_sqrtf (float a)
+{
+  return 1.0f/ctx_invsqrtf (a);
+}
+
+CTX_INLINE static float ctx_sqrtf_fast (float a)
+{
+  return 1.0f/ctx_invsqrtf_fast (a);
+}
+
+CTX_INLINE static float ctx_hypotf (float a, float b)
+{
+  return ctx_sqrtf (ctx_pow2 (a)+ctx_pow2 (b) );
+}
+
+CTX_INLINE static float ctx_hypotf_fast (float a, float b)
+{
+  return ctx_sqrtf_fast (ctx_pow2 (a)+ctx_pow2 (b) );
+}
+
 CTX_INLINE static float
 ctx_sinf (float x)
 {
@@ -4892,15 +5029,6 @@ static inline float ctx_atan2f (float y, float x)
   return atan;
 }
 
-CTX_INLINE static float ctx_sqrtf (float a)
-{
-  return 1.0f/ctx_invsqrtf (a);
-}
-
-CTX_INLINE static float ctx_hypotf (float a, float b)
-{
-  return ctx_sqrtf (ctx_pow2 (a)+ctx_pow2 (b) );
-}
 
 static inline float ctx_atanf (float a)
 {
@@ -4935,7 +5063,7 @@ static inline float
 ctx_expf (float x)
 {
   union { uint32_t i; float f; } v =
-    { (1 << 23) * (x + 183.1395965) };
+    {  (uint32_t)( (1 << 23) * (x + 183.1395965f)) };
   return v.f;
 }
 
@@ -4965,14 +5093,11 @@ static inline float _ctx_parse_float (const char *str, char **endptr)
 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;
 
-typedef struct _CtxMatrix     CtxMatrix;
-struct
-  _CtxMatrix
-{
-  float m[3][2];
-};
+void
+ctx_matrix_translate (CtxMatrix *matrix, float x, float y);
+
+
 void ctx_get_matrix (Ctx *ctx, CtxMatrix *matrix);
 
 int ctx_color (Ctx *ctx, const char *string);
@@ -4992,17 +5117,6 @@ int ctx_utf8_len (const unsigned char first_byte);
 void ctx_user_to_device          (Ctx *ctx, float *x, float *y);
 void ctx_user_to_device_distance (Ctx *ctx, float *x, float *y);
 const char *ctx_utf8_skip (const char *s, int utf8_length);
-void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix);
-void ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y);
-void ctx_matrix_invert (CtxMatrix *m);
-void ctx_matrix_identity (CtxMatrix *matrix);
-void ctx_matrix_scale (CtxMatrix *matrix, float x, float y);
-void ctx_matrix_rotate (CtxMatrix *matrix, float angle);
-void ctx_matrix_multiply (CtxMatrix       *result,
-                          const CtxMatrix *t,
-                          const CtxMatrix *s);
-void
-ctx_matrix_translate (CtxMatrix *matrix, float x, float y);
 int ctx_is_set_now (Ctx *ctx, uint64_t hash);
 void ctx_set_size (Ctx *ctx, int width, int height);
 
@@ -5014,14 +5128,17 @@ static inline float ctx_matrix_get_scale (CtxMatrix *matrix)
                          ctx_fabsf (matrix->m[1][1]) ) );
 }
 
-#if CTX_FONTS_FROM_FILE
-int   ctx_load_font_ttf_file (const char *name, const char *path);
+#if CTX_GET_CONTENTS
 int
 _ctx_file_get_contents (const char     *path,
                         unsigned char **contents,
                         long           *length);
 #endif
 
+#if CTX_FONTS_FROM_FILE
+int   ctx_load_font_ttf_file (const char *name, const char *path);
+#endif
+
 #endif
 #ifndef __CTX_CONSTANTS
 #define __CTX_CONSTANTS
@@ -5159,6 +5276,8 @@ _ctx_file_get_contents (const char     *path,
 #define CTX_newPath    TOKENHASH(23510545975776)
 #define CTX_new_state  TOKENHASH(16912954280416)
 #define CTX_none       TOKENHASH(492640)
+#define CTX_nonzero     TOKENHASH(37865948256)
+#define CTX_non_zero    TOKENHASH(1211709359200)
 #define CTX_normal     TOKENHASH(946840672)
 #define CTX_quad_to    TOKENHASH(37986115046)
 #define CTX_quadTo     TOKENHASH(1233857682918)
@@ -5214,6 +5333,7 @@ _ctx_file_get_contents (const char     *path,
 #define CTX_sourceOut  TOKENHASH(7371294932695718)
 #define CTX_source_out         TOKENHASH(3851660580666474)
 #define CTX_sourceOver         TOKENHASH(7584784067385004)
+#define CTX_sourceTransform TOKENHASH(7515321363744130)
 #define CTX_source_over        TOKENHASH(8690648756484770)
 #define CTX_square     TOKENHASH(511950058)
 #define CTX_start      TOKENHASH(47455658)
@@ -5508,6 +5628,7 @@ struct _CtxGradient
 struct _CtxSource
 {
   int type;
+  CtxMatrix  set_transform;
   CtxMatrix  transform;
   union
   {
@@ -5516,8 +5637,6 @@ struct _CtxSource
     {
       uint8_t rgba[4]; // shares data with set color
       uint8_t pad;
-      float x0;
-      float y0;
       CtxBuffer *buffer;
     } texture;
     struct
@@ -5869,7 +5988,7 @@ struct _Ctx
 };
 
 
-void ctx_process (Ctx *ctx, CtxEntry *entry);
+static void ctx_process (Ctx *ctx, CtxEntry *entry);
 CtxBuffer *ctx_buffer_new (int width, int height,
                            CtxPixelFormat pixel_format);
 void ctx_buffer_free (CtxBuffer *buffer);
@@ -5877,10 +5996,10 @@ 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);
+static inline void ctx_interpret_style         (CtxState *state, CtxEntry *entry, void *data);
+static inline void ctx_interpret_transforms    (CtxState *state, CtxEntry *entry, void *data);
+static inline void ctx_interpret_pos           (CtxState *state, CtxEntry *entry, void *data);
+static inline void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data);
 
 struct _CtxInternalFsEntry
 {
@@ -5913,14 +6032,14 @@ struct _CtxPixelFormatInfo
 };
 
 
-static void
+static inline void
 _ctx_user_to_device (CtxState *state, float *x, float *y);
 static void
 _ctx_user_to_device_distance (CtxState *state, float *x, float *y);
 static void ctx_state_init (CtxState *state);
-void
+static inline void
 ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data);
-void
+static inline void
 ctx_drawlist_deinit (CtxDrawlist *drawlist);
 
 CtxPixelFormatInfo *
@@ -5940,17 +6059,6 @@ ctx_utf8_to_unichar (const char *input);
 
 typedef struct _CtxHasher CtxHasher;
 
-typedef struct CtxEdge
-{
-#if CTX_ALIGNED_STRUCTS==1
-  uint32_t index;
-#else
-  uint16_t index;
-#endif
-  int32_t  val;     /* the center-line intersection      */
-  int32_t  delta;
-} CtxEdge;
-
 typedef void (*CtxFragment) (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy);
 
 #define CTX_MAX_GAUSSIAN_KERNEL_DIM    512
@@ -5976,6 +6084,13 @@ struct _CtxShapeCache
 
 typedef struct _CtxShapeCache CtxShapeCache;
 
+typedef enum {
+   CTX_COV_PATH_FALLBACK =0,
+   CTX_COV_PATH_OVER,
+   CTX_COV_PATH_COPY,
+   CTX_COV_PATH_COPY_FRAGMENT,
+   CTX_COV_PATH_OVER_FRAGMENT
+} CtxCovPath;
 
 struct _CtxRasterizer
 {
@@ -5986,51 +6101,45 @@ struct _CtxRasterizer
      would be yet another refinement on top.
    */
 
-#if CTX_ENABLE_SHADOW_BLUR
-  float      kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM];
-#endif
 
-  int        aa;          // level of vertical aa
-  int        active_edges;
-  int        active_edges2;
-  int        pending_edges;   // this-scanline
-  int        ending_edges;    // count of edges ending this scanline
-  int        edge_pos;         // where we're at in iterating all edges
-  CtxEdge    edges[CTX_MAX_EDGES];
+#define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * 
__restrict__ src, int x0, uint8_t * __restrict__ coverage, int count
+  void (*comp_op)(CTX_COMPOSITE_ARGUMENTS);
+  CtxFragment fragment;
+  Ctx       *ctx;
+  CtxState  *state;
+  void      *buf;
+  int fast_aa;
+  CtxCovPath  comp;
+  float      x;  // < redundant? use state instead?
+  float      y;
 
-  int        scanline;
+  unsigned int aa;          // level of vertical aa
+  int prev_active_edges;
+  int active_edges;
+  int pending_edges;
+  int ending_edges;
+  int edge_pos;         // where we're at in iterating all edges
+  unsigned int needs_aa3; // count of how many edges implies antialiasing
+  unsigned int needs_aa5; // count of how many edges implies antialiasing
+  unsigned int needs_aa15; // count of how many edges implies antialiasing
+  int        horizontal_edges;
+
+  int scanline;
   int        scan_min;
   int        scan_max;
   int        col_min;
   int        col_max;
 
-  CtxDrawlist edge_list;
 
-  CtxState  *state;
-  Ctx       *ctx;
-  Ctx       *texture_source; /* normally same as ctx */
-
-  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;
+  int        inner_x;
+  int        inner_y;
 
   float      first_x;
   float      first_y;
-  int8_t     needs_aa3; // count of how many edges implies antialiasing
-  int8_t     needs_aa5; // count of how many edges implies antialiasing
-  int8_t     needs_aa15; // count of how many edges implies antialiasing
+  int        uses_transforms;
   int        has_shape:2;
   int        has_prev:2;
   int        preserve:1;
-  int        uses_transforms:1;
 
   int16_t    blit_x;
   int16_t    blit_y;
@@ -6039,6 +6148,14 @@ struct _CtxRasterizer
   int16_t    blit_stride;
 
   CtxPixelFormatInfo *format;
+  Ctx       *texture_source; /* normally same as ctx */
+
+  int edges[CTX_MAX_EDGES]; // integer position in edge array
+  CtxDrawlist edge_list;
+
+
+
+
 
 #if CTX_ENABLE_SHADOW_BLUR
   int in_shadow;
@@ -6047,18 +6164,23 @@ struct _CtxRasterizer
   int shadow_x;
   int shadow_y;
 
-  CtxFragment         fragment;
   int swap_red_green;
   uint8_t             color[4*5];
 
-#define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * 
__restrict__ src, int x0, uint8_t * __restrict__ coverage, int count
+  int clip_rectangle;
 
-  void (*comp_op)(CTX_COMPOSITE_ARGUMENTS);
 #if CTX_ENABLE_CLIP
   CtxBuffer *clip_buffer;
 #endif
 
-  int clip_rectangle;
+
+#if CTX_COMPOSITING_GROUPS
+  void      *saved_buf; // when group redirected
+  CtxBuffer *group[CTX_GROUP_MAX];
+#endif
+#if CTX_ENABLE_SHADOW_BLUR
+  float      kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM];
+#endif
 
 #if CTX_SHAPE_CACHE
   CtxShapeCache shape_cache;
@@ -6211,11 +6333,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, uint64_t key, float value);
+static 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, uint64_t hash);
+static float ctx_state_get (CtxState *state, uint64_t hash);
 
 #if CTX_RASTERIZER
 
@@ -6288,12 +6410,6 @@ ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
                           uint8_t b,
                           uint8_t a);
 static void
-ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
-                          float x,
-                          float y,
-                          float width,
-                          float height);
-static void
 ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius);
 
 #endif
@@ -6324,6 +6440,70 @@ CTX_INLINE static uint8_t ctx_lerp_u8 (uint8_t v0, uint8_t v1, uint8_t dx)
 #endif
 }
 
+CTX_INLINE static uint32_t ctx_lerp_RGBA8 (const uint32_t v0, const uint32_t v1, const uint8_t dx)
+{
+#if 0
+  char bv0[4];
+  char bv1[4];
+  char res[4];
+  memcpy (&bv0[0], &v0, 4);
+  memcpy (&bv1[0], &v1, 4);
+  for (int c = 0; c < 4; c++)
+    res [c] = ctx_lerp_u8 (bv0[c], bv1[c], dx);
+  return ((uint32_t*)(&res[0]))[0];
+#else
+  const uint32_t cov = dx;
+  const uint32_t si_ga = (v1 & 0xff00ff00);
+  const uint32_t si_rb = v1 & 0x00ff00ff;
+  const uint32_t di_rb = v0 & 0x00ff00ff;
+  const uint32_t d_rb = si_rb - di_rb;
+  const uint32_t di_ga = v0 & 0xff00ff00;
+  const uint32_t d_ga = (si_ga >>8) - (di_ga>>8);
+  return
+     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) |
+     (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
+
+#endif
+}
+
+CTX_INLINE static void ctx_lerp_RGBA8_split (const uint32_t v0, const uint32_t v1, const uint8_t dx,
+                                             uint32_t *dest_ga, uint32_t *dest_rb)
+{
+  const uint32_t cov = dx;
+  const uint32_t si_ga = (v1 & 0xff00ff00);
+  const uint32_t si_rb = v1 & 0x00ff00ff;
+  const uint32_t di_rb = v0 & 0x00ff00ff;
+  const uint32_t di_ga = v0 & 0xff00ff00;
+  const uint32_t d_rb = si_rb - di_rb;
+  const uint32_t d_ga = (si_ga >>8) - (di_ga >> 8);
+  *dest_rb = (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff));
+  *dest_ga = (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
+}
+
+CTX_INLINE static uint32_t ctx_lerp_RGBA8_merge (uint32_t di_ga, uint32_t di_rb, uint32_t si_ga, uint32_t 
si_rb, const uint8_t dx)
+{
+  const uint32_t cov = dx;
+  const uint32_t d_rb = si_rb - di_rb;
+  const uint32_t d_ga = (si_ga - di_ga) >> 8;
+  return
+     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff))  |
+      ((di_ga + ((0xff00ff + d_ga * cov)      & 0xff00ff00)));
+}
+
+CTX_INLINE static uint32_t ctx_lerp_RGBA8_2 (const uint32_t v0, uint32_t si_ga, uint32_t si_rb, const 
uint8_t dx)
+{
+  const uint32_t cov = dx;
+  const uint32_t di_ga = ( v0 & 0xff00ff00);
+  const uint32_t di_rb = v0 & 0x00ff00ff;
+  const uint32_t d_rb = si_rb - di_rb;
+  const uint32_t d_ga = si_ga - (di_ga>>8);
+  return
+     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) |
+     (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
+}
+
+
+
 CTX_INLINE static float
 ctx_lerpf (float v0, float v1, float dx)
 {
@@ -6420,13 +6600,21 @@ struct _CtxTiled
 #endif
 };
 
-void
+static void
 _ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
                                       CtxBuffer     *buffer);
 
 #endif
 
 
+uint64_t    thash              (const char *utf8);
+const char *thash_decode       (uint64_t hash);
+uint64_t    ctx_strhash        (const char *str);
+CtxColor   *ctx_color_new      (void);
+int         ctx_get_int        (Ctx *ctx, uint64_t hash);
+int         ctx_get_is_set     (Ctx *ctx, uint64_t hash);
+Ctx        *ctx_new_for_buffer (CtxBuffer *buffer);
+
 #if CTX_IMPLEMENTATION
 
 #define SHA1_IMPLEMENTATION
@@ -7326,7 +7514,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
   {
     p = src[i];
     val *= 85;
-    if (p == '~')
+    if (CTX_UNLIKELY(p == '~'))
     {
       break;
     }
@@ -7344,10 +7532,10 @@ int ctx_a85dec (const char *src, char *dst, int count)
       k = 0;
     }
 #endif
-    else if (p >= '!' && p <= 'u')
+    else if (CTX_LIKELY(p >= '!' && p <= 'u'))
     {
       val += p-'!';
-      if (k % 5 == 4)
+      if (CTX_UNLIKELY (k % 5 == 4))
       {
          for (int j = 0; j < 4; j++)
          {
@@ -7360,7 +7548,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
     }
     // we treat all other chars as whitespace
   }
-  if (p != '~')
+  if (CTX_LIKELY (p != '~'))
   { 
     val *= 85;
   }
@@ -7385,7 +7573,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
   return out_len;
 }
 
-#if 0
+#if 1
 int ctx_a85len (const char *src, int count)
 {
   int out_len = 0;
@@ -7492,8 +7680,8 @@ typedef struct EncodeUtf5 {
   uint32_t current;
 } EncodeUtf5;
 
-void thash_encode_utf5 (const char *input, int inlen,
-                   char *output, int *r_outlen)
+static void thash_encode_utf5 (const char *input, int inlen,
+                               char *output, int *r_outlen)
 {
   uint32_t offset = THASH_START_OFFSET;
 
@@ -7590,7 +7778,7 @@ void thash_encode_utf5 (const char *input, int inlen,
   *r_outlen = len;
 }
 
-uint64_t _thash (const char *utf8)
+static inline uint64_t _thash (const char *utf8)
 {
   char encoded[4096]="";
   int  encoded_len=0;
@@ -7649,7 +7837,6 @@ static int interned_compare (const void *a, const void *b)
   return 0;
 }
 
-
 uint64_t thash (const char *utf8)
 {
   uint64_t hash = _thash (utf8);
@@ -7683,7 +7870,9 @@ uint64_t thash (const char *utf8)
   }
   return hash;
 }
-uint64_t ctx_strhash(const char *str, int ignored) { return thash (str);}
+uint64_t ctx_strhash(const char *str) {
+  return thash (str);
+}
 
 typedef struct ThashUtf5Dec {
   int      is_utf5;
@@ -7708,7 +7897,7 @@ static void thash_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *wr
   data->buf[data->length]=0;
 }
 
-void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
+static void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
 {
   if (dec->is_utf5)
   {
@@ -7750,7 +7939,7 @@ void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
   }
 }
 
-void thash_decode_utf5_bytes (int is_utf5, 
+static void thash_decode_utf5_bytes (int is_utf5, 
                         const unsigned char *input, int inlen,
                         char *output, int *r_outlen)
 {
@@ -8040,6 +8229,109 @@ ctx_base642bin (const char    *ascii,
     *length= outputno;
   return outputno;
 }
+#include <stdio.h>
+#include <string.h>
+
+static int ctx_yenc (const char *src, char *dst, int count)
+{
+  int out_len = 0;
+  for (int i = 0; i < count; i ++)
+  {
+    int o = (src[i] + 42) % 256;
+    switch (o)
+    {
+      case 0x00: //null
+      case 0x20: //space// but better safe
+      case 0x0A: //lf   // than sorry
+      case 0x0D: //cr
+      case 0x09: //tab  // not really needed
+      case 0x10: //datalink escape (used by ctx)
+      case 0x11: //xoff
+      case 0x13: //xon
+      case 0x1b: //
+      case 0xff: //
+      case 0x3D: //=
+        dst[out_len++] = '=';
+        o = (o + 64) % 256;
+        /* FALLTHROUGH */
+      default:
+        dst[out_len++] = o;
+        break;
+    }
+  }
+  dst[out_len]=0;
+  return out_len;
+}
+
+static int ctx_ydec (const char *tmp_src, char *dst, int count)
+{
+  const char *src = tmp_src;
+#if 0
+  if (tmp_src == dst)
+  {
+    src = malloc (count);
+    memcpy (src, tmp_src, count);
+  }
+#endif
+  int out_len = 0;
+  for (int i = 0; i < count; i ++)
+  {
+    int o = src[i];
+    switch (o)
+    {
+      case '=':
+        i++;
+        o = src[i];
+        if (o == 'y')
+        {
+          dst[out_len]=0;
+#if 0
+          if (tmp_src == dst) free (src);
+#endif
+          return out_len;
+        }
+        o = (o-42-64) % 256;
+        dst[out_len++] = o;
+        break;
+      case '\n':
+      case '\e':
+      case '\r':
+      case '\0':
+        break;
+      default:
+        o = (o-42) % 256;
+        dst[out_len++] = o;
+        break;
+    }
+  }
+  dst[out_len]=0;
+#if 0
+  if (tmp_src == dst) free (src);
+#endif
+  return out_len;
+}
+
+#if 0
+int main (){
+  char *input="this is a testæøåÅØ'''\"!:_asdac\n\r";
+  char  encoded[256]="";
+  char  decoded[256]="";
+  int   in_len = strlen (input);
+  int   out_len;
+  int   dec_len;
+
+  printf ("input: %s\n", input);
+
+  out_len = ctx_yenc (input, encoded, in_len);
+  printf ("encoded: %s\n", encoded);
+
+  dec_len = ydec (encoded, encoded, out_len);
+
+  printf ("decoded: %s\n", encoded);
+
+  return 0;
+}
+#endif
 #ifndef CTX_DRAWLIST_H
 #define CTX_DRAWLIST_H
 
@@ -8068,7 +8360,7 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length);
 
 static CtxEntry
 ctx_void (CtxCode code);
-static CtxEntry
+static inline CtxEntry
 ctx_f (CtxCode code, float x, float y);
 static CtxEntry
 ctx_u32 (CtxCode code, uint32_t x, uint32_t y);
@@ -8085,7 +8377,7 @@ ctx_u8 (CtxCode code,
         uint8_t e, uint8_t f, uint8_t g, uint8_t h);
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = ctx_void (cmd); \
+  CtxEntry command = {cmd};\
   ctx_process (ctx, &command);}while(0) \
 
 #define CTX_PROCESS_F(cmd, x, y) do {\
@@ -8125,6 +8417,19 @@ ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg
 static void
 ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len);
 
+#pragma pack(push,1)
+typedef struct 
+CtxSegment {
+  uint16_t code;
+  union {
+   int16_t s16[4];
+   uint32_t u32[2];
+  } data;
+  int32_t val;
+  int32_t delta;
+} CtxSegment;
+#pragma pack(pop)
+
 #endif
 
 #ifndef __CTX_UTIL_H
@@ -8160,7 +8465,7 @@ static int ctx_str_is_number (const char *str)
   return 0;
 }
 
-#if CTX_FONTS_FROM_FILE
+#if CTX_GET_CONTENTS
 
 typedef struct CtxFileContent
 {
@@ -8266,6 +8571,7 @@ __ctx_file_get_contents (const char     *path,
 #endif
 
 
+
 static inline int
 ctx_conts_for_entry (CtxEntry *entry)
 {
@@ -8283,6 +8589,7 @@ ctx_conts_for_entry (CtxEntry *entry)
       case CTX_CURVE_TO:
       case CTX_REL_CURVE_TO:
       case CTX_APPLY_TRANSFORM:
+      case CTX_SOURCE_TRANSFORM:
       case CTX_COLOR:
       case CTX_ROUND_RECTANGLE:
       case CTX_SHADOW_COLOR:
@@ -8311,7 +8618,6 @@ ctx_conts_for_entry (CtxEntry *entry)
           int pix_len = entry[2 + eid_len + 1].data.u32[1];
           return eid_len + pix_len + 2 + 1;
         }
-
       default:
         return 0;
     }
@@ -8413,7 +8719,7 @@ ctx_iterator_next (CtxIterator *iterator)
 #if CTX_BITPACK
   int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK;
 again:
-  if (iterator->bitpack_length)
+  if (CTX_UNLIKELY(iterator->bitpack_length))
     {
       ret = &iterator->bitpack_command[iterator->bitpack_pos];
       iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1);
@@ -8426,7 +8732,7 @@ again:
 #endif
   ret = _ctx_iterator_next (iterator);
 #if CTX_BITPACK
-  if (ret && expand_bitpack)
+  if (CTX_UNLIKELY(ret && expand_bitpack))
     switch ((CtxCode)(ret->code))
       {
         case CTX_REL_CURVE_TO_REL_LINE_TO:
@@ -8512,6 +8818,7 @@ again:
         case CTX_CURVE_TO:
         case CTX_REL_CURVE_TO:
         case CTX_APPLY_TRANSFORM:
+        case CTX_SOURCE_TRANSFORM:
         case CTX_ROUND_RECTANGLE:
         case CTX_TEXT:
         case CTX_STROKE_TEXT:
@@ -8592,14 +8899,15 @@ static void ctx_drawlist_compact (CtxDrawlist *drawlist);
 static void
 ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
 {
+  int flags=drawlist->flags;
 #if CTX_DRAWLIST_STATIC
-  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
+  if (flags & CTX_DRAWLIST_EDGE_LIST)
     {
-      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
-      drawlist->entries = &sbuf[0];
+      static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE];
+      drawlist->entries = (CtxEntry*)&sbuf[0];
       drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
     }
-  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
+  else if (flags & CTX_DRAWLIST_CURRENT_PATH)
     {
       static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
       drawlist->entries = &sbuf[0];
@@ -8616,47 +8924,49 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
   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))
+  if ((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)
+  else if (flags & CTX_DRAWLIST_CURRENT_PATH)
     {
       min_size = CTX_MIN_EDGE_LIST_SIZE;
       max_size = CTX_MAX_EDGE_LIST_SIZE;
     }
   else
     {
+#if 0
       ctx_drawlist_compact (drawlist);
+#endif
     }
 
-  if (new_size < drawlist->size)
+  if (CTX_UNLIKELY(new_size < drawlist->size))
     { return; }
-  if (drawlist->size == max_size)
+  if (CTX_UNLIKELY(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; }
+  new_size = ctx_maxi (new_size, min_size);
+  //if (new_size < drawlist->count)
+  //  { new_size = drawlist->count + 4; }
+  new_size = ctx_mini (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);
+      int item_size = sizeof (CtxEntry);
+      if (flags & CTX_DRAWLIST_EDGE_LIST) item_size = sizeof (CtxSegment);
+      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", 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) );
+      CtxEntry *ne =  (CtxEntry *) malloc (item_size * new_size);
+      memcpy (ne, drawlist->entries, drawlist->size * item_size );
       free (drawlist->entries);
       drawlist->entries = ne;
-      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, sizeof (CtxEntry) * new_size);
+      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, item_size * new_size);
     }
   else
     {
       //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size);
-      drawlist->entries = (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
+      drawlist->entries = (CtxEntry *) malloc (item_size * new_size);
     }
   drawlist->size = new_size;
     }
@@ -8664,34 +8974,105 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
 #endif
 }
 
-static int
-ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
+static void
+ctx_edgelist_resize (CtxDrawlist *drawlist, int desired_size)
 {
+#if CTX_DRAWLIST_STATIC
+    {
+      static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE];
+      drawlist->entries = (CtxEntry*)&sbuf[0];
+      drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+#else
+  int new_size = desired_size;
+  int min_size = CTX_MIN_JOURNAL_SIZE;
   int max_size = CTX_MAX_JOURNAL_SIZE;
-  int ret = drawlist->count;
-  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)
+
+  if (CTX_UNLIKELY(drawlist->size == max_size))
+    { return; }
+  new_size = ctx_maxi (new_size, min_size);
+  //if (new_size < drawlist->count)
+  //  { new_size = drawlist->count + 4; }
+  new_size = ctx_mini (new_size, max_size);
+  if (new_size != drawlist->size)
+    {
+      int item_size = item_size = sizeof (CtxSegment);
+      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", 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 (item_size * new_size);
+      memcpy (ne, drawlist->entries, drawlist->size * item_size );
+      free (drawlist->entries);
+      drawlist->entries = ne;
+      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, item_size * new_size);
+    }
+  else
+    {
+      //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size);
+      drawlist->entries = (CtxEntry *) malloc (item_size * new_size);
+    }
+  drawlist->size = new_size;
+    }
+  //fprintf (stderr, "drawlist %p is %d\n", drawlist, drawlist->size);
+#endif
+}
+
+
+static inline int
+ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
+{
+  int max_size = CTX_MAX_JOURNAL_SIZE;
+  int ret = drawlist->count;
+  int flags = drawlist->flags;
+  if (CTX_LIKELY((flags & CTX_DRAWLIST_EDGE_LIST ||
+       flags & CTX_DRAWLIST_CURRENT_PATH)))
     {
       max_size = CTX_MAX_EDGE_LIST_SIZE;
     }
-  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
+  if (CTX_UNLIKELY(flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES))
     {
       return ret;
     }
-  if (ret + 1024 >= drawlist->size - 40)
+  if (CTX_UNLIKELY(ret + 64 >= drawlist->size - 40))
     {
       int new_ = CTX_MAX (drawlist->size * 2, ret + 1024);
       ctx_drawlist_resize (drawlist, new_);
     }
 
-  if (drawlist->count >= max_size - 20)
+  if (CTX_UNLIKELY(drawlist->count >= max_size - 20))
+    {
+      return 0;
+    }
+  if ((flags & CTX_DRAWLIST_EDGE_LIST))
+    ((CtxSegment*)(drawlist->entries))[drawlist->count] = *(CtxSegment*)entry;
+  else
+    drawlist->entries[drawlist->count] = *entry;
+  ret = drawlist->count;
+  drawlist->count++;
+  return ret;
+}
+
+static inline int
+ctx_edgelist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
+{
+  int max_size = CTX_MAX_EDGE_LIST_SIZE;
+  int ret = drawlist->count;
+  if ((ret + 64 >= drawlist->size - 40))
+    {
+      int new_ = CTX_MAX (drawlist->size * 2, ret + 1024);
+      ctx_edgelist_resize (drawlist, new_);
+    }
+
+  if (CTX_UNLIKELY(drawlist->count >= max_size - 20))
     {
       return 0;
     }
-  drawlist->entries[drawlist->count] = *entry;
+  ((CtxSegment*)(drawlist->entries))[drawlist->count] = *(CtxSegment*)entry;
   ret = drawlist->count;
   drawlist->count++;
   return ret;
@@ -8703,7 +9084,7 @@ ctx_add_single (Ctx *ctx, void *entry)
   return ctx_drawlist_add_single (&ctx->drawlist, (CtxEntry *) entry);
 }
 
-static int
+static inline int
 ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry)
 {
   int length = ctx_conts_for_entry (entry) + 1;
@@ -8770,7 +9151,7 @@ int ctx_set_drawlist (Ctx *ctx, void *data, int length)
     {
       return -1;
     }
-  if (length % 9) return -1;
+  if (CTX_UNLIKELY(length % 9)) return -1;
   ctx_drawlist_resize (drawlist, length/9);
   memcpy (drawlist->entries, data, length);
   drawlist->count = length / 9;
@@ -8790,7 +9171,7 @@ const CtxEntry *ctx_get_drawlist (Ctx *ctx)
 int
 ctx_add_data (Ctx *ctx, void *data, int length)
 {
-  if (length % sizeof (CtxEntry) )
+  if (CTX_UNLIKELY(length % sizeof (CtxEntry) ))
     {
       //ctx_log("err\n");
       return -1;
@@ -8818,14 +9199,14 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
   entry.data.u32[0] = 0;
   entry.data.u32[1] = 0;
   int ret = ctx_drawlist_add_single (drawlist, &entry);
-  if (!data) { return -1; }
+  if (CTX_UNLIKELY(!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)
+  if (CTX_UNLIKELY(drawlist->count >= drawlist->size))
     { return -1; }
   drawlist->count += length_in_blocks;
   drawlist->entries[ret].data.u32[0] = length;
@@ -8845,20 +9226,19 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
   return ret;
 }
 
-static CtxEntry
+static inline CtxEntry
 ctx_void (CtxCode code)
 {
   CtxEntry command;
   command.code = code;
-  command.data.u32[0] = 0;
-  command.data.u32[1] = 0;
   return command;
 }
 
-static CtxEntry
+static inline CtxEntry
 ctx_f (CtxCode code, float x, float y)
 {
-  CtxEntry command = ctx_void (code);
+  CtxEntry command;
+  command.code = code;
   command.data.f[0] = x;
   command.data.f[1] = y;
   return command;
@@ -8887,7 +9267,20 @@ ctx_s32 (CtxCode code, int32_t x, int32_t y)
 static inline CtxEntry
 ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1)
 {
-  CtxEntry command = ctx_void (code);
+  CtxEntry command;
+  command.code = code;
+  command.data.s16[0] = x0;
+  command.data.s16[1] = y0;
+  command.data.s16[2] = x1;
+  command.data.s16[3] = y1;
+  return command;
+}
+
+static inline CtxSegment
+ctx_segment_s16 (CtxCode code, int x0, int y0, int x1, int y1)
+{
+  CtxSegment command;
+  command.code = code;
   command.data.s16[0] = x0;
   command.data.s16[1] = y0;
   command.data.s16[2] = x1;
@@ -8900,7 +9293,8 @@ 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);
+  CtxEntry command;
+  command.code = code;
   command.data.u8[0] = a;
   command.data.u8[1] = b;
   command.data.u8[2] = c;
@@ -8913,7 +9307,7 @@ ctx_u8 (CtxCode code,
 }
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = ctx_void (cmd); \
+  CtxEntry command = {cmd};\
   ctx_process (ctx, &command);}while(0) \
 
 #define CTX_PROCESS_F(cmd, x, y) do {\
@@ -9295,20 +9689,36 @@ uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry)
 {
   return &entry[2 + 1 + 1 + ctx_conts_for_entry (&entry[2])].data.u8[0];
 }
+
 #ifndef __CTX_TRANSFORM
 #define __CTX_TRANSFORM
 
-static void
+static inline 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]);
+}
+
+void
+ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
+{
+  _ctx_matrix_apply_transform (m, x, y);
+}
+
+static inline void
 _ctx_user_to_device (CtxState *state, float *x, float *y)
 {
-  ctx_matrix_apply_transform (&state->gstate.transform, x, y);
+  _ctx_matrix_apply_transform (&state->gstate.transform, x, y);
 }
 
 static void
 _ctx_user_to_device_distance (CtxState *state, float *x, float *y)
 {
   const CtxMatrix *m = &state->gstate.transform;
-  ctx_matrix_apply_transform (m, x, y);
+  _ctx_matrix_apply_transform (m, x, y);
   *x -= m->m[2][0];
   *y -= m->m[2][1];
 }
@@ -9333,8 +9743,8 @@ ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e,
   matrix->m[2][1] = f;
 }
 
-void
-ctx_matrix_identity (CtxMatrix *matrix)
+static inline void
+_ctx_matrix_identity (CtxMatrix *matrix)
 {
   matrix->m[0][0] = 1.0f;
   matrix->m[0][1] = 0.0f;
@@ -9345,9 +9755,15 @@ ctx_matrix_identity (CtxMatrix *matrix)
 }
 
 void
-ctx_matrix_multiply (CtxMatrix       *result,
-                     const CtxMatrix *t,
-                     const CtxMatrix *s)
+ctx_matrix_identity (CtxMatrix *matrix)
+{
+  _ctx_matrix_identity (matrix);
+}
+
+static void
+_ctx_matrix_multiply (CtxMatrix       *result,
+                      const CtxMatrix *t,
+                      const CtxMatrix *s)
 {
   CtxMatrix r;
   r.m[0][0] = t->m[0][0] * s->m[0][0] + t->m[0][1] * s->m[1][0];
@@ -9359,6 +9775,13 @@ ctx_matrix_multiply (CtxMatrix       *result,
   *result = r;
 }
 
+void
+ctx_matrix_multiply (CtxMatrix       *result,
+                     const CtxMatrix *t,
+                     const CtxMatrix *s)
+{
+  _ctx_matrix_multiply (result, t, s);
+}
 
 void
 ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
@@ -9370,7 +9793,7 @@ ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] = x;
   transform.m[2][1] = y;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  _ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 void
@@ -9383,7 +9806,7 @@ ctx_matrix_scale (CtxMatrix *matrix, float x, float y)
   transform.m[1][1] = y;
   transform.m[2][0] = 0.0f;
   transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  _ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 void
@@ -9398,7 +9821,7 @@ ctx_matrix_rotate (CtxMatrix *matrix, float angle)
   transform.m[1][1] = val_cos;
   transform.m[2][0] =     0.0f;
   transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  _ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 #if 0
@@ -9413,7 +9836,7 @@ ctx_matrix_skew_x (CtxMatrix *matrix, float angle)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] =    0.0f;
   transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  _ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 static void
@@ -9427,7 +9850,7 @@ ctx_matrix_skew_y (CtxMatrix *matrix, float angle)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] =    0.0f;
   transform.m[2][1] = 0.0f;
-  ctx_matrix_multiply (matrix, &transform, matrix);
+  _ctx_matrix_multiply (matrix, &transform, matrix);
 }
 #endif
 
@@ -9438,6 +9861,8 @@ 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
@@ -9465,6 +9890,29 @@ ctx_get_transform  (Ctx *ctx, float *a, float *b,
   if (f) { *f = ctx->state.gstate.transform.m[2][1]; }
 }
 
+void
+ctx_source_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_SOURCE_TRANSFORM, a, b),
+    ctx_f (CTX_CONT,             c, d),
+    ctx_f (CTX_CONT,             e, f)
+  };
+  ctx_process (ctx, command);
+}
+
+void
+ctx_source_transform_matrix (Ctx *ctx, CtxMatrix *matrix)
+{
+  ctx_source_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]);
+}
+
 void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix)
 {
   ctx_apply_transform (ctx,
@@ -9533,14 +9981,6 @@ ctx_matrix_invert (CtxMatrix *m)
   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
@@ -9589,7 +10029,7 @@ inline static float ctx_u8_to_float (uint8_t val_u8)
 float ctx_u8_float[256];
 #endif
 
-CtxColor *ctx_color_new ()
+CtxColor *ctx_color_new (void)
 {
   CtxColor *color = (CtxColor*)ctx_calloc (sizeof (CtxColor), 1);
   return color;
@@ -9845,7 +10285,9 @@ static void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out)
   out[3] = color->alpha;
 }
 
-void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
+
+static inline void
+_ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
 {
 #if CTX_ENABLE_CM
   if (! (color->valid & CTX_VALID_RGBA) )
@@ -9867,6 +10309,12 @@ void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
 #endif
 }
 
+void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
+{
+  _ctx_color_get_rgba (state, color, out);
+}
+
+
 
 float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb)
 {
@@ -9938,8 +10386,8 @@ static void ctx_color_get_cmyka_u8 (CtxState *state, CtxColor *color, uint8_t *o
 #endif
 #endif
 
-void
-ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
+static inline void
+_ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
   if (! (color->valid & CTX_VALID_RGBA_U8) )
     {
@@ -9955,6 +10403,12 @@ ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
   out[3] = color->rgba[3];
 }
 
+void
+ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
+{
+  _ctx_color_get_rgba8 (state, color, out);
+}
+
 void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
   if (! (color->valid & CTX_VALID_GRAYA_U8) )
@@ -10378,7 +10832,7 @@ mrg_color_parse_hex (CtxState *ctxstate, CtxColor *color, const char *color_stri
 int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string)
 {
   int i;
-  uint64_t hash = ctx_strhash (string, 0);
+  uint64_t hash = ctx_strhash (string);
 //  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]);
@@ -10595,7 +11049,7 @@ void ctx_gradient_add_stop_string
 //  userRGB - settable at any time, stored in save|restore 
 //  texture - set as the space of data on subsequent 
 
-float ctx_state_get (CtxState *state, uint64_t hash)
+static float ctx_state_get (CtxState *state, uint64_t hash)
 {
   for (int i = state->gstate.keydb_pos-1; i>=0; i--)
     {
@@ -10605,7 +11059,7 @@ float ctx_state_get (CtxState *state, uint64_t hash)
   return -0.0;
 }
 
-void ctx_state_set (CtxState *state, uint64_t key, float value)
+static void ctx_state_set (CtxState *state, uint64_t key, float value)
 {
   if (key != CTX_new_state)
     {
@@ -10791,22955 +11245,36199 @@ int ctx_is_set_now (Ctx *ctx, uint64_t hash)
 {
   return ctx_is_set (ctx, hash);
 }
-#if CTX_RASTERIZER
 
-void ctx_compositor_setup_default (CtxRasterizer *rasterizer);
+#if CTX_COMPOSITE
 
-#define CTX_FULL_AA 15
-inline static void
-ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
-                               uint8_t * __restrict__ dst,
-                               int            x,
-                               uint8_t * __restrict__ coverage,
-                               int            count)
-{
-  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);
-}
+#define CTX_REFERENCE 0
 
-static void
-ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
-{
-  /* FIXME XXX we only have one gradient, but might need separate gradients
-   * for fill/stroke !
-   * 
-   */
-  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 int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
-{
-  CtxEntry entry = {CTX_EDGE, {{0},}};
-  if (y1 < rasterizer->scan_min)
-    { rasterizer->scan_min = y1; }
-  if (y1 > rasterizer->scan_max)
-    { rasterizer->scan_max = y1; }
+#define CTX_RGBA8_R_SHIFT  0
+#define CTX_RGBA8_G_SHIFT  8
+#define CTX_RGBA8_B_SHIFT  16
+#define CTX_RGBA8_A_SHIFT  24
 
-  if (x1 < rasterizer->col_min)
-    { rasterizer->col_min = x1; }
-  if (x1 > rasterizer->col_max)
-    { rasterizer->col_max = x1; }
+#define CTX_RGBA8_R_MASK   (0xff << CTX_RGBA8_R_SHIFT)
+#define CTX_RGBA8_G_MASK   (0xff << CTX_RGBA8_G_SHIFT)
+#define CTX_RGBA8_B_MASK   (0xff << CTX_RGBA8_B_SHIFT)
+#define CTX_RGBA8_A_MASK   (0xff << CTX_RGBA8_A_SHIFT)
 
-  entry.data.s16[2]=x1;
-  entry.data.s16[3]=y1;
-  return ctx_drawlist_add_single (&rasterizer->edge_list, &entry);
-}
+#define CTX_RGBA8_RB_MASK  (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK)
+#define CTX_RGBA8_GA_MASK  (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK)
 
-#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
+
+CTX_INLINE static void
+ctx_RGBA8_associate_alpha (uint8_t *u8)
+{
+#if 1
+  uint32_t val = *((uint32_t*)(u8));
+  uint8_t a = u8[3];
+  uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
+  uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
+  *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
 #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
+  uint32_t a = u8[3];
+  u8[0] = (u8[0] * a + 255) >> 8;
+  u8[1] = (u8[1] * a + 255) >> 8;
+  u8[2] = (u8[2] * a + 255) >> 8;
 #endif
+}
 
-float ctx_shape_cache_rate = 0.0;
-#if CTX_SHAPE_CACHE
-int   _ctx_shape_cache_enabled = 1;
-
-//static CtxShapeCache ctx_cache = {{NULL,}, 0};
-
-static long ctx_shape_cache_hits = 0;
-static long ctx_shape_cache_misses = 0;
-
+CTX_INLINE static void
+ctx_RGBA8_associate_alpha_probably_opaque (uint8_t *u8)
+{
+  uint32_t val = *((uint32_t*)(u8));
+  uint32_t a = val>>24;//u8[3];
+  //if (CTX_UNLIKELY(a==0))
+  //   *((uint32_t*)(u8)) = 0;
+  if (CTX_UNLIKELY(a!=255))
+  {
+     uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
+     uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
+     *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
+  }
+}
 
-/* 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)
+CTX_INLINE static uint32_t ctx_bi_RGBA8 (uint32_t isrc00, uint32_t isrc01, uint32_t isrc10, uint32_t isrc11, 
uint8_t dx, uint8_t dy)
 {
-  /* use both some high and some low bits  */
-  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
-  int i;
+#if 0
+#if 0
+  uint8_t ret[4];
+  uint8_t *src00 = (uint8_t*)&isrc00;
+  uint8_t *src10 = (uint8_t*)&isrc10;
+  uint8_t *src01 = (uint8_t*)&isrc01;
+  uint8_t *src11 = (uint8_t*)&isrc11;
+  for (int c = 0; c < 4; c++)
   {
-    static int i = 0;
-    i++;
-    if (i>1000)
-      {
-        ctx_shape_cache_rate = ctx_shape_cache_hits * 100.0  / (ctx_shape_cache_hits+ctx_shape_cache_misses);
-        i = 0;
-        ctx_shape_cache_hits = 0;
-        ctx_shape_cache_misses = 0;
-      }
+    ret[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                         ctx_lerp_u8 (src10[c], src11[c], dx), dy);
   }
-// 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;
+  return  ((uint32_t*)&ret[0])[0];
+#else
+  return ctx_lerp_RGBA8 (ctx_lerp_RGBA8 (isrc00, isrc01, dx),
+                         ctx_lerp_RGBA8 (isrc10, isrc11, dx), dy);
+#endif
+#else
+  uint32_t s0_ga, s0_rb, s1_ga, s1_rb;
+  ctx_lerp_RGBA8_split (isrc00, isrc01, dx, &s0_ga, &s0_rb);
+  ctx_lerp_RGBA8_split (isrc10, isrc11, dx, &s1_ga, &s1_rb);
+  return ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, dy);
+#endif
+}
 
-  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++; }
-          ctx_shape_cache_hits ++;
-          return entry;
-        }
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
+static uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
+extern int ctx_gradient_cache_valid;
 
-      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);
-    }
 
-  ctx_shape_cache_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];
+inline static int ctx_grad_index (float v)
+{
+  int ret = v * (CTX_GRADIENT_CACHE_ELEMENTS - 1) + 0.5f;
+  ret = ctx_maxi (0, ret);
+  ret = ctx_mini (CTX_GRADIENT_CACHE_ELEMENTS-1, ret);
+  return ret;
+}
+
+inline static int ctx_grad_index_i (int v)
+{
+  v = v >> 8;
+  return ctx_maxi (0, ctx_mini (CTX_GRADIENT_CACHE_ELEMENTS-1, v));
 }
 
+
+//static void
+//ctx_gradient_cache_reset (void)
+//{
+//  ctx_gradient_cache_valid = 0;
+//}
 #endif
 
-static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
+
+CTX_INLINE static void
+_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
 {
-  int16_t x = 0;
-  int16_t y = 0;
+  float v = x;
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
 
-  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++)
+  if (g->n_stops == 0)
     {
-      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;
+      rgba[0] = rgba[1] = rgba[2] = v * 255;
+      rgba[3] = 255;
+      return;
     }
-  return hash;
-}
-
-static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
-{
-  int16_t x = 0;
-  int16_t y = 0;
-  if (rasterizer->edge_list.count == 0)
-     return 0;
-#if CTX_SHAPE_CACHE
-  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 % CTX_FULL_AA);
-#endif
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
     {
-      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]);
-        }
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
     }
-#if CTX_SHAPE_CACHE
-  return hash;
-#else
-  return 0;
-#endif
-}
-
-static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
-{
-  if (rasterizer->has_shape && rasterizer->has_prev)
+  if (stop == NULL && next_stop)
     {
-      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
-      rasterizer->has_prev = 0;
+      color = & (next_stop->color);
     }
-}
-
-static void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
-{
-  float tx = x; float ty = y;
-  int aa = 15;//rasterizer->aa;
-  rasterizer->x        = x;
-  rasterizer->y        = y;
-  rasterizer->first_x  = x;
-  rasterizer->first_y  = y;
-  rasterizer->has_prev = -1;
-  if (rasterizer->uses_transforms)
+  else if (stop && next_stop == NULL)
     {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
+      color = & (stop->color);
     }
-
-  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 inline 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)
+  else if (stop && next_stop)
     {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
+      uint8_t stop_rgba[4];
+      uint8_t next_rgba[4];
+      ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
+#if 1
+      ((uint32_t*)rgba)[0] = ctx_lerp_RGBA8 (((uint32_t*)stop_rgba)[0],
+                                             ((uint32_t*)next_rgba)[0], dx);
+#else
+      for (int c = 0; c < 4; c++)
+        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
+#endif
+      ctx_RGBA8_associate_alpha (rgba);
+      return;
     }
-  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 * 15);//rasterizer->aa);
-
-  if (rasterizer->has_prev<=0)
+  else
     {
-      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 * 15;//rasterizer->aa;
-      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].code = CTX_NEW_EDGE;
-      rasterizer->has_prev = 1;
+      color = & (g->stops[g->n_stops-1].color);
     }
-  rasterizer->has_shape = 1;
-  rasterizer->y         = y;
-  rasterizer->x         = x;
+  ctx_color_get_rgba8 (rasterizer->state, color, rgba);
+  if (rasterizer->swap_red_green)
+  {
+    uint8_t tmp = rgba[0];
+    rgba[0] = rgba[2];
+    rgba[2] = tmp;
+  }
+  ctx_RGBA8_associate_alpha (rgba);
 }
 
+#if CTX_GRADIENT_CACHE
+static void
+ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
+#endif
 
-CTX_INLINE static float
-ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
+CTX_INLINE static void
+ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
 {
-  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);
+#if CTX_GRADIENT_CACHE
+  *((uint32_t*)rgba) = *((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(x)][0]));
+#else
+ _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba);
+#endif
 }
+#endif
 
-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)
+CTX_INLINE static void
+ctx_u8_associate_alpha (int components, uint8_t *u8)
 {
-  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
-  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
+  for (int c = 0; c < components-1; c++)
+    u8[c] = (u8[c] * u8[components-1] + 255)>>8;
 }
 
-static inline 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)
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
+static void
+ctx_gradient_cache_prime (CtxRasterizer *rasterizer)
 {
-  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)
+  if (ctx_gradient_cache_valid)
+    return;
+  for (int u = 0; u < CTX_GRADIENT_CACHE_ELEMENTS; u++)
+  {
+    float v = u / (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f);
+    _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &ctx_gradient_cache_u8[u][0]);
+    //*((uint32_t*)(&ctx_gradient_cache_u8_a[u][0]))= *((uint32_t*)(&ctx_gradient_cache_u8[u][0]));
+    //memcpy(&ctx_gradient_cache_u8_a[u][0], &ctx_gradient_cache_u8[u][0], 4);
+    //ctx_RGBA8_associate_alpha (&ctx_gradient_cache_u8_a[u][0]);
+  }
+  ctx_gradient_cache_valid = 1;
+}
+#endif
+
+CTX_INLINE static void
+ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+{
+  float v = x;
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
+  if (g->n_stops == 0)
     {
-      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; }
+      rgba[0] = rgba[1] = rgba[2] = v * 255;
+      rgba[1] = 255;
+      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);
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
+    {
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
+    }
+  if (stop == NULL && next_stop)
+    {
+      color = & (next_stop->color);
+    }
+  else if (stop && next_stop == NULL)
+    {
+      color = & (stop->color);
+    }
+  else if (stop && next_stop)
+    {
+      uint8_t stop_rgba[4];
+      uint8_t next_rgba[4];
+      ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
+      for (int c = 0; c < 2; c++)
+        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
+      return;
+    }
+  else
+    {
+      color = & (g->stops[g->n_stops-1].color);
+    }
+  ctx_color_get_graya_u8 (rasterizer->state, color, rgba);
 }
 
-static void
-ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
-                         float x0, float y0,
-                         float x1, float y1,
-                         float x2, float y2)
+CTX_INLINE static void
+ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba)
 {
-  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 1
-    if(
-        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
-        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
-        (maxx < rasterizer->blit_x) ||
-        (maxy < rasterizer->blit_y) )
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
+  if (g->n_stops == 0)
+    {
+      rgba[0] = rgba[1] = rgba[2] = v;
+      rgba[3] = 1.0;
+      return;
+    }
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
     {
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
     }
-    else
-#endif
+  if (stop == NULL && next_stop)
     {
-      ctx_rasterizer_bezier_divide (rasterizer,
-                                    ox, oy, x0, y0,
-                                    x1, y1, x2, y2,
-                                    ox, oy, x2, y2,
-                                    0.0f, 1.0f, 0.0f, tolerance);
+      color = & (next_stop->color);
+    }
+  else if (stop && next_stop == NULL)
+    {
+      color = & (stop->color);
+    }
+  else if (stop && next_stop)
+    {
+      float stop_rgba[4];
+      float next_rgba[4];
+      ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) / (next_stop->pos - stop->pos);
+      for (int c = 0; c < 4; c++)
+        { rgba[c] = ctx_lerpf (stop_rgba[c], next_rgba[c], dx); }
+      return;
     }
-  }
   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);
+      color = & (g->stops[g->n_stops-1].color);
     }
-  ctx_rasterizer_line_to (rasterizer, x2, y2);
+  ctx_color_get_rgba (rasterizer->state, color, rgba);
 }
+#endif
 
 static void
-ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
+ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
 {
-  if (x == 0.f && y == 0.f)
-    { return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_move_to (rasterizer, x, y);
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  ctx_assert (rasterizer);
+  ctx_assert (g);
+  ctx_assert (buffer);
+
+  for (int i = 0; i < count; i ++)
+  {
+
+  int u = x;
+  int v = y;
+  int width = buffer->width;
+  int height = buffer->height;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
+    {
+      *((uint32_t*)(rgba)) = 0;
+    }
+  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 < width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < 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)
+        {
+          case 1:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[0]; }
+            rgba[3] = 255;
+            break;
+          case 2:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[0]; }
+            rgba[3] = src[1];
+            break;
+          case 3:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[c]; }
+            rgba[3] = 255;
+            break;
+          case 4:
+            for (int c = 0; c < 4; c++)
+              { rgba[c] = src[c]; }
+            break;
+        }
+      }
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+    }
+    ctx_RGBA8_associate_alpha_probably_opaque (rgba); // XXX: really?
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
 }
 
-static void
-ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
+#if CTX_DITHER
+static inline int ctx_dither_mask_a (int x, int y, int c, int divisor)
 {
-  if (x== 0.f && y==0.f)
-    { return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_line_to (rasterizer, x, y);
+  /* https://pippin.gimp.org/a_dither/ */
+  return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor;
 }
 
-static void
-ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer,
-                             float x0, float y0, float x1, float y1, float x2, float y2)
+inline static void
+ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
 {
-  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);
+  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);
+    }
 }
 
-
-static int
-ctx_rasterizer_find_texture (CtxRasterizer *rasterizer,
-                             const char *eid)
+inline static void
+ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
 {
-  int no;
-  for (no = 0; no < CTX_MAX_TEXTURES; no++)
-  {
-    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;
+  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
 
-static void
-ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
-                            const char *eid,
-                            float x,
-                            float y)
+CTX_INLINE static void
+ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out)
 {
-  int is_stroke = (rasterizer->state->source != 0);
-  CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)?
-                        &rasterizer->state->gstate.source_stroke:
-                        &rasterizer->state->gstate.source_fill;
-  rasterizer->state->source = 0;
-
-  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)
+    uint32_t val = *((uint32_t*)(in));
+    int a = val >> CTX_RGBA8_A_SHIFT;
+    if (a)
     {
-      fprintf (stderr, "ctx tex fail %p %s %i\n", rasterizer->texture_source, eid, no);
-      return;
+    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
   {
-    rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame;
+  for (int c = 0; c < components; c++)
+    out[c] = 0;
   }
-  source->type = CTX_SOURCE_TEXTURE;
-  source->texture.buffer = &rasterizer->texture_source->texture[no];
-  source->texture.x0 = 0;
-  source->texture.y0 = 0;
-  source->transform = rasterizer->state->gstate.transform;
-  ctx_matrix_translate (&source->transform, x, y);
-  ctx_matrix_invert (&source->transform);
 }
 
+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;
+}
 
-static void ctx_rasterizer_define_texture (CtxRasterizer *rasterizer,
-                                           const char *eid,
-                                           int width,
-                                           int height,
-                                           int format,
-                                           char unsigned *data)
+CTX_INLINE static void
+ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
 {
-  _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
-                     */
+  float ralpha = rgba[components-1];
+  if (ralpha != 0.0) ralpha = 1.0/ralpha;
 
-  _ctx_texture_unlock ();
-  ctx_rasterizer_set_texture (rasterizer, eid, 0.0, 0.0);
+  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 int ctx_compare_edges (const void *ap, const void *bp)
+CTX_INLINE static void
+ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
 {
-  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_float_deassociate_alpha (4, rgba, dst);
 }
 
-CTX_INLINE static int ctx_edge_qsort_partition (CtxEntry *A, int low, int high)
+
+static inline void ctx_swap_red_green_u8 (void *data)
 {
-  CtxEntry pivot = A[ (high+low) /2];
-  int i = low;
-  int j = high;
-  while (i <= j)
-    {
-      while (ctx_compare_edges (&A[i], &pivot) <0) { i ++; }
-      while (ctx_compare_edges (&pivot, &A[j]) <0) { j --; }
-      if (i <= j)
-        {
-          CtxEntry tmp = A[i];
-          A[i] = A[j];
-          A[j] = tmp;
-          i++;
-          j--;
-        }
-    }
-  return i;
+  uint8_t *rgba = (uint8_t*)data;
+  uint8_t tmp = rgba[0];
+  rgba[0] = rgba[2];
+  rgba[2] = tmp;
 }
 
-static void ctx_edge_qsort (CtxEntry *entries, int low, int high)
+static void
+ctx_fragment_swap_red_green_u8 (void *out, int count)
 {
+  uint8_t *rgba = (uint8_t*)out;
+  for (int x = 0; x < count; x++)
   {
-    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); }
+    ctx_swap_red_green_u8 (rgba);
+    rgba += 4;
   }
 }
 
-static CTX_INLINE void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
+/**** rgb8 ***/
+
+static void
+ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out, int count, float dx, float dy)
 {
-  ctx_edge_qsort (& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
-}
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  int width = buffer->width;
+  int height = buffer->height;
 
+  for (int i = 0; i < count; i++)
+  {
 
-static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
-{
-  int scanline = rasterizer->scanline;
-  int slope_limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
-  int slope_limit5 = CTX_RASTERIZER_AA_SLOPE_LIMIT5;
-  int slope_limit15 = CTX_RASTERIZER_AA_SLOPE_LIMIT15;
-  rasterizer->ending_edges = 0;
-  for (int i = 0; i < rasterizer->active_edges; i++)
+  int u = x;
+  int v = y;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
     {
-      int edge_end =rasterizer->edge_list.entries[rasterizer->edges[i].index].data.s16[3]-1;
-      if (CTX_UNLIKELY(edge_end < scanline))
-        {
-          int dx_dy = abs(rasterizer->edges[i].delta);
-          if (dx_dy > slope_limit15)
-            { rasterizer->needs_aa15 --; }
-          else if (dx_dy > slope_limit5)
-            { rasterizer->needs_aa5 --; }
-          else if (dx_dy > slope_limit3)
-            { rasterizer->needs_aa3 --; }
-          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
-          rasterizer->active_edges--;
-          i--;
-        }
-      else if (edge_end < scanline + CTX_FULL_AA)
-        rasterizer->ending_edges = 1; // only used as a flag!
+      *((uint32_t*)(rgba))= 0;
     }
-}
-
-inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
-{
-  rasterizer->scanline += count;
-  for (int i = 0; i < rasterizer->active_edges; i++)
+  else
     {
-      rasterizer->edges[i].val += rasterizer->edges[i].delta * count;
-    }
-  for (int i = 0; i < rasterizer->pending_edges; i++)
-    {
-      rasterizer->edges[CTX_MAX_EDGES-1-i].val += rasterizer->edges[CTX_MAX_EDGES-1-i].delta * count;
-    }
-}
+      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;
 
-/* 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)
-{
-  int miny;
-  CtxEntry *entries = rasterizer->edge_list.entries;
-  for (int i = 0; i < rasterizer->pending_edges; i++)
-    {
-      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i].index].data.s16[1] - 1 <= rasterizer->scanline)
-        {
-          if (CTX_LIKELY(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--;
-            }
-        }
-    }
-  int scanline = rasterizer->scanline;
-  while (CTX_LIKELY(rasterizer->edge_pos < rasterizer->edge_list.count &&
-         (miny=entries[rasterizer->edge_pos].data.s16[1]-1)  <= scanline + 15))
-    {
-      int maxy=entries[rasterizer->edge_pos].data.s16[3]-1;
-      if (CTX_LIKELY(rasterizer->active_edges < CTX_MAX_EDGES-2) &&
-          maxy >= scanline)
-        {
-          int dy = (entries[rasterizer->edge_pos].data.s16[3] - 1 - miny);
-          if (dy) /* skipping horizontal edges */
+            if (v+ov >= 0 && u+ou >=0 && u + ou < width && v + ov < height)
             {
-              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].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER;
-              int dx_dy;
-              dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
-              rasterizer->edges[no].delta = dx_dy;
-              rasterizer->edges[no].val += (yd * dx_dy);
-
-              {
-                int abs_dx_dy = abs(dx_dy);
-                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT3)
-                  { rasterizer->needs_aa3 ++; }
-                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT5)
-                  { rasterizer->needs_aa5 ++; }
-                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT15)
-                  { rasterizer->needs_aa15 ++; }
-              }
+              int o = (v+ov) * width + (u + ou);
+              src += o * bpp;
 
-              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--;
-                  }
-                }
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
             }
-        }
-      rasterizer->edge_pos++;
+          }
+          if (count)
+          {
+            int recip = 65536/count;
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c] * recip >> 16;
+          }
+          ctx_RGBA8_associate_alpha_probably_opaque (rgba);
     }
-
-    ctx_rasterizer_discard_edges (rasterizer);
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-CTX_INLINE static int ctx_compare_edges2 (const void *ap, const void *bp)
+static void
+ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  const CtxEdge *a = (const CtxEdge *) ap;
-  const CtxEdge *b = (const CtxEdge *) bp;
-  return a->val - b->val;
+  ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-CTX_INLINE static void ctx_edge2_insertion_sort (CtxEdge *entries, int count)
+static void
+ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  for(int i=1; i<count; i++)
-   {
-     CtxEdge temp = entries[i];
-     int j = i-1;
-     while (j >= 0 && ctx_compare_edges2 (&temp, &entries[j])<0)
-     {
-       entries[j+1] = entries[j];
-       j--;
-     }
-     entries[j+1] = temp;
-   }
+  uint8_t *rgba = (uint8_t *) out;
+
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  int width = buffer->width;
+  int height = buffer->height;
+
+  for (int i = 0; i < count; i++)
+  {
+
+  int u = x;
+  int v = y;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
+    {
+      *((uint32_t*)(rgba))= 0;
+    }
+  else
+    {
+      int bpp = 3;
+      rgba[3]=255;
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      int stride = buffer->stride;
+      src00 += v * stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < height)
+      {
+        src10 = src00 + stride;
+        src11 = src01 + stride;
+      }
+      float dx = (x-(int)(x)) * 255.9f;
+      float dy = (y-(int)(y)) * 255.9f;
+      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);
+      }
+      ctx_RGBA8_associate_alpha_probably_opaque (rgba);
+    }
+    x += dx;
+    y += dy;
+    rgba += 4;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-static inline void ctx_rasterizer_sort_active_edges (CtxRasterizer *rasterizer)
+static void
+ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  ctx_edge2_insertion_sort (rasterizer->edges, rasterizer->active_edges);
+  ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-#undef CTX_CMPSWP
-
-void ctx_coverage_post_process (CtxRasterizer *rasterizer, int minx, int maxx, uint8_t *coverage)
+static CTX_INLINE void
+ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                       float x,
+                                       float y,
+                                       void *out, int count, float dx, float dy)
 {
-  int scanline     = rasterizer->scanline;
-#if CTX_ENABLE_SHADOW_BLUR
-  if (CTX_UNLIKELY(rasterizer->in_shadow))
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
+  if (buffer->color_managed)
+   buffer = buffer->color_managed;
+  uint8_t *rgba = (uint8_t *) out;
+  uint8_t *src = (uint8_t *) buffer->data;
+  int bwidth = buffer->width;
+  int bheight = buffer->height;
+  int stride = buffer->stride;
+
+  x += 0.5f;
+  y += 0.5f;
+
+  if (CTX_UNLIKELY (dy == 0.0f && dx > 0.999f && dx < 1.001f))
   {
-    float radius = rasterizer->state->gstate.shadow_blur;
-    int dim = 2 * radius + 1;
-    if (CTX_UNLIKELY (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM))
-      dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+    int v = y;
+    int u = x;
+  
+    if (v < buffer->height && v > 0)
     {
-      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;
+      int o = v * stride + u * 3;
+      int i;
+      for (i = 0; i < count && u < bwidth && u <0; i++)
+      {
+        *((uint32_t*)(rgba))= 0;
+        rgba += 4;
+        o += 3;
+        u+=1;
+      }
+
+      for (; i < count && u < bwidth; i++)
+      {
+        rgba[0] = src[o];
+        rgba[1] = src[o+1];
+        rgba[2] = src[o+2]; 
+        rgba[3]=255;
+        rgba += 4;
+        o += 3;
+        u+=1;
+      }
+      for (; i < count; i++)
+      {
+        *((uint32_t*)(rgba))= 0;
+        rgba += 4;
+      }
+    }
+    else
+    {
+      for (int i = 0; i < count; i++)
+      {
+        *((uint32_t*)(rgba))= 0;
+        rgba+=4;
+      }
     }
   }
+  else
+  {
+    int u = x;
+    int v = y;
+    int i;
+    for (i = 0; i < count && u < bwidth && u <0; i++)
+    {
+      u = x;
+      v = y;;
+      *((uint32_t*)(rgba))= 0;
+      x += dx;
+      y += dy;
+      rgba += 4;
+    }
+    for (; i < count && u < bwidth; i++)
+    {
+      u = x;
+      v = y;
+    if (CTX_UNLIKELY(v < 0 || v >= bheight))
+      {
+        *((uint32_t*)(rgba))= 0;
+      }
+    else
+      {
+        int o = v * stride + u * 3;
+        rgba[0] = src[o];
+        rgba[1] = src[o+1];
+        rgba[2] = src[o+2]; 
+        rgba[3]=255;
+      }
+  
+      rgba += 4;
+      x += dx;
+      y += dy;
+    }
+      for (; i < count; i++)
+      {
+        *((uint32_t*)(rgba))= 0;
+        rgba += 4;
+      }
+  }
+#if CTX_DITHER
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
 #endif
+}
 
-#if CTX_ENABLE_CLIP
-  if (CTX_UNLIKELY(rasterizer->clip_buffer &&  !rasterizer->clip_rectangle))
+
+static CTX_INLINE void
+ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
+                                                      float x,
+                                                      float y,
+                                                      void *out, int count, float dx, float dy)
+{
+  ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
+}
+
+static void
+ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer,
+                               float x,
+                               float y,
+                               void *out, int count, float dx, float dy)
+{
+  if (rasterizer->state->gstate.image_smoothing)
   {
-    /* perhaps not working right for clear? */
-    int y = scanline / 15;//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 ++)
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor <= 0.50f)
     {
-#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;
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+    }
+#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
+    else if (factor > 0.99f && factor < 1.01f)
+    {
+      // XXX missing translate test
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+    }
 #endif
+    else
+    {
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
     }
   }
-  if (CTX_UNLIKELY(rasterizer->aa == 1))
+  else
   {
-    for (int x = minx; x <= maxx; x ++)
-     coverage[x] = coverage[x] > 127?255:0;
+    if (rasterizer->swap_red_green)
+      ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+    else
+      ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  }
+#if CTX_DITHER
+  {
+  uint8_t *rgba = (uint8_t*)out;
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
   }
 #endif
 }
 
-inline static void
-ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
-                                  int            minx,
-                                  int            maxx,
-                                  uint8_t       *coverage,
-                                  int            winding,
-                                  int            aa_factor)
-{
-  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]-1)
-#define CTX_EDGE_X(no)    (edges[no].val)
-  for (int t = 0; t < active_edges -1;t++)
-    {
-      int ymin = CTX_EDGE_YMIN (t);
-      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);
-          int x1 = CTX_EDGE_X (t+1);
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart / 256;
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int last      = grayend / 256;
-
-          if (CTX_UNLIKELY(first < minx))
-          { 
-            first = minx;
-            graystart=0;//255;
-          }
-          if (CTX_UNLIKELY(last > maxx))
-          {
-            last = maxx;
-            grayend=255;
-          }
-
-          graystart=fraction- (graystart&0xff)/aa_factor;
-          grayend = (grayend & 0xff) / aa_factor;
 
-          if (first == last)
-            coverage[first] += (graystart-(fraction-grayend));
-          else if (first < last)
-          {
-              coverage[first] += graystart;
-              for (int x = first + 1; x < last; x++)
-                coverage[x] += fraction;
-              coverage[last]  += grayend;
-          }
-        }
-   }
-}
+/************** rgba8 */
 
-inline static void
-ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer,
-                                  int            minx,
-                                  int            maxx,
-                                  uint8_t       *coverage,
-                                  int            winding,
-                                  int            aa_factor)
+static void
+ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  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]-1)
-#define CTX_EDGE_X(no)    (edges[no].val)
-  for (int t = 0; t < active_edges -1;t++)
-    {
-      int ymin = CTX_EDGE_YMIN (t);
-      if (scanline != ymin)
-        {
-          if (winding)
-            { parity += ( (CTX_EDGE (t).code == CTX_EDGE_FLIPPED) ?1:-1); }
-          else
-            { parity = 1 - parity; }
-        }
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
 
-       if (parity)
-        {
-          int x0 = CTX_EDGE_X (t);
-          int x1 = CTX_EDGE_X (t+1);
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart / 256;
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int last      = grayend / 256;
+  for (int i = 0; i < count; i ++)
+  {
 
-          if (CTX_UNLIKELY(first < minx))
-          { 
-            first = minx;
-            graystart=0;//255;
-          }
-          if (CTX_UNLIKELY(last > maxx))
+  int u = x;
+  int v = y;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
+    {
+      *((uint32_t*)(rgba))= 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;
+          int width = buffer->width;
+          int height = buffer->height;
+          for (int ou = - dim; ou < dim; ou++)
+          for (int ov = - dim; ov < dim; ov++)
           {
-            last = maxx;
-            grayend=255;
-          }
 
-          graystart=fraction- (graystart&0xff)/aa_factor;
-          grayend = (grayend & 0xff) / aa_factor;
+            if (v+ov >= 0 && u+ou >=0 && u + ou < width && v + ov < height)
+            {
+              int o = (v+ov) * width + (u + ou);
+              uint8_t *src = (uint8_t *) buffer->data + o * bpp;
 
-          if (first == last)
-            coverage[first] += (graystart-(fraction-grayend));
-          else if (first < last)
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
+            }
+          }
+          if (count)
           {
-              coverage[first] += graystart;
-              for (int x = first + 1; x < last; x++)
-                coverage[x] = fraction;
-              coverage[last]  += grayend;
+            int recip = 65536/count;
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c]*recip>>16;
           }
-        }
-   }
+          ctx_RGBA8_associate_alpha_probably_opaque (rgba);
+    }
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-#undef CTX_EDGE_Y0
-#undef CTX_EDGE
 
 static void
-ctx_rasterizer_reset (CtxRasterizer *rasterizer)
+ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                        float x,
+                                        float y,
+                                        void *out, int count, float dx, float dy)
 {
-  rasterizer->pending_edges   = 0;
-  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_aa3       = 0;
-  rasterizer->needs_aa5       = 0;
-  rasterizer->needs_aa15      = 0;
-  rasterizer->scanline        = 0;
-  if (!rasterizer->preserve)
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
+  if (buffer->color_managed)
+    buffer = buffer->color_managed;
+  int ideltax = dx * 65536;
+  int ideltay = dy * 65536;
+  uint32_t *src = (uint32_t *) buffer->data;
+  uint32_t *dst = (uint32_t*)out;
+  int bwidth  = buffer->width;
+  int bheight = buffer->height;
+  x += 0.5f;
+  y += 0.5f;
+
+#if 1
+  if (CTX_UNLIKELY(ideltay == 0 && ideltax == 65536))
   {
-    rasterizer->scan_min      = 5000;
-    rasterizer->scan_max      = -5000;
-    rasterizer->col_min       = 5000;
-    rasterizer->col_max       = -5000;
+    int i = 0;
+    int u = x;
+    int v = y;
+    if (!(v >= 0 && v < bheight))
+    {
+      for (i = 0 ; i < count; i++)
+        *dst++ = 0;
+      return;
+    }
+    src += bwidth * v + u;
+    while (count && !(u >= 0))
+    {
+      *dst++ = 0;
+      src ++;
+      u++;
+      count--;
+    }
+    int limit = ctx_mini (count, bwidth - u);
+    if (limit>0)
+    {
+      memcpy (dst, src, limit * 4);
+      dst += limit;
+      i = limit;
+    }
+    for (;i < count; i++)
+      *dst++ = 0;
+    return;
   }
-  //rasterizer->comp_op       = NULL;
-}
-
-static void
-ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, int winding
-#if CTX_SHAPE_CACHE
-                                ,CtxShapeEntry *shape
 #endif
-                               )
-{
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-  int real_aa = rasterizer->aa;
+  {
+    int i = 0;
+    float u1 = x + dx * (count-1);
+    float v1 = y + dy * (count-1);
+    uint32_t *edst = ((uint32_t*)out)+count;
+    for (; i < count; )
+    {
+      if ((u1 < 0.0f || v1 < 0.0f || u1 >= bwidth || v1 >= bheight))
+      {
+        *edst-- = 0;
+        count --;
+        u1 -= dx;
+        v1 -= dy;
+      }
+      else break;
+    }
 
-  int scan_start = rasterizer->blit_y * CTX_FULL_AA;
-  int scan_end   = scan_start + rasterizer->blit_height * CTX_FULL_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)
+    for (i = 0; i < count; i ++)
     {
-      ctx_rasterizer_reset (rasterizer);
-      return;
+      if ((x < 0.0f || y < 0.0f || x >= bwidth || y >= bheight))
+      {
+        *dst = 0;
+        dst++;
+        x += dx;
+        y += dy;
+      }
+      else break;
     }
-#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];
 
 
-#if CTX_SHAPE_CACHE
-  if (shape)
+    uint32_t ix = x * 65536;
+    uint32_t iy = y * 65536;
+
+    for (; i < count; i ++)
     {
-      coverage = &shape->data[0];
+      *dst = src[(iy>>16) * bwidth + (ix>>16)];
+      ix += ideltax;
+      iy += ideltay;
+      dst++;
     }
-#endif
-  ctx_assert (coverage);
-  rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA);
-#if CTX_SHAPE_CACHE
-  if (shape)
-    {
-      scan_start = rasterizer->scan_min;
-      scan_end   = rasterizer->scan_max;
-    }
-  else
-#endif
+  }
+}
+
+
+
+static void
+ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out, int count, float dx, float dy)
+{
+  uint8_t *rgba = (uint8_t *) out;
+  float ox = (x-(int)(x));
+  float oy = (y-(int)(y));
+
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  const int bwidth = buffer->width;
+  const int bheight = buffer->height;
+  int i = 0;
+
+  if (dy == 0.0f && dx > 0.0f)
+  {
+    if ((dx > 0.99f && dx < 1.01f && 
+         ox < 0.01 && oy < 0.01) || y < 0 || y > bheight-1)
     {
-      if (rasterizer->scan_min > scan_start)
-        {
-          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA);
-          scan_start = rasterizer->scan_min;
-        }
-      if (rasterizer->scan_max < scan_end)
-        { scan_end = rasterizer->scan_max; }
+      /* TODO: this could have been rigged up in composite_setup */
+      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer,
+                                   x, y, out, count, dx, dy);
+      return;
     }
-  if (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start )
-    { 
-       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / 
CTX_FULL_AA);
-       scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; 
+    x+=1; // XXX off by one somewhere? ,, needed for alignment with nearest
+
+    uint32_t *data = ((uint32_t*)buffer->data);
+    uint32_t yi = y * 65536;
+    uint32_t xi = x * 65536;
+    int xi_delta = dx * 65536;
+
+    for (i= 0; i < count; i ++)
+    {
+      int u = xi >> 16;
+      if ( u  < 0 || u >= bwidth-1)
+      {
+        *((uint32_t*)(rgba))= 0;
+        xi += xi_delta;
+        rgba += 4;
+      }
+      else
+        break;
     }
-  if (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA < scan_end)
-    { scan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA; }
-  if (scan_start > scan_end ||
-      (scan_start > (rasterizer->blit_y + rasterizer->blit_height) * CTX_FULL_AA) ||
-      (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))
-  { 
-    /* not affecting this rasterizers scanlines */
-    ctx_rasterizer_reset (rasterizer);
-    return;
-  }
 
-  ctx_rasterizer_sort_edges (rasterizer);
+  int loaded = -4;
+  uint32_t s0_ga = 0, s0_rb = 0, s1_ga = 0, s1_rb = 0;
+ 
+  int v = yi >> 16;
+  data += bwidth * v;
+  int dv = (yi >> 8) & 0xff;
+
+  int u = xi >> 16;
+
+  uint32_t *ndata = data;
+  if (v < bheight-1) ndata += bwidth;
+
+  uint32_t *src0 = data, *src1 = ndata;
+
+
+  if (xi_delta == 65536 && u < bwidth -1)
   {
-    int halfstep2 = CTX_FULL_AA/2;
-    int halfstep  = halfstep2 + 1;
-    rasterizer->needs_aa3  = 0;
-    rasterizer->needs_aa5  = 0;
-    rasterizer->needs_aa15 = 0;
-    rasterizer->scanline = scan_start;
-    ctx_rasterizer_feed_edges (rasterizer); 
+    int du = (xi >> 8);
 
-  for (; rasterizer->scanline <= scan_end;)
-    {
-      int contains_edge_end = rasterizer->pending_edges ||
-                              rasterizer->ending_edges;
+    src0 = data + u;
+    src1 = ndata + u;
+    ctx_lerp_RGBA8_split (src0[0],src1[0], dv, &s1_ga, &s1_rb);
 
-      // check if all pending edges are scanline aligned at start
-      ctx_memset (coverage, 0,
-#if CTX_SHAPE_CACHE
-                  shape?shape->width:
-#endif
-                  sizeof (_coverage) );
+    int limit = bwidth-u;
+    limit = ctx_mini(count,limit);
 
-    if (contains_edge_end)
+    for (; i < limit; i ++)
     {
-        for (int i = 0; i < real_aa; i++)
-        {
-          ctx_rasterizer_feed_edges (rasterizer);
-          ctx_rasterizer_sort_active_edges (rasterizer);
-          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, real_aa);
-          ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA/real_aa);
-        }
+      s0_ga = s1_ga;
+      s0_rb = s1_rb;
+      ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb);
+      ((uint32_t*)(&rgba[0]))[0] = 
+      ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du);
+      rgba += 4;
+      u++;
+      src0 ++;
+      src1 ++;
     }
-    else if (!rasterizer->needs_aa3) // if it doesnt need aa3 it doesnt need aa5 or aa15 either
-    {
-      ctx_rasterizer_increment_edges (rasterizer, halfstep2);
-      ctx_rasterizer_feed_edges (rasterizer);
+  }
+  else
+  {
 
-      ctx_rasterizer_sort_active_edges (rasterizer);
-      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, winding, 1);
-      ctx_rasterizer_increment_edges (rasterizer, halfstep);
+  for (; i < count; i ++)
+  {
+    if (CTX_UNLIKELY(u >= bwidth-1))
+    {
+      break;
     }
-    else  if (rasterizer->needs_aa15)
-      {
-        for (int i = 0; i < CTX_FULL_AA; i++)
-        {
-          ctx_rasterizer_feed_edges (rasterizer);
-          ctx_rasterizer_sort_active_edges (rasterizer);
-          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA);
-          ctx_rasterizer_increment_edges (rasterizer, 1);
-        }
+    else if (CTX_LIKELY(loaded + 1 == u))
+    {
+      s0_ga = s1_ga;
+      s0_rb = s1_rb;
+      ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb);
+      src0 ++;
+      src1 ++;
     }
-    else if (rasterizer->needs_aa5)
+    else if (loaded != u)
     {
-      for (int i = 0; i < CTX_FULL_AA; i+=3)
-      {
-        ctx_rasterizer_feed_edges (rasterizer);
-        ctx_rasterizer_sort_active_edges (rasterizer);
-        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA/3);
-        ctx_rasterizer_increment_edges (rasterizer, 3);
-      }
-        //for (int x = minx; x <= maxx; x++) coverage[x-minx] *= 0.5;
+      src0 = data + u;
+      src1 = ndata + u;
+      ctx_lerp_RGBA8_split (src0[0],src1[0], dv, &s0_ga, &s0_rb);
+      ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb);
     }
-    else if (rasterizer->needs_aa3)
+    loaded = u;
+    ((uint32_t*)(&rgba[0]))[0] = 
+      ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8));
+    xi += xi_delta;
+    rgba += 4;
+    u = xi >> 16;
+  }
+  }
+
+  }
+  else
+  {
+    uint32_t *data = ((uint32_t*)buffer->data);
+    for (i= 0; i < count; i ++)
     {
-      for (int i = 0; i < CTX_FULL_AA; i+=5)
+      int u = x;
+      int v = y;
+      int ut = x + 1.5;
+      int vt = y + 1.5;
+      if ( ut  <= 0 || vt  <= 0 || u >= buffer->width || v >= buffer->height)
       {
-        ctx_rasterizer_feed_edges (rasterizer);
-        ctx_rasterizer_sort_active_edges (rasterizer);
-        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA/5);
-        ctx_rasterizer_increment_edges (rasterizer, 5);
+        *((uint32_t*)(rgba))= 0;
       }
-      //for (int x = minx; x <= maxx; x++) coverage[x-minx] *= 0.75;
+      else
+        break;
+      x += dx;
+      y += dy;
+      rgba += 4;
     }
-    else
-    {
-      ctx_rasterizer_increment_edges (rasterizer, halfstep2);
-      ctx_rasterizer_feed_edges (rasterizer);
 
-      ctx_rasterizer_sort_active_edges (rasterizer);
-      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, winding, 1);
-      ctx_rasterizer_increment_edges (rasterizer, halfstep);
-    }
-  ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx);
+  uint32_t yi = y * 65536;
+  uint32_t xi = x * 65536;
 
+  int yi_delta = dy * 65536;
+  int xi_delta = dx * 65536;
 
+  int loaded = -4;
+  uint32_t *src00=data;
+  uint32_t *src01=data;
+  uint32_t *src10=data;
+  uint32_t *src11=data;
 
+  int u = xi >> 16;
+  int v = yi >> 16;
+  int offset = bwidth * v + u;
 
-        {
-#if CTX_SHAPE_CACHE
-          if (shape == NULL)
-#endif
-            {
-#if 0
-              if (CTX_FULL_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);
-            }
-        }
-#if CTX_SHAPE_CACHE
-      if (shape)
-        {
-          coverage += shape->width;
-        }
-#endif
-      dst += rasterizer->blit_stride;
+  for (; i < count; i ++)
+  {
+  if (CTX_UNLIKELY(
+       u >= buffer->width ||
+       v  <= -65536 ||
+       u  <= -65536 ||
+       v >= buffer->height))
+    {
+      break;
     }
+#if 1
+  else if (CTX_UNLIKELY(u < 0 || v < 0)) // default to next sample down and to right
+  {
+      int got_prev_pix = (u >= 0);
+      int got_prev_row = (v>=0);
+      src11 = data  + offset + bwidth + 1;
+      src10 = src11 - got_prev_pix;
+      src01 = src11 - bwidth * got_prev_row;
+      src00 = src10 - bwidth * got_prev_row;
   }
-
-  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)
+#endif
+#if 1
+  else if (loaded + 1 == offset)
   {
-     /* 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 * CTX_FULL_AA;
-     int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
-     int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_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);
-
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_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 += CTX_FULL_AA;
-       dst += rasterizer->blit_stride;
-     }
-     if (minx < startx)
-     {
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_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 / CTX_FULL_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);
+      src00++;
+      src01++;
+      src10++;
+      src11++;
+  }
+#endif
+  else if (loaded != offset)
+  {
+      int next_row = ( v + 1 < bheight) * bwidth;
+      int next_pix = (u + 1 < bwidth);
+      src00 = data  + offset;
+      src01 = src00 + next_pix;
+      src10 = src00 + next_row;
+      src11 = src01 + next_row;
+  }
+    loaded = offset;
+    ((uint32_t*)(&rgba[0]))[0] = ctx_bi_RGBA8 (*src00,*src01,*src10,*src11, (xi>>8),(yi>>8)); // the 
argument type does the & 0xff
+    xi += xi_delta;
+    yi += yi_delta;
+    rgba += 4;
 
-       rasterizer->scanline += CTX_FULL_AA;
-       dst += rasterizer->blit_stride;
-     }
-     }
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA);
-     // XXX valgrind/asan this
-     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, clipw-1);
+    u = xi >> 16;
+    v = yi >> 16;
+    offset = bwidth * v + u;
+  }
+  }
 
-       rasterizer->scanline += CTX_FULL_AA;
-       dst += rasterizer->blit_stride;
-     }
+  for (; i < count; i ++)
+  {
+    *((uint32_t*)(rgba))= 0;
+    rgba += 4;
   }
-  ctx_rasterizer_reset (rasterizer);
 }
 
-inline static int
-ctx_is_transparent (CtxRasterizer *rasterizer, int stroke)
+#define ctx_clampi(val,min,max) \
+     ctx_mini (ctx_maxi ((val), (min)), (max))
+
+static inline uint32_t ctx_yuv_to_rgba32 (uint8_t y, uint8_t u, uint8_t v)
 {
-  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;
+  int cy  = ((y - 16) * 76309) >> 16;
+  int cr  = (v - 128);
+  int cb  = (u - 128);
+  int red = cy + ((cr * 104597) >> 16);
+  int green = cy - ((cb * 25674 + cr * 53278) >> 16);
+  int blue = cy + ((cb * 132201) >> 16);
+  return  ctx_clampi (red, 0, 255) |
+          (ctx_clampi (green, 0, 255) << 8) |
+          (ctx_clampi (blue, 0, 255) << 16) |
+          (0xff << 24);
 }
 
 static void
-ctx_rasterizer_fill (CtxRasterizer *rasterizer)
+ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                         float x,
+                                         float y,
+                                         void *out, int count, float dx, float dy)
 {
-  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
-
-  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) ); }
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
+  if (buffer->color_managed)
+    buffer = buffer->color_managed;
+  uint8_t *src = (uint8_t *) buffer->data;
+  int bwidth  = buffer->width;
+  int bheight = buffer->height;
+  int bwidth_div_2  = bwidth/2;
+  int bheight_div_2  = bheight/2;
+  x += 0.5f;
+  y += 0.5f;
 
-#if CTX_ENABLE_SHADOW_BLUR
-  if (rasterizer->in_shadow)
   {
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
+    int i = 0;
+
+    for (; i < 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 * CTX_FULL_AA;
+      int u = x;
+      int v = y;
+      if ((u < 0 || v < 0 || u >= bwidth || v >= bheight))
+      {
+        *((uint32_t*)(rgba))= 0;
+      }
+      else
+      {
+        break;
+      }
+      x += dx;
+      y += dy;
+      rgba += 4;
     }
-    rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_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 (ctx_is_transparent (rasterizer, 0) ||
-      rasterizer->scan_min / CTX_FULL_AA > rasterizer->blit_y + rasterizer->blit_height ||
-      rasterizer->scan_max / CTX_FULL_AA < rasterizer->blit_y ||
-      rasterizer->col_min / CTX_SUBDIV > rasterizer->blit_x + rasterizer->blit_width ||
-      rasterizer->col_max / CTX_SUBDIV < rasterizer->blit_x)
+    uint32_t u_offset = bheight * bwidth;
+    uint32_t v_offset = u_offset + bheight_div_2 * bwidth_div_2;
+    if (rasterizer->swap_red_green)
     {
-      ctx_rasterizer_reset (rasterizer);
+      v_offset = bheight * bwidth;
+      u_offset = v_offset + bheight_div_2 * bwidth_div_2;
     }
-  else
-  {
-    if (rasterizer->comp_op == NULL)
-      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 / CTX_FULL_AA);
-    rasterizer->state->max_y =
-      ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / CTX_FULL_AA);
-
-    ctx_rasterizer_finish_shape (rasterizer);
+    int ix = x * 65536;
+    int iy = y * 65536;
 
-    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
-    if (hash){};
+    int ideltax = dx * 65536;
+    int ideltay = dy * 65536;
 
-#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 + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / 
CTX_FULL_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
-        )
+    for (; i < count; i ++)
+    {
+      int u = ix >> 16;
+      int v = iy >> 16;
+      if (u >= 0 && v >= 0 && u < bwidth && v < bheight)
       {
-        int scan_min = rasterizer->scan_min;
-        int col_min = rasterizer->col_min;
-        scan_min -= (scan_min % CTX_FULL_AA);
-        int y0 = scan_min / CTX_FULL_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;
+        uint32_t y  = v * bwidth + u;
+        uint32_t uv = (v / 2) * bwidth_div_2 + (u / 2);
 
-        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_shape_cache_enabled)
-        {
-          ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
-#if CTX_SHAPE_CACHE
-                                        , NULL
+        *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y],
+                        //127, 127);
+                        src[u_offset+uv], src[v_offset+uv]);
+        //ctx_RGBA8_associate_alpha_probably_opaque (rgba);
+#if CTX_DITHER
+       ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                           rasterizer->format->dither_green);
 #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 += CTX_FULL_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 += CTX_FULL_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
-                                   );
-  }
-  if (rasterizer->preserve)
+      else
+      {
+        break;
+      }
+      ix += ideltax;
+      iy += ideltay;
+      rgba += 4;
+    }
+
+    for (; i < count; i++)
     {
-      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
+      *((uint32_t*)(rgba))= 0;
+      rgba += 4;
     }
-#if CTX_ENABLE_SHADOW_BLUR
-  if (rasterizer->in_shadow)
-  {
-    rasterizer->scan_min -= rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->scan_max -= rasterizer->shadow_y * CTX_FULL_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;
 }
 
-#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)
+ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-
+  ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
-#endif
-
 
-typedef struct _CtxTermGlyph CtxTermGlyph;
-
-struct _CtxTermGlyph
+static void
+ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  uint32_t unichar;
-  int      col;
-  int      row;
-  uint8_t  rgba_bg[4];
-  uint8_t  rgba_fg[4];
-};
+  ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
+}
 
-static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke);
 static void
-ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
+ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  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;
+  ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
+}
 
-  if (rasterizer->term_glyphs)
+static void
+ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer,
+                                float x,
+                                float y,
+                                void *out, int count, float dx, float dy)
+{
+  if (rasterizer->state->gstate.image_smoothing)
   {
-    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;
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor <= 0.50f)
+    {
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+    }
+#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
+    else if (factor > 0.99f && factor < 1.01f)
+    {
+      // XXX: also verify translate == 0 for this fast path to be valid
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+    }
+#endif
+    else
+    {
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+    }
   }
-  if (rasterizer->term_glyphs && !stroke &&
-      fabs (font_size - ch) < 0.5)
+  else
   {
-    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]);
+    if (rasterizer->swap_red_green)
+      ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+    else
+      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
   }
-  else
+  //ctx_fragment_swap_red_green_u8 (out, count);
+#if CTX_DITHER
+  uint8_t *rgba = (uint8_t*)out;
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
 #endif
-  _ctx_glyph (rasterizer->ctx, unichar, stroke);
 }
 
-
 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)
+ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-#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)
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
+  ctx_assert (rasterizer);
+  ctx_assert (g);
+  ctx_assert (buffer);
+  for (int i = 0; i < count; i ++)
   {
-    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++)
+  int u = x;
+  int v = y;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
     {
-      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);
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
     }
-  }
   else
-#endif
-  {
-    _ctx_text (rasterizer->ctx, string, stroke, 1);
+    {
+      uint8_t *src = (uint8_t *) buffer->data;
+      src += v * buffer->stride + u / 8;
+      if (*src & (1<< (u & 7) ) )
+        {
+          rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+        }
+      else
+        {
+          for (int c = 0; c < 4; c++)
+            { rgba[c] = 255;
+            }//g->texture.rgba[c];
+            //}
+        }
+    }
+
+    rgba += 4;
+    x += dx;
+    y += dy;
   }
 }
 
-void
-_ctx_font (Ctx *ctx, const char *name);
+#if CTX_GRADIENTS
 static void
-ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
+ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  _ctx_font (rasterizer->ctx, font_name);
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i <  count; i ++)
+  {
+    float v = (ctx_hypotf_fast (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
+              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
+#if CTX_GRADIENT_CACHE
+    uint32_t *rgbap = (uint32_t*)&ctx_gradient_cache_u8[ctx_grad_index(v)][0];
+    *((uint32_t*)rgba) = *rgbap;
+#else
+    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
+#endif
+#if CTX_DITHER
+    ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                        rasterizer->format->dither_green);
+#endif
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
 }
 
 static void
-ctx_rasterizer_arc (CtxRasterizer *rasterizer,
-                    float        x,
-                    float        y,
-                    float        radius,
-                    float        start_angle,
-                    float        end_angle,
-                    int          anticlockwise)
+ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  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;
+#if 0
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i <  count; i ++)
+  {
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
+#if CTX_GRADIENT_CACHE
+  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0]));
+  *((uint32_t*)rgba) = *rgbap;
+#else
+  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
+#endif
+#if CTX_DITHER
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
+#else
+  uint8_t *rgba = (uint8_t *) out;
 
-  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;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float u0 = x; float v0 = y;
+  float ud = dx; float vd = dy;
+  float linear_gradient_rdelta = g->linear_gradient.rdelta;
+  float linear_gradient_length = g->linear_gradient.length;
+  float linear_gradient_length_recip = 1.0f/linear_gradient_length;
+  float linear_gradient_dx = g->linear_gradient.dx *linear_gradient_length_recip * linear_gradient_rdelta;
+  float linear_gradient_dy = g->linear_gradient.dy *linear_gradient_length_recip * linear_gradient_rdelta;
+  float linear_gradient_start = g->linear_gradient.start * linear_gradient_rdelta;
 
-  if (radius <= 0.0001)
-          return;
+#if CTX_DITHER
+  int dither_red_blue = rasterizer->format->dither_red_blue;
+  int dither_green = rasterizer->format->dither_green;
+#endif
 
-  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
+  u0 *= linear_gradient_dx;
+  v0 *= linear_gradient_dy;
+  ud *= linear_gradient_dx;
+  vd *= linear_gradient_dy;
+
+#if CTX_GRADIENT_CACHE
+  int vv = ((u0 + v0) - linear_gradient_start) * (CTX_GRADIENT_CACHE_ELEMENTS-1) * 256;
+  int ud_plus_vd = (ud + vd) * (CTX_GRADIENT_CACHE_ELEMENTS-1) * 256;
+#else
+  float vv = ((u0 + v0) - linear_gradient_start);
+  float ud_plus_vd = (ud + vd);
+#endif
+
+  for (int x = 0; x < count ; x++)
+  {
+#if CTX_GRADIENT_CACHE
+  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index_i (vv)][0]));
+  *((uint32_t*)rgba) = *rgbap;
+#else
+  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba);
+#endif
+#if CTX_DITHER
+      ctx_dither_rgba_u8 (rgba, u0, v0, dither_red_blue, dither_green);
+#endif
+    rgba+= 4;
+    vv += ud_plus_vd;
+  }
 #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);
 }
 
+#endif
+
 static void
-ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
-                        float        cx,
-                        float        cy,
-                        float        x,
-                        float        y)
+ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
 {
-  /* 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);
+  uint8_t *rgba_out = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  _ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba_out);
+  ctx_RGBA8_associate_alpha (rgba_out);
+  if (rasterizer->swap_red_green)
+  {
+    int tmp = rgba_out[0];
+    rgba_out[0] = rgba_out[2];
+    rgba_out[2] = tmp;
+  }
+  for (int i = 1; i < count; i++, rgba_out+=4)
+    memcpy (rgba_out + count * 4, rgba_out, 4);
 }
+#if CTX_ENABLE_FLOAT
 
+#if CTX_GRADIENTS
 static void
-ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
-                            float cx, float cy,
-                            float x,  float y)
+ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
-                          x  + rasterizer->x, y  + rasterizer->y);
+  float *rgba = (float *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
+  {
+    float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                  g->linear_gradient.length) -
+                g->linear_gradient.start) * (g->linear_gradient.rdelta);
+    ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba);
+    x += dx;
+    y += dy;
+    rgba += 4;
+  }
 }
 
-#define LENGTH_OVERSAMPLE 1
 static void
-ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
+ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  // 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_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
+  float *rgba = (float *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
+  {
+  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);
+    x+=dx;
+    y+=dy;
+    rgba +=4;
+  }
+}
+#endif
+
+
+static void
+ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+{
+  float *rgba = (float *) out;
+  for (int i = 0; i < count; i++)
+  {
+    CtxSource *g = &rasterizer->state->gstate.source_fill;
+    ctx_color_get_rgba (rasterizer->state, &g->color, rgba);
+    for (int c = 0; c < 3; c++)
+      rgba[c] *= rgba[3];
+    rgba += 4;
+  }
+}
+
+static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+  float *outf = (float *) out;
+  uint8_t rgba[4];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
     {
-      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);
-        }
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
     }
-  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
+  for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); }
 }
 
-static void
-ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
+static CtxFragment ctx_rasterizer_get_fragment_RGBAF (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 = 15;//rasterizer->aa;
-  int start = 0;
-  int end = 0;
-#if 0
-  float factor = ctx_matrix_get_scale (&state->gstate.transform);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+    {
+      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;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF;
+#endif
+    }
+  return ctx_fragment_color_RGBAF;
+}
 #endif
 
-  while (start < count)
+static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (gstate->source_fill.type)
     {
-      int started = 0;
-      int i;
-      for (i = start; i < count; i++)
+      case CTX_SOURCE_TEXTURE:
+        if (!buffer || !buffer->format)
+          return ctx_fragment_color_RGBA8;
+
+        if (buffer->format->pixel_format == CTX_FORMAT_YUV420)
         {
-          CtxEntry *entry = &temp[i];
-          float x, y;
-          if (entry->code == CTX_NEW_EDGE)
-            {
-              if (started)
+          return ctx_fragment_image_yuv420_RGBA8_nearest;
+        }
+        else
+        switch (buffer->format->bpp)
+          {
+            case 1:  return ctx_fragment_image_gray1_RGBA8;
+            case 24: 
+              {
+                if (gstate->image_smoothing)
                 {
-                  end = i - 1;
-                  goto foo;
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                          //fprintf (stderr, "{%.3f}", factor);
+                  if (factor < 0.5f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_box;
+                  }
+#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
+                  else if (factor > 0.99f && factor < 1.01f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_nearest;
+                  }
+#endif
+                  else
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_bi;
+                  }
                 }
-              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++)
+                else
                 {
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
-                  tx += dx;
-                  ty += dy;
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
+                  if (rasterizer->swap_red_green)
+                    return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
+                  return ctx_fragment_image_rgb8_RGBA8_nearest;
                 }
-            }
-          prev_x = x;
-          prev_y = y;
-        }
-      end = i-1;
-foo:
-      start = end+1;
+              }
+              break;
+            case 32:
+              {
+                if (gstate->image_smoothing)
+                {
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                          //fprintf (stderr, "[%.3f]", factor);
+                  if (factor < 0.5f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_box;
+                  }
+#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
+                  else if (factor > 0.99f && factor < 1.01f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_nearest;
+                  }
+#endif
+                  else
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_bi;
+                  }
+                }
+                else
+                {
+                  if (rasterizer->swap_red_green)
+                    return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
+                  return ctx_fragment_image_rgba8_RGBA8_nearest;
+                }
+              }
+            default: return ctx_fragment_image_RGBA8;
+          }
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBA8;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8;
+#endif
     }
-  ctx_rasterizer_reset (rasterizer);
+  return ctx_fragment_color_RGBA8;
 }
 
 static void
-ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
+ctx_init_uv (CtxRasterizer *rasterizer,
+             int x0, int count,
+             float *u0, float *v0, float *ud, float *vd)
 {
-  CtxSource source_backup = rasterizer->state->gstate.source_fill;
-  if (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_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);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  *u0 = x0;
+  *v0 = rasterizer->scanline / 15;//rasterizer->aa;
+  float u1 = *u0 + count;
+  float v1 = *v0;
 
-  int aa = 15;//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)
-    {
-      ctx_rasterizer_stroke_1px (rasterizer);
-    }
-  else
-#endif
+  _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);
+}
+
+
+static void
+ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  if (CTX_UNLIKELY(rasterizer->fragment))
     {
-      factor *= 0.86; /* this hack adjustment makes sharp 1px and 2px strokewidths
-                            end up sharp without erronious AA
-                       */
-      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)
+      float u0 = 0; float v0 = 0;
+      float ud = 0; float vd = 0;
+      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+      while (count--)
+      {
+        uint8_t cov = *coverage;
+        if (CTX_UNLIKELY(cov == 0))
         {
-          half_width_x = .5;
-          half_width_y = .5;
+          u0+=ud;
+          v0+=vd;
         }
-      int start = 0;
-      int end   = 0;
-      while (start < count)
+        else
         {
-          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
-
-                  // 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);
-
-
-                  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: incorrect - if rectangles were in
-                               //                  reverse order - rotation would be off
-                               //                  better implement correct here
-            {
-              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_rectangle (rasterizer, x - half_width_x, y - half_width_y, 
half_width_x, half_width_y);
-                          ctx_rasterizer_finish_shape (rasterizer);
-                        }
-                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                      y = entry->data.s16[1] * 1.0f / aa;
-                      ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x 
* 2, half_width_y * 2);
-                      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_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, 
half_width_y * 2);
-              ctx_rasterizer_finish_shape (rasterizer);
-            }
-            break;
-          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;
-            }
+          rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
+          u0+=ud;
+          v0+=vd;
+          if (cov == 255)
+          {
+            for (int c = 0; c < components; c++)
+              dst[c] = src[c];
+          }
+          else
+          {
+            uint8_t rcov = 255 - cov;
+            for (int c = 0; c < components; c++)
+              { dst[c] = (src[c]*cov + dst[c]*rcov)/255; }
+          }
         }
-      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;
+        dst += components;
+        coverage ++;
+      }
+      return;
     }
-  rasterizer->state->gstate.source_fill = source_backup;
+
+  while (count--)
+  {
+    uint8_t cov = *coverage;
+    uint8_t rcov = 255-cov;
+    for (int c = 0; c < components; c++)
+      { dst[c] = (src[c]*cov+dst[c]*rcov)/255; }
+    dst += components;
+    coverage ++;
+  }
 }
 
-#if CTX_1BIT_CLIP
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1
-#else
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8
-#endif
+static void
+ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  while (count--)
+  {
+    uint8_t cov = *coverage;
+    for (int c = 0; c < components; c++)
+      { dst[c] = (dst[c] * (256-cov)) >> 8; }
+    coverage ++;
+    dst += components;
+  }
+}
+
+typedef enum {
+  CTX_PORTER_DUFF_0,
+  CTX_PORTER_DUFF_1,
+  CTX_PORTER_DUFF_ALPHA,
+  CTX_PORTER_DUFF_1_MINUS_ALPHA,
+} CtxPorterDuffFactor;
 
+#define  \
+ctx_porter_duff_factors(mode, foo, bar)\
+{\
+  switch (mode)\
+  {\
+     case CTX_COMPOSITE_SOURCE_ATOP:\
+        f_s = CTX_PORTER_DUFF_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_1;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+        f_s = CTX_PORTER_DUFF_1;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1;\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+        f_s = CTX_PORTER_DUFF_ALPHA;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+        f_s = CTX_PORTER_DUFF_1;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     default:\
+     case CTX_COMPOSITE_CLEAR:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+  }\
+}
 
 static void
-ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer)
+ctx_u8_source_over_normal_color (int components,
+                                 CtxRasterizer         *rasterizer,
+                                 uint8_t * __restrict__ dst,
+                                 uint8_t * __restrict__ src,
+                                 int                    x0,
+                                 uint8_t * __restrict__ coverage,
+                                 int                    count)
 {
-#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;
+  uint8_t tsrc[5];
+  *((uint32_t*)tsrc) = *((uint32_t*)src);
 
-  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;
+  while (count--)
+  {
+    for (int c = 0; c < components; c++)
+      //dst[c] =  ((tsrc[c] * *coverage)>>8) + (dst[c] * (((65536)-(tsrc[components-1] * *coverage)))>>16);
+      dst[c] =  ((((tsrc[c] * *coverage)) + (dst[c] * (((255)-(((255+(tsrc[components-1] * 
*coverage))>>8))))))>>8);
+    coverage ++;
+    dst+=components;
+  }
 }
 
 static void
-ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
-                           CtxEntry      *edges)
+ctx_u8_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
 {
-  int count = edges[0].data.u32[0];
-
-  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;
+  while (count--)
+  {
+    for (int c = 0; c < components; c++)
+      dst[c] =  ctx_lerp_u8(dst[c],src[c],coverage[0]);
+    coverage ++;
+    dst+=components;
+  }
+}
 
-  int aa = 15;//rasterizer->aa;
-  float coords[6][2];
+static inline void
+ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+{
+  while (count--)
+  {
+     uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8;
+     uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff;
+//     uint32_t di_ga = ((*((uint32_t*)dst)) & 0xff00ff00) >> 8;
+//     uint32_t di_rb = (*((uint32_t*)dst)) & 0x00ff00ff;
+     uint32_t si_a  = si_ga >> 16;
+     uint32_t cov = *coverage;
+     uint32_t racov = (255-((255+si_a*cov)>>8));
+     *((uint32_t*)(dst)) =
 
-  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; }
+     (((si_rb*cov+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)|
+     ((si_ga*cov+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00);
 
-      if (i < 6)
-      {
-        coords[i][0] = x;
-        coords[i][1] = y;
-      }
-    }
+     coverage ++;
+     tsrc += 4;
+     dst  += 4;
+  }
+}
 
-#if CTX_ENABLE_CLIP
+static inline void
+ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+{
+  while (count--)
+  {
+     uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8;
+     uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff;
+     uint32_t si_a  = si_ga >> 16;
+     uint32_t racov = (255-si_a);
+     *((uint32_t*)(dst)) =
+     (((si_rb*255+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)|
+     ((si_ga*255+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00);
+     tsrc += 4;
+     dst  += 4;
+  }
+}
 
-  if ((rasterizer->clip_rectangle==1
-                          || !rasterizer->clip_buffer)
-                  )
-  //  XXX  disabled, it makes clip test fail, a lot of unneded work
-  //  can be skipped here.
+static void
+ctx_RGBA8_source_copy_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+{
+  while (count--)
   {
-    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
+    ((uint32_t*)dst)[0]=ctx_lerp_RGBA8 (((uint32_t*)dst)[0],
+                                        ((uint32_t*)tsrc)[0], coverage[0]);
+    coverage ++;
+    tsrc += 4;
+    dst  += 4;
+  }
+}
 
-         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 void
+ctx_RGBA8_source_over_normal_fragment (CTX_COMPOSITE_ARGUMENTS)
+{
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  uint8_t _tsrc[4 * (count)];
+  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
+  ctx_RGBA8_source_over_normal_buf (rasterizer,
+                       dst, src, x0, coverage, count, &_tsrc[0]);
+}
 
-         rasterizer->clip_rectangle = 1;
+static inline void
+ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS)
+{
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  uint8_t _tsrc[4 * (count)];
+  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
+  ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer,
+                       dst, src, x0, coverage, count, &_tsrc[0]);
+}
 
-#if 0
-         if (!rasterizer->clip_buffer)
-           rasterizer->clip_buffer = ctx_buffer_new (blit_width,
-                                                     blit_height,
-                                                     CTX_CLIP_FORMAT);
+static void
+ctx_RGBA8_source_copy_normal_fragment (CTX_COMPOSITE_ARGUMENTS)
+{
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  uint8_t _tsrc[4 * (count)];
+  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
+  ctx_RGBA8_source_copy_normal_buf (rasterizer,
+                       dst, src, x0, coverage, count, &_tsrc[0]);
+}
 
-         memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
-         int i = 0;
-         for (int y = rasterizer->state->gstate.clip_min_y;
-                  y <= rasterizer->state->gstate.clip_max_y;
-                  y++)
-         for (int x = rasterizer->state->gstate.clip_min_x;
-                  x <= rasterizer->state->gstate.clip_max_x;
-                  x++, i++)
-         {
-           ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255;
-         }
-#endif
 
-         return;
-      }
-    }
-  }
-  rasterizer->clip_rectangle = 0;
+static void
+ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
+{
+#if CTX_REFERENCE
+  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, x0, coverage, count);
+#else
+  uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
+  uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
+  uint32_t si_a  = si_ga >> 16;
 
-  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
+  while (count--)
   {
-    ctx_rasterizer_clip_reset (rasterizer);
-    return;//goto done;
+     uint32_t cov   = *coverage++;
+     uint32_t rcov  = (((255+si_a * cov)>>8))^255;
+     uint32_t di    = *((uint32_t*)dst);
+     uint32_t di_ga = ((di & 0xff00ff00) >> 8);
+     uint32_t di_rb = (di & 0x00ff00ff);
+     *((uint32_t*)(dst)) =
+     (((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8)  |
+      ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00);
+     dst+=4;
   }
+#endif
+}
 
-  int we_made_it = 0;
-  CtxBuffer *clip_buffer;
-
-  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);
-  }
+static void
+ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
+{
+#if CTX_REFERENCE
+  ctx_u8_source_copy_normal_color (4, rasterizer, dst, src, x0, coverage, count);
+#else
+  uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
+  uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
 
+  while (count--)
   {
+     uint32_t cov   = *coverage++;
+     uint32_t di    = *((uint32_t*)dst);
+     uint32_t di_ga = (di & 0xff00ff00);
+     uint32_t di_rb = (di & 0x00ff00ff);
 
-  int prev_x = 0;
-  int prev_y = 0;
+     uint32_t d_rb  = si_rb - di_rb;
+     uint32_t d_ga  = si_ga - (di_ga>>8);
 
-    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height,
-       blit_width,
-       CTX_CLIP_FORMAT);
+     *((uint32_t*)(dst)) =
 
-  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);
+     (((di_rb + ((d_rb * cov)>>8)) & 0x00ff00ff))  |
+      ((di_ga + ((d_ga * cov)      & 0xff00ff00)));
+     dst +=4;
   }
+#endif
+}
 
-  int maybe_rect = 1;
-  rasterizer->clip_rectangle = 0;
+static void
+ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
+}
 
-  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
+static void
+ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)
+{
+  for (int j = 0; j < count; j++)
   {
-    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
+  switch (components)
   {
-    int count = blit_width * blit_height;
+     case 3:
+       ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2];
+       *((uint16_t*)(blended)) = *((uint16_t*)(src));
+       break;
+     case 2:
+       *((uint16_t*)(blended)) = *((uint16_t*)(src));
+       break;
+     case 5:
+       *((uint32_t*)(blended)) = *((uint32_t*)(src));
+       ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4];
+       break;
+     case 4:
+       *((uint32_t*)(blended)) = *((uint32_t*)(src));
+       break;
+     default:
+       {
+        for (int i = 0; i<components;i++)
+           blended[i] = src[i];
+       }
+       break;
+  }
+    blended+=components;
+    src+=components;
+  }
+}
 
+/* branchless 8bit add that maxes out at 255 */
+static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b)
+{
+  uint16_t s = (uint16_t)a+b;
+  return -(s>>8) | (uint8_t)s;
+}
 
-    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;
-      }
-    }
-
-    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;
-
-      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;
-    }
-
-    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 (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; }
-      }
-    }
-
-    next_stage = 0;
-    /* foot */
-    for (; i < count && maybe_rect && !next_stage; i++)
-    {
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
-
-      if (val != 0){ maybe_rect = 0; next_stage = 1; }
-    }
+#if CTX_BLENDING_AND_COMPOSITING
 
+#define ctx_u8_blend_define(name, CODE) \
+static void \
+ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)\
+{\
+  for (int j = 0; j < count; j++) { \
+  uint8_t *s=src; uint8_t b[components];\
+  ctx_u8_deassociate_alpha (components, dst, b);\
+    CODE;\
+  blended[components-1] = src[components-1];\
+  ctx_u8_associate_alpha (components, blended);\
+  src += components;\
+  dst += components;\
+  blended += components;\
+  }\
+}
 
-    for (; i < count; i++)
-    {
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
-    }
+#define ctx_u8_blend_define_seperable(name, CODE) \
+        ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
 
-    if (maybe_rect)
-       rasterizer->clip_rectangle = 1;
+ctx_u8_blend_define_seperable(multiply,     blended[c] = (b[c] * s[c])/255;)
+ctx_u8_blend_define_seperable(screen,       blended[c] = s[c] + b[c] - (s[c] * b[c])/255;)
+ctx_u8_blend_define_seperable(overlay,      blended[c] = b[c] < 127 ? (s[c] * b[c])/255 :
+                                                         s[c] + b[c] - (s[c] * b[c])/255;)
+ctx_u8_blend_define_seperable(darken,       blended[c] = ctx_mini (b[c], s[c]))
+ctx_u8_blend_define_seperable(lighten,      blended[c] = ctx_maxi (b[c], s[c]))
+ctx_u8_blend_define_seperable(color_dodge,  blended[c] = b[c] == 0 ? 0 :
+                                     s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c])))
+ctx_u8_blend_define_seperable(color_burn,   blended[c] = b[c] == 1 ? 1 :
+                                     s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c]))
+ctx_u8_blend_define_seperable(hard_light,   blended[c] = s[c] < 127 ? (b[c] * s[c])/255 :
+                                                          b[c] + s[c] - (b[c] * s[c])/255;)
+ctx_u8_blend_define_seperable(difference,   blended[c] = (b[c] - s[c]))
+ctx_u8_blend_define_seperable(divide,       blended[c] = s[c]?(255 * b[c]) / s[c]:0)
+ctx_u8_blend_define_seperable(addition,     blended[c] = ctx_sadd8 (s[c], b[c]))
+ctx_u8_blend_define_seperable(subtract,     blended[c] = ctx_maxi(0, s[c]-b[c]))
+ctx_u8_blend_define_seperable(exclusion,    blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255))
+ctx_u8_blend_define_seperable(soft_light,
+  if (s[c] <= 255/2)
+  {
+    blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255);
   }
-  if (!we_made_it)
-   ctx_buffer_free (clip_buffer);
-#else
-  if (coords[0][0]){};
-#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);
-}
+  else
+  {
+    int d;
+    if (b[c] <= 255/4)
+      d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255;
+    else
+      d = ctx_sqrtf(b[c]/255.0) * 255.4;
+    blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255;
+  }
+)
 
-static void
-ctx_rasterizer_clip (CtxRasterizer *rasterizer)
+static int ctx_int_get_max (int components, int *c)
 {
-  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;
-    }
+  int max = 0;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] > max) max = c[i];
+  }
+  return max;
 }
 
-
-#if 0
-static void
-ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
-                           const char  *path,
-                           float x,
-                           float y)
+static int ctx_int_get_min (int components, int *c)
 {
-  // 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 min = 400;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] < min) min = c[i];
+  }
+  return min;
 }
-#endif
-
 
-CTX_INLINE void
-ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
-                          float x,
-                          float y,
-                          float width,
-                          float height)
+static int ctx_int_get_lum (int components, int *c)
 {
-  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, width/2, 0);
-  ctx_rasterizer_finish_shape (rasterizer);
+  switch (components)
+  {
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
+    default:
+       {
+         int sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
+            break;
+  }
 }
 
-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)
+static int ctx_u8_get_lum (int components, uint8_t *c)
 {
-  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 - and has
-  // received less testing than code paths part of protocol,
-  // using rectangle properly will trigger the fillrect fastpath
-  ctx_rasterizer_pset (rasterizer, x, y, 255);
-#else
-  ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0);
-  ctx_rasterizer_fill (rasterizer);
-#endif
+  switch (components)
+  {
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
+    default:
+       {
+         int sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
+            break;
+  }
 }
-
-#if CTX_ENABLE_SHADOW_BLUR
-static float
-ctx_gaussian (float x, float mu, float sigma)
+static int ctx_u8_get_sat (int components, uint8_t *c)
 {
-  float a = ( x- mu) / sigma;
-  return ctx_expf (-0.5 * a * a);
+  switch (components)
+  {
+    case 3:
+    case 4:
+            { int r = c[0];
+              int g = c[1];
+              int b = c[2];
+              return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b));
+            }
+            break;
+    case 1:
+    case 2:
+            return 0.0;
+            break;
+    default:
+       {
+         int min = 1000;
+         int max = -1000;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           if (c[i] < min) min = c[i];
+           if (c[i] > max) max = c[i];
+         }
+         return max-min;
+       }
+       break;
+  }
 }
 
-static void
-ctx_compute_gaussian_kernel (int dim, float radius, float *kernel)
+static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum)
 {
-  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
+  int d = lum - ctx_u8_get_lum (components, c);
+  int tc[components];
+  for (int i = 0; i < components - 1; i++)
+  {
+    tc[i] = c[i] + d;
+  }
 
-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;
+  int l = ctx_int_get_lum (components, tc);
+  int n = ctx_int_get_min (components, tc);
+  int x = ctx_int_get_max (components, tc);
 
-  if (radius > width/2) radius = width/2;
-  if (radius > height/2) radius = height/2;
+  if (n < 0 && l!=n)
+  {
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * l) / (l-n));
+  }
 
-  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);
+  if (x > 255 && x!=l)
+  {
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l));
+  }
+  for (int i = 0; i < components - 1; i++)
+    c[i] = tc[i];
 }
 
-static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command);
-
-int
-_ctx_is_rasterizer (Ctx *ctx)
+static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat)
 {
-  if (ctx->renderer && ctx->renderer->process == ctx_rasterizer_process)
-    return 1;
-  return 0;
+  int max = 0, mid = 1, min = 2;
+  
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+
+  if (c[max] > c[min])
+  {
+    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
+    c[max] = sat;
+  }
+  else
+  {
+    c[mid] = c[max] = 0;
+  }
+  c[min] = 0;
 }
 
-#if CTX_COMPOSITING_GROUPS
-static void
-ctx_rasterizer_start_group (CtxRasterizer *rasterizer)
+ctx_u8_blend_define(color,
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s));
+)
+
+ctx_u8_blend_define(hue,
+  int in_sat = ctx_u8_get_sat(components, b);
+  int in_lum = ctx_u8_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_u8_set_sat(components, blended, in_sat);
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+
+ctx_u8_blend_define(saturation,
+  int in_sat = ctx_u8_get_sat(components, s);
+  int in_lum = ctx_u8_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_u8_set_sat(components, blended, in_sat);
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+
+ctx_u8_blend_define(luminosity,
+  int in_lum = ctx_u8_get_lum(components, s);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+#endif
+
+CTX_INLINE static void
+ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, 
int count)
 {
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-  // allocate buffer, and set it as temporary target
-  int no;
-  if (rasterizer->group[0] == NULL) // first group
+#if CTX_BLENDING_AND_COMPOSITING
+  switch (blend)
   {
-    rasterizer->saved_buf = rasterizer->buf;
+    case CTX_BLEND_NORMAL:      ctx_u8_blend_normal      (components, dst, src, blended, count); break;
+    case CTX_BLEND_MULTIPLY:    ctx_u8_blend_multiply    (components, dst, src, blended, count); break;
+    case CTX_BLEND_SCREEN:      ctx_u8_blend_screen      (components, dst, src, blended, count); break;
+    case CTX_BLEND_OVERLAY:     ctx_u8_blend_overlay     (components, dst, src, blended, count); break;
+    case CTX_BLEND_DARKEN:      ctx_u8_blend_darken      (components, dst, src, blended, count); break;
+    case CTX_BLEND_LIGHTEN:     ctx_u8_blend_lighten     (components, dst, src, blended, count); break;
+    case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended, count); break;
+    case CTX_BLEND_COLOR_BURN:  ctx_u8_blend_color_burn  (components, dst, src, blended, count); break;
+    case CTX_BLEND_HARD_LIGHT:  ctx_u8_blend_hard_light  (components, dst, src, blended, count); break;
+    case CTX_BLEND_SOFT_LIGHT:  ctx_u8_blend_soft_light  (components, dst, src, blended, count); break;
+    case CTX_BLEND_DIFFERENCE:  ctx_u8_blend_difference  (components, dst, src, blended, count); break;
+    case CTX_BLEND_EXCLUSION:   ctx_u8_blend_exclusion   (components, dst, src, blended, count); break;
+    case CTX_BLEND_COLOR:       ctx_u8_blend_color       (components, dst, src, blended, count); break;
+    case CTX_BLEND_HUE:         ctx_u8_blend_hue         (components, dst, src, blended, count); break;
+    case CTX_BLEND_SATURATION:  ctx_u8_blend_saturation  (components, dst, src, blended, count); break;
+    case CTX_BLEND_LUMINOSITY:  ctx_u8_blend_luminosity  (components, dst, src, blended, count); break;
+    case CTX_BLEND_ADDITION:    ctx_u8_blend_addition    (components, dst, src, blended, count); break;
+    case CTX_BLEND_DIVIDE:      ctx_u8_blend_divide      (components, dst, src, blended, count); break;
+    case CTX_BLEND_SUBTRACT:    ctx_u8_blend_subtract    (components, dst, src, blended, count); break;
+  }
+#else
+  switch (blend)
+  {
+    default:                    ctx_u8_blend_normal      (components, dst, src, blended, count); break;
   }
-  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);
+#endif
 }
 
-static void
-ctx_rasterizer_end_group (CtxRasterizer *rasterizer)
+CTX_INLINE static void
+__ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
+                     int                    components,
+                     uint8_t *              dst,
+                     uint8_t *              src,
+                     int                    x0,
+                     uint8_t * __restrict__ coverage,
+                     int                    count,
+                     CtxCompositingMode     compositing_mode,
+                     CtxFragment            fragment,
+                     CtxBlend               blend)
 {
-  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;
+  CtxPorterDuffFactor f_s, f_d;
+  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  uint8_t global_alpha_u8 = gstate->global_alpha_u8;
+  uint8_t tsrc[components * count];
+  int src_step = 0;
 
-  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)
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
   {
-    rasterizer->buf = rasterizer->saved_buf;
+    src = &tsrc[0];
+    fragment (rasterizer, 0, 0, src, 1, 0, 0);
+    if (blend != CTX_BLEND_NORMAL)
+      ctx_u8_blend (components, blend, dst, src, src, 1);
   }
   else
   {
-    rasterizer->buf = rasterizer->group[no-1]->data;
+    float u0 = 0; float v0 = 0;
+    float ud = 0; float vd = 0;
+    src = &tsrc[0];
+
+    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+    fragment (rasterizer, u0, v0, src, count, ud, vd);
+    if (blend != CTX_BLEND_NORMAL)
+      ctx_u8_blend (components, blend, dst, src, src, count);
+    src_step = components;
   }
-  // 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);
+
+  while (count--)
   {
-     char *eid = ".ctx-group";
-     int   eid_len = strlen (eid);
+    uint32_t cov = *coverage;
 
-     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;
+    if (CTX_UNLIKELY(global_alpha_u8 != 255))
+      cov = (cov * global_alpha_u8 + 255) >> 8;
 
-     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
-  }
-  {
-    CtxEntry commands[2]=
+    uint8_t csrc[components];
+    for (int c = 0; c < components; c++)
+      csrc[c] = (src[c] * cov + 255) >> 8;
+
+    for (int c = 0; c < components; c++)
     {
-      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);
+      uint32_t res = 0;
+#if 1
+      switch (f_s)
+      {
+        case CTX_PORTER_DUFF_0:             break;
+        case CTX_PORTER_DUFF_1:             res += (csrc[c] ); break;
+        case CTX_PORTER_DUFF_ALPHA:         res += (csrc[c] * dst[components-1] + 255) >> 8; break;
+        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (256-dst[components-1])) >> 8; break;
+      }
+      switch (f_d)
+      {
+        case CTX_PORTER_DUFF_0: break;
+        case CTX_PORTER_DUFF_1:             res += dst[c]; break;
+        case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * csrc[components-1] + 255) >> 8; break;
+        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (256-csrc[components-1])) >> 8; break;
+      }
+#else
+      switch (f_s)
+      {
+        case CTX_PORTER_DUFF_0:             break;
+        case CTX_PORTER_DUFF_1:             res += (csrc[c] ); break;
+        case CTX_PORTER_DUFF_ALPHA:         res += (csrc[c] * dst[components-1])/255; break;
+        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (255-dst[components-1]))/255; break;
+      }
+      switch (f_d)
+      {
+        case CTX_PORTER_DUFF_0: break;
+        case CTX_PORTER_DUFF_1:             res += dst[c]; break;
+        case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * csrc[components-1])/255; break;
+        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-csrc[components-1]))/255; break;
+      }
+#endif
+      dst[c] = res;
+    }
+    coverage ++;
+    src+=src_step;
+    dst+=components;
   }
-  {
-    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
 
-#if CTX_ENABLE_SHADOW_BLUR
+CTX_INLINE static void
+_ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
+                     int                    components,
+                     uint8_t *              dst,
+                     uint8_t * __restrict__ src,
+                     int                    x0,
+                     uint8_t *              coverage,
+                     int                    count,
+                     CtxCompositingMode     compositing_mode,
+                     CtxFragment            fragment,
+                     CtxBlend               blend)
+{
+  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, 
blend);
+}
+
+#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \
+   switch (rasterizer->state->gstate.compositing_mode) \
+   { \
+     case CTX_COMPOSITE_SOURCE_ATOP: \
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
+        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_XOR, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_COPY, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_CLEAR:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_CLEAR, fragment, blend);\
+       break;\
+   }
+
+/* generating one function per compositing_mode would be slightly more efficient,
+ * but on embedded targets leads to slightly more code bloat,
+ * here we trade off a slight amount of performance
+ */
+#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \
+static void \
+ctx_##comp_format##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
+{ \
+  _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\
+}
+
+ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+//ctx_u8_porter_duff(comp_name, components,color_##blend_name,  NULL, blend_mode)
+
 static void
-ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
+ctx_RGBA8_nop (CTX_COMPOSITE_ARGUMENTS)
 {
-  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++)
+static void
+ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components       = 4;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBA8 (rasterizer);
+  rasterizer->comp_op  = ctx_RGBA8_porter_duff_generic;
+  rasterizer->comp = CTX_COV_PATH_FALLBACK;
+
+  int blend_mode       = gstate->blend_mode;
+  int compositing_mode = gstate->compositing_mode;
+
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      ctx_fragment_color_RGBA8 (rasterizer, 0,0, rasterizer->color, 1, 0,0);
+      if (gstate->global_alpha_u8 != 255)
       {
-        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
+        for (int c = 0; c < 4; c ++)
+          rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8 + 255)>>8;
+      }
+      uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
+      uint32_t si_ga      = (src_pix & 0xff00ff00) >> 8;
+      uint32_t si_rb      = src_pix & 0x00ff00ff;
+      uint32_t si_ga_full = si_ga * 255;
+      uint32_t si_rb_full = si_rb * 255;
+//      uint32_t si_a       = si_ga >> 16;
+
+      ((uint32_t*)rasterizer->color)[1] = si_ga;
+      ((uint32_t*)rasterizer->color)[2] = si_rb;
+      ((uint32_t*)rasterizer->color)[3] = si_ga_full;
+      ((uint32_t*)rasterizer->color)[4] = si_rb_full;
+
+      if (blend_mode == CTX_BLEND_NORMAL &&
+           compositing_mode == CTX_COMPOSITE_COPY)
+      {
+        rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color;
+        rasterizer->comp = CTX_COV_PATH_COPY;
       }
+      else if (blend_mode == CTX_BLEND_NORMAL &&
+          compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+     {
+       if (rasterizer->color[components-1] == 255)
+       {
+        rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color;
+        rasterizer->comp = CTX_COV_PATH_COPY;
+       }
+       else
+       {
+        rasterizer->comp_op = ctx_RGBA8_source_over_normal_color;
+        rasterizer->comp = CTX_COV_PATH_OVER;
+       }
+     }
+     else if (compositing_mode == CTX_COMPOSITE_CLEAR)
+     {
+       rasterizer->comp_op = ctx_RGBA8_clear_normal;
+     }
+  }
+  else if (blend_mode == CTX_BLEND_NORMAL &&
+           compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+  {
+     rasterizer->comp_op = ctx_RGBA8_source_over_normal_fragment;
+     rasterizer->comp = CTX_COV_PATH_OVER_FRAGMENT;
+  }
+  else if (blend_mode == CTX_BLEND_NORMAL &&
+           compositing_mode == CTX_COMPOSITE_COPY)
+  {
+     rasterizer->comp_op = ctx_RGBA8_source_copy_normal_fragment;
+     rasterizer->comp = CTX_COV_PATH_COPY_FRAGMENT;
   }
-  //free (kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
 }
 
 static void
-ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str)
+ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS)
 {
-  float x = rasterizer->state->x;
-  float y = rasterizer->state->y;
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
+  uint8_t pixels[count * rasterizer->format->ebpp];
+  rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count);
+}
 
-  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);
+#if CTX_ENABLE_FLOAT
+static void
+ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  float *srcf = (float*)src;
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
 
-  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_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+
+  while (count--)
   {
-    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);
+    uint8_t cov = *coverage;
+    float covf = ctx_u8_to_float (cov);
+    for (int c = 0; c < components; c++)
+      dstf[c] = dstf[c]*(1.0-covf) + srcf[c]*covf;
+    dstf += components;
+    coverage ++;
+  }
+}
 
+static void
+ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  while (count--)
   {
+#if 0
+    uint8_t cov = *coverage;
+    if (cov == 0)
+    {
+    }
+    else if (cov == 255)
+    {
+#endif
+      switch (components)
       {
-        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 2:
+          ((uint64_t*)(dst))[0] = 0;
+          break;
+        case 4:
+          ((uint64_t*)(dst))[0] = 0;
+          ((uint64_t*)(dst))[1] = 0;
+          break;
+        default:
+          for (int c = 0; c < components; c++)
+            dstf[c] = 0.0f;
       }
+#if 0
+    }
+    else
+    {
+      float ralpha = 1.0 - ctx_u8_to_float (cov);
+      for (int c = 0; c < components; c++)
+        { dstf[c] = (dstf[c] * ralpha); }
+    }
+    coverage ++;
+#endif
+    dstf += components;
   }
-  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)
+ctx_float_source_over_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
+  float *dstf = (float*)dst;
+  float *srcf = (float*)src;
+  while (count--)
+  {
+    uint8_t cov = *coverage;
+    float fcov = ctx_u8_to_float (cov);
+    float ralpha = 1.0f - fcov * srcf[components-1];
+    for (int c = 0; c < components-1; c++)
+      dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
+    coverage ++;
+    dstf+= components;
+  }
+}
 
-  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);
+static void
+ctx_float_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  float *srcf = (float*)src;
 
-  CtxEntry set_color_command [3]=
+  while (count--)
   {
-    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);
+    uint8_t cov = *coverage;
+    float fcov = ctx_u8_to_float (cov);
+    float ralpha = 1.0f - fcov;
+    for (int c = 0; c < components-1; c++)
+      dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
+    coverage ++;
+    dstf+= components;
+  }
+}
+
+inline static void
+ctx_float_blend_normal (int components, float *dst, float *src, float *blended)
+{
+  float a = src[components-1];
+  for (int c = 0; c <  components - 1; c++)
+    blended[c] = src[c] * a;
+  blended[components-1]=a;
+}
 
+static float ctx_float_get_max (int components, float *c)
+{
+  float max = -1000.0f;
+  for (int i = 0; i < components - 1; i ++)
   {
-    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;
-      }
+    if (c[i] > max) max = c[i];
   }
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  return max;
 }
-#endif
 
-static void
-ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, int count, float *dashes)
+static float ctx_float_get_min (int components, float *c)
 {
-  if (!dashes)
+  float min = 400.0;
+  for (int i = 0; i < components - 1; i ++)
   {
-    rasterizer->state->gstate.n_dashes = 0;
-    return;
+    if (c[i] < min) min = c[i];
   }
-  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 ++)
+  return min;
+}
+
+static float ctx_float_get_lum (int components, float *c)
+{
+  switch (components)
   {
-    if (rasterizer->state->gstate.dashes[i] < 0.0001f)
-      rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
+    default:
+       {
+         float sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
   }
 }
 
-
-static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command)
+static float ctx_float_get_sat (int components, float *c)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int clear_clip = 0;
-  ctx_interpret_style (rasterizer->state, entry, NULL);
-  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;
+  switch (components)
+  {
+    case 3:
+    case 4:
+            { float r = c[0];
+              float g = c[1];
+              float b = c[2];
+              return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b));
             }
-          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;
+            break;
+    case 1:
+    case 2: return 0.0;
+            break;
+    default:
+       {
+         float min = 1000;
+         float max = -1000;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           if (c[i] < min) min = c[i];
+           if (c[i] > max) max = c[i];
+         }
+         return max-min;
+       }
+  }
+}
 
-      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);
-          rasterizer->comp_op = NULL;
-        }
-        break;
-      case CTX_TEXTURE:
-        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:
-        ctx_rasterizer_load_image (rasterizer, ctx_arg_string(),
-                                   ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-#endif
-#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_rasterizer_gradient_add_stop (rasterizer,
-                                            ctx_arg_float (0), rgba);
-          rasterizer->comp_op = NULL;
-        }
-        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_IDENTITY:
-      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 (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;
-      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 = 15;//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 = rasterizer->state->gstate.line_dash_offset * factor;
-      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++)
-            {
-              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
-              {
-                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;
-        }
-
-        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);
-}
-
-void
-ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
+static void ctx_float_set_lum (int components, float *c, float lum)
 {
-  ctx_drawlist_deinit (&rasterizer->edge_list);
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
+  float d = lum - ctx_float_get_lum (components, c);
+  float tc[components];
+  for (int i = 0; i < components - 1; i++)
   {
-    ctx_buffer_free (rasterizer->clip_buffer);
-    rasterizer->clip_buffer = NULL;
+    tc[i] = c[i] + d;
   }
-#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);
-}
 
+  float l = ctx_float_get_lum (components, tc);
+  float n = ctx_float_get_min (components, tc);
+  float x = ctx_float_get_max (components, tc);
 
-CtxAntialias ctx_get_antialias (Ctx *ctx)
-{
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  if (n < 0.0f && l != n)
   {
-     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
-     return fb->antialias;
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * l) / (l-n));
   }
-#endif
-  if (!_ctx_is_rasterizer (ctx)) return CTX_ANTIALIAS_DEFAULT;
 
-  switch (((CtxRasterizer*)(ctx->renderer))->aa)
+  if (x > 1.0f && x != l)
   {
-    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;
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l));
   }
+  for (int i = 0; i < components - 1; i++)
+    c[i] = tc[i];
 }
 
-int _ctx_antialias_to_aa (CtxAntialias antialias)
+static void ctx_float_set_sat (int components, float *c, float sat)
 {
-  switch (antialias)
+  int max = 0, mid = 1, min = 2;
+  
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+
+  if (c[max] > c[min])
   {
-    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;
+    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
+    c[max] = sat;
   }
-}
-
-void
-ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
-{
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  else
   {
-     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;
+    c[mid] = c[max] = 0.0f;
   }
-#endif
-  if (!_ctx_is_rasterizer (ctx)) return;
+  c[min] = 0.0f;
 
-  ((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
- *
- */
 }
 
-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);
+#define ctx_float_blend_define(name, CODE) \
+static void \
+ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\
+{\
+  float *s = src; float b[components];\
+  ctx_float_deassociate_alpha (components, dst, b);\
+    CODE;\
+  blended[components-1] = s[components-1];\
+  ctx_float_associate_alpha (components, blended);\
+}
 
-  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);
-  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);
+#define ctx_float_blend_define_seperable(name, CODE) \
+        ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
 
-  return rasterizer;
-}
+ctx_float_blend_define_seperable(multiply,    blended[c] = (b[c] * s[c]);)
+ctx_float_blend_define_seperable(screen,      blended[c] = b[c] + s[c] - (b[c] * s[c]);)
+ctx_float_blend_define_seperable(overlay,     blended[c] = b[c] < 0.5f ? (s[c] * b[c]) :
+                                                          s[c] + b[c] - (s[c] * b[c]);)
+ctx_float_blend_define_seperable(darken,      blended[c] = ctx_minf (b[c], s[c]))
+ctx_float_blend_define_seperable(lighten,     blended[c] = ctx_maxf (b[c], s[c]))
+ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f :
+                                     s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c])))
+ctx_float_blend_define_seperable(color_burn,  blended[c] = (b[c] == 1.0f) ? 1.0f :
+                                     s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c]))
+ctx_float_blend_define_seperable(hard_light,  blended[c] = s[c] < 0.f ? (b[c] * s[c]) :
+                                                          b[c] + s[c] - (b[c] * s[c]);)
+ctx_float_blend_define_seperable(difference,  blended[c] = (b[c] - s[c]))
 
-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;
-}
+ctx_float_blend_define_seperable(divide,      blended[c] = s[c]?(b[c]) / s[c]:0.0f)
+ctx_float_blend_define_seperable(addition,    blended[c] = s[c]+b[c])
+ctx_float_blend_define_seperable(subtract,    blended[c] = s[c]-b[c])
 
-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;
-}
+ctx_float_blend_define_seperable(exclusion,   blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c])
+ctx_float_blend_define_seperable(soft_light,
+  if (s[c] <= 0.5f)
+  {
+    blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]);
+  }
+  else
+  {
+    int d;
+    if (b[c] <= 255/4)
+      d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]);
+    else
+      d = ctx_sqrtf(b[c]);
+    blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c]));
+  }
+)
 
-// 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)
-{
-  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
+ctx_float_blend_define(color,
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s));
+)
 
-CtxPixelFormatInfo *ctx_pixel_formats = NULL;
+ctx_float_blend_define(hue,
+  float in_sat = ctx_float_get_sat(components, b);
+  float in_lum = ctx_float_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_float_set_sat(components, blended, in_sat);
+  ctx_float_set_lum(components, blended, in_lum);
+)
 
-extern CtxPixelFormatInfo ctx_pixel_formats_default[];
+ctx_float_blend_define(saturation,
+  float in_sat = ctx_float_get_sat(components, s);
+  float in_lum = ctx_float_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_float_set_sat(components, blended, in_sat);
+  ctx_float_set_lum(components, blended, in_lum);
+)
 
+ctx_float_blend_define(luminosity,
+  float in_lum = ctx_float_get_lum(components, s);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_float_set_lum(components, blended, in_lum);
+)
 
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format)
+inline static void
+ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended)
 {
-  if (!ctx_pixel_formats)
+  switch (blend)
   {
-    ctx_pixel_formats = ctx_pixel_formats_default;
-
+    case CTX_BLEND_NORMAL:      ctx_float_blend_normal      (components, dst, src, blended); break;
+    case CTX_BLEND_MULTIPLY:    ctx_float_blend_multiply    (components, dst, src, blended); break;
+    case CTX_BLEND_SCREEN:      ctx_float_blend_screen      (components, dst, src, blended); break;
+    case CTX_BLEND_OVERLAY:     ctx_float_blend_overlay     (components, dst, src, blended); break;
+    case CTX_BLEND_DARKEN:      ctx_float_blend_darken      (components, dst, src, blended); break;
+    case CTX_BLEND_LIGHTEN:     ctx_float_blend_lighten     (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_BURN:  ctx_float_blend_color_burn  (components, dst, src, blended); break;
+    case CTX_BLEND_HARD_LIGHT:  ctx_float_blend_hard_light  (components, dst, src, blended); break;
+    case CTX_BLEND_SOFT_LIGHT:  ctx_float_blend_soft_light  (components, dst, src, blended); break;
+    case CTX_BLEND_DIFFERENCE:  ctx_float_blend_difference  (components, dst, src, blended); break;
+    case CTX_BLEND_EXCLUSION:   ctx_float_blend_exclusion   (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR:       ctx_float_blend_color       (components, dst, src, blended); break;
+    case CTX_BLEND_HUE:         ctx_float_blend_hue         (components, dst, src, blended); break;
+    case CTX_BLEND_SATURATION:  ctx_float_blend_saturation  (components, dst, src, blended); break;
+    case CTX_BLEND_LUMINOSITY:  ctx_float_blend_luminosity  (components, dst, src, blended); break;
+    case CTX_BLEND_ADDITION:    ctx_float_blend_addition    (components, dst, src, blended); break;
+    case CTX_BLEND_SUBTRACT:    ctx_float_blend_subtract    (components, dst, src, blended); break;
+    case CTX_BLEND_DIVIDE:      ctx_float_blend_divide      (components, dst, src, blended); break;
   }
-
-  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)
+/* this is the grunt working function, when inlined code-path elimination makes
+ * it produce efficient code.
+ */
+CTX_INLINE static void
+ctx_float_porter_duff (CtxRasterizer         *rasterizer,
+                       int                    components,
+                       uint8_t * __restrict__ dst,
+                       uint8_t * __restrict__ src,
+                       int                    x0,
+                       uint8_t * __restrict__ coverage,
+                       int                    count,
+                       CtxCompositingMode     compositing_mode,
+                       CtxFragment            fragment,
+                       CtxBlend               blend)
 {
-  return NULL;
-}
-#endif
+  float *dstf = (float*)dst;
 
-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)
-    {
-      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; }
-}
-
-float ctx_x (Ctx *ctx)
-{
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return x;
-}
+  CtxPorterDuffFactor f_s, f_d;
+  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
+  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
+  float   global_alpha_f = rasterizer->state->gstate.global_alpha_f;
+  
+  {
+    float tsrc[components];
+    float u0 = 0; float v0 = 0;
+    float ud = 0; float vd = 0;
 
-float ctx_y (Ctx *ctx)
-{
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return y;
-}
+    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
 
-void
-ctx_process (Ctx *ctx, CtxEntry *entry)
-{
-#if CTX_CURRENT_PATH
-  switch (entry->code)
+    while (count--)
     {
-      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;
-    }
+      uint8_t cov = *coverage;
+#if 1
+      if (
+        CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)||
+        (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER ||
+        compositing_mode == CTX_COMPOSITE_XOR               ||
+        compositing_mode == CTX_COMPOSITE_DESTINATION_OUT   ||
+        compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      
+        ))))
+      {
+        u0 += ud;
+        v0 += vd;
+        coverage ++;
+        dstf+=components;
+        continue;
+      }
 #endif
-  if (ctx->renderer && ctx->renderer->process)
-    {
-      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
-    }
-  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);
+
+      fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
+      if (blend != CTX_BLEND_NORMAL)
+        ctx_float_blend (components, blend, dstf, tsrc, tsrc);
+      u0 += ud;
+      v0 += vd;
+      float covf = ctx_u8_to_float (cov);
+
+      if (global_alpha_u8 != 255)
+        covf = covf * global_alpha_f;
+
+      if (covf != 1.0f)
+      {
+        for (int c = 0; c < components; c++)
+          tsrc[c] *= covf;
+      }
+
+      for (int c = 0; c < components; c++)
+      {
+        float res;
+        /* these switches and this whole function is written to be
+         * inlined when compiled when the enum values passed in are
+         * constants.
+         */
+        switch (f_s)
+        {
+          case CTX_PORTER_DUFF_0: res = 0.0f; break;
+          case CTX_PORTER_DUFF_1:             res = (tsrc[c]); break;
+          case CTX_PORTER_DUFF_ALPHA:         res = (tsrc[c] *       dstf[components-1]); break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break;
+        }
+        switch (f_d)
+        {
+          case CTX_PORTER_DUFF_0: dstf[c] = res; break;
+          case CTX_PORTER_DUFF_1:             dstf[c] = res + (dstf[c]); break;
+          case CTX_PORTER_DUFF_ALPHA:         dstf[c] = res + (dstf[c] *       tsrc[components-1]); break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break;
+        }
+      }
+      coverage ++;
+      dstf     +=components;
     }
+  }
+}
+
+/* generating one function per compositing_mode would be slightly more efficient,
+ * but on embedded targets leads to slightly more code bloat,
+ * here we trade off a slight amount of performance
+ */
+#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \
+static void \
+ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
+{ \
+   switch (rasterizer->state->gstate.compositing_mode) \
+   { \
+     case CTX_COMPOSITE_SOURCE_ATOP: \
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
+        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_XOR, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_COPY, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_CLEAR:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_CLEAR, fragment, blend);\
+       break;\
+   }\
 }
+#endif
 
+#if CTX_ENABLE_RGBAF
 
-int ctx_gradient_cache_valid = 0;
+ctx_float_porter_duff(RGBAF, 4,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(RGBAF, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-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;
-}
+#if CTX_INLINED_NORMAL
+#if CTX_GRADIENTS
+ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
+#endif
+ctx_float_porter_duff(RGBAF, 4,image,           ctx_fragment_image_RGBAF,           
rasterizer->state->gstate.blend_mode)
 
-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 ****/
+#if CTX_GRADIENTS
+#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
+ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
+ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
+ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name,  
ctx_fragment_linear_gradient_RGBA8, blend_mode)\
+ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name,  
ctx_fragment_radial_gradient_RGBA8, blend_mode)\
+ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
+#else
+#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
+ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
+ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
+ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
+#endif
 
-CtxBuffer *ctx_buffer_new_bare (void)
-{
-  CtxBuffer *buffer = (CtxBuffer *) ctx_calloc (sizeof (CtxBuffer), 1);
-  return buffer;
-}
+ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal)
 
-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)
+
+static void
+ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  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;
+  ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-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_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxBuffer *buffer = ctx_buffer_new_bare ();
-  ctx_buffer_set_data (buffer, data, width, height, stride, pixel_format,
-                       freefunc, user_data);
-  return buffer;
+  ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-void ctx_buffer_pixels_free (void *pixels, void *userdata)
+#if 1
+static void
+ctx_RGBAF_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
 {
-  free (pixels);
+  ctx_float_source_over_normal_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
+#endif
+#endif
 
-CtxBuffer *ctx_buffer_new (int width, int height,
-                           CtxPixelFormat pixel_format)
+static void
+ctx_setup_RGBAF (CtxRasterizer *rasterizer)
 {
-  //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);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 4;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer);
+  rasterizer->comp = CTX_COV_PATH_FALLBACK;
+#if 1
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
+      ctx_fragment_color_RGBAF (rasterizer, 0,0, rasterizer->color, 1, 0,0);
+      if (gstate->global_alpha_u8 != 255)
+        for (int c = 0; c < components; c ++)
+          ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
+    }
+  else
+#endif
+  {
+    rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
+  }
 
-  ctx_buffer_set_data (buffer, pixels, width, height, stride, pixel_format,
-                       ctx_buffer_pixels_free, NULL);
-  return buffer;
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_RGBAF_clear_normal;
+  else
+    switch (gstate->blend_mode)
+    {
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+        {
+          rasterizer->comp_op = ctx_RGBAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+        {
+          rasterizer->comp_op = ctx_RGBA8_nop;
+        }
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              rasterizer->comp_op = ctx_RGBAF_source_over_normal_color;
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal;
+            }
+            break;
+#if CTX_GRADIENTS
+          case CTX_SOURCE_LINEAR_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal;
+            break;
+          case CTX_SOURCE_RADIAL_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal;
+            break;
+#endif
+          case CTX_SOURCE_TEXTURE:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal;
+            break;
+          default:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
+            //rasterizer->fragment = NULL;
+            break;
+#if CTX_GRADIENTS
+          case CTX_SOURCE_LINEAR_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient;
+            break;
+          case CTX_SOURCE_RADIAL_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient;
+            break;
+#endif
+          case CTX_SOURCE_TEXTURE:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_image;
+            break;
+          default:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
+            break;
+        }
+        break;
+    }
+#endif
 }
 
-void ctx_buffer_deinit (CtxBuffer *buffer)
+#endif
+#if CTX_ENABLE_GRAYAF
+
+#if CTX_GRADIENTS
+static void
+ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  if (buffer->free_func)
-    buffer->free_func (buffer->data, buffer->user_data);
-  if (buffer->eid)
+  float rgba[4];
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0 ; i < count; i++)
   {
-    free (buffer->eid);
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
+  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0, rgba);
+  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
+  ((float*)out)[1] = rgba[3];
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
   }
-  buffer->eid = NULL;
-  buffer->data = NULL;
-  buffer->free_func = NULL;
-  buffer->user_data  = NULL;
-  if (buffer->color_managed)
+}
+
+static void
+ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+  float rgba[4];
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i ++)
   {
-    if (buffer->color_managed != buffer)
+  float v = 0.0f;
+  if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f)
     {
-      ctx_buffer_free (buffer->color_managed);
+      v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
+      v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta);
     }
-    buffer->color_managed = NULL;
+  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba);
+  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
+  ((float*)out)[1] = rgba[3];
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
   }
 }
+#endif
 
-void ctx_buffer_free (CtxBuffer *buffer)
+static void
+ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  ctx_buffer_deinit (buffer);
-  free (buffer);
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
+  {
+     ctx_color_get_graya (rasterizer->state, &g->color, (float*)out);
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
+  }
 }
 
-static int
-ctx_texture_check_eid (Ctx *ctx, const char *eid, int *tw, int *th)
+static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
-  {
-    if (ctx->texture[i].data &&
-        ctx->texture[i].eid  &&
-        !strcmp (ctx->texture[i].eid, eid))
+  uint8_t rgba[4];
+  float rgbaf[4];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
     {
-      if (tw) *tw = ctx->texture[i].width;
-      if (th) *th = ctx->texture[i].height;
-      ctx->texture[i].frame = ctx->texture_cache->frame;
-      return i;
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
     }
+  for (int c = 0; c < 2 * count; c ++) { 
+    rgbaf[c] = ctx_u8_to_float (rgba[c]);
+    ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf);
+    ((float*)out)[1] = rgbaf[3];
+    out = ((float*)out) + 2;
   }
-  return -1;
 }
 
-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)
+static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer)
 {
-  int id = 0;
-  if (eid)
-  {
-    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
-    {
-      if (ctx->texture[i].data &&
-          ctx->texture[i].eid &&
-          !strcmp (ctx->texture[i].eid, eid))
-      {
-        ctx->texture[i].frame = ctx->texture_cache->frame;
-        if (freefunc && user_data != (void*)23)
-          freefunc (pixels, user_data);
-        return ctx->texture[i].eid;
-      }
-      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++)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
     {
-      if (ctx->texture[i].data == NULL 
-          || (ctx->texture_cache->frame - ctx->texture[i].frame > 2))
-        id = i;
+      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;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF;
+#endif
     }
-  }
-  //int bpp = ctx_pixel_format_bits_per_pixel (format);
-  ctx_buffer_deinit (&ctx->texture[id]);
+  return ctx_fragment_color_GRAYAF;
+}
 
-  if (stride<=0)
-  {
-    stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
-  }
+ctx_float_porter_duff(GRAYAF, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-  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;
-  }
+#if CTX_INLINED_NORMAL
+ctx_float_porter_duff(GRAYAF, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
 
-  ctx_buffer_set_data (&ctx->texture[id],
-                       pixels, width, height,
-                       stride, format,
-                       freefunc, user_data);
-#if CTX_ENABLE_CM
-  ctx->texture[id].space = space;
+static void
+ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
+}
+
+static void
+ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
+}
+
+static void
+ctx_GRAYAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
 #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 ++)
+static void
+ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 2;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer);
+  rasterizer->comp = CTX_COV_PATH_FALLBACK;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
-       ascii[i*2]=hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
+      rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
+  //  rasterizer->fragment = NULL;
+      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;
     }
-    ascii[40]=0;
-    ctx->texture[id].eid = strdup (ascii);
+  else
+  {
+    rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
   }
-  return ctx->texture[id].eid;
-}
 
-void
-_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
-                                      CtxBuffer     *buffer)
-{
-   switch (buffer->format->pixel_format)
-   {
-#ifndef NO_BABL
-#if CTX_BABL
-     case CTX_FORMAT_RGBA8:
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_GRAYAF_clear_normal;
+  else
+    switch (gstate->blend_mode)
+    {
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
         {
-          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;
+          rasterizer->comp_op = ctx_GRAYAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = ctx_RGBA8_nop;
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = ctx_RGBA8_nop;
+#if 1
+              else //if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = ctx_GRAYAF_source_copy_normal_color;
 #endif
+              //else
+          //      rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
+//            rasterizer->fragment = NULL;
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
+//            rasterizer->fragment = NULL;
+            }
+            break;
+          default:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
+//          rasterizer->fragment = NULL;
+            break;
+          default:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
+            break;
+        }
+        break;
+    }
 #endif
-     default:
-       buffer->color_managed = buffer;
-   }
 }
 
+#endif
+#if CTX_ENABLE_GRAYF
 
-
-int ctx_utf8_len (const unsigned char first_byte)
+static void
+ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS)
 {
-  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;
+  float *dstf = (float*)dst;
+
+  float temp[count*2];
+  for (int i = 0; i < count; i++)
+  {
+    temp[i*2] = dstf[i];
+    temp[i*2+1] = 1.0f;
+  }
+  rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count);
+  for (int i = 0; i < count; i++)
+  {
+    dstf[i] = temp[i*2];
+  }
 }
 
+#endif
+#if CTX_ENABLE_BGRA8
 
-const char *ctx_utf8_skip (const char *s, int utf8_length)
+inline static void
+ctx_swap_red_green (uint8_t *rgba)
 {
-  int count;
-  if (!s)
-    { return NULL; }
-  for (count = 0; *s; s++)
+  uint32_t *buf  = (uint32_t *) rgba;
+  uint32_t  orig = *buf;
+  uint32_t  green_alpha = (orig & 0xff00ff00);
+  uint32_t  red_blue    = (orig & 0x00ff00ff);
+  uint32_t  red         = red_blue << 16;
+  uint32_t  blue        = red_blue >> 16;
+  *buf = green_alpha | red | blue;
+}
+
+static void
+ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  uint32_t *srci = (uint32_t *) buf;
+  uint32_t *dsti = (uint32_t *) rgba;
+  while (count--)
     {
-      if ( (*s & 0xC0) != 0x80)
-        { count++; }
-      if (count == utf8_length + 1)
-        { return s; }
+      uint32_t val = *srci++;
+      ctx_swap_red_green ( (uint8_t *) &val);
+      *dsti++      = val;
     }
-  return s;
 }
 
-//  XXX  :  unused
-int ctx_utf8_strlen (const char *s)
+static void
+ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  int count;
-  if (!s)
-    { return 0; }
-  for (count = 0; *s; s++)
-    if ( (*s & 0xC0) != 0x80)
-      { count++; }
-  return count;
+  ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count);
 }
 
-int
-ctx_unichar_to_utf8 (uint32_t  ch,
-                     uint8_t  *dest)
+static void
+ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS)
 {
-  /* 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)
+  // for better performance, this could be done without a pre/post conversion,
+  // by swapping R and B of source instead... as long as it is a color instead
+  // of gradient or image
+  //
+  //
+  uint8_t pixels[count * 4];
+  ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_BGRA8_to_RGBA8  (rasterizer, x0, &pixels[0], dst, count);
+}
+
+
+#endif
+#if CTX_ENABLE_CMYKAF
+
+static void
+ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+{
+  float *cmyka = (float*)out;
+  float _rgba[4 * count];
+  float *rgba = &_rgba[0];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
     {
-      dest[0] = (ch>>12) | 0xE0;
-      dest[1] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[2] = (ch & 0x3F) | 0x80;
-      return 3;
+      case CTX_SOURCE_TEXTURE:
+        ctx_fragment_image_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+      case CTX_SOURCE_COLOR:
+        ctx_fragment_color_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT:
+        ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+      case CTX_SOURCE_RADIAL_GRADIENT:
+        ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+#endif
+      default:
+        rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f;
+        break;
     }
-  if (ch < 0x110000)
+  for (int i = 0; i < count; i++)
+  {
+    cmyka[4]=rgba[3];
+    ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]);
+    cmyka += 5;
+    rgba += 4;
+  }
+}
+
+static void
+ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  float *cmyka = (float*)out;
+  float cmyka_in[5];
+  ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in);
+  for (int i = 0; i < count; i++)
+  {
+    for (int c = 0; c < 4; c ++)
     {
-      dest[0] = (ch>>18) | 0xF0;
-      dest[1] = ( (ch>>12) & 0x3F) | 0x80;
-      dest[2] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[3] = (ch & 0x3F) | 0x80;
-      return 4;
+      cmyka[c] = (1.0f - cmyka_in[c]);
     }
-  return 0;
+    cmyka[4] = cmyka_in[4];
+    cmyka += 5;
+  }
 }
 
-uint32_t
-ctx_utf8_to_unichar (const char *input)
+static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer)
 {
-  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;
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+    {
+      case CTX_SOURCE_COLOR:
+        return ctx_fragment_color_CMYKAF;
+    }
+  return ctx_fragment_other_CMYKAF;
 }
 
-#if CTX_RASTERIZER
-
+ctx_float_porter_duff (CMYKAF, 5,color,           rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff (CMYKAF, 5,generic,         rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
+#if CTX_INLINED_NORMAL
+ctx_float_porter_duff (CMYKAF, 5,color_normal,            rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_float_porter_duff (CMYKAF, 5,generic_normal,          rasterizer->fragment, CTX_BLEND_NORMAL)
 
-static int
-ctx_rect_intersect (const CtxIntRectangle *a, const CtxIntRectangle *b)
+static void
+ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  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;
+  ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count);
 }
 
 static void
-_ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
+ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  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];
-      }
-    }
+  ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count);
 }
 
-
 static void
-ctx_hasher_process (void *user_data, CtxCommand *command)
+ctx_CMYKAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxHasher *hasher = (CtxHasher*) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int aa = 15;//rasterizer->aa;
+  ctx_float_source_copy_normal_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
+#endif
 
-  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
-  ctx_interpret_style (rasterizer->state, entry, NULL);
+static void
+ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 5;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer);
+  rasterizer->comp = CTX_COV_PATH_FALLBACK;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
+      rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+ //     rasterizer->fragment = NULL;
+      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;
+    }
+  else
+  {
+    rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+  }
 
-  switch (c->code)
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_CMYKAF_clear_normal;
+#if 1
+  else
+    switch (gstate->blend_mode)
     {
-      case CTX_TEXT:
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
         {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, 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);
-           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);
+          rasterizer->comp_op = ctx_CMYKAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = ctx_RGBA8_nop;
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = ctx_RGBA8_nop;
+#if 1
+              else //if (((float*)rasterizer->color)[components-1] == 1.0f)
+                rasterizer->comp_op = ctx_CMYKAF_source_copy_normal_color;
+   //           else
+   //             rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
+              rasterizer->fragment = NULL;
 #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);
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
+   //         rasterizer->fragment = NULL;
+            }
+            break;
+          default:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal;
+            break;
         }
-        ctx_rasterizer_reset (rasterizer);
         break;
-      case CTX_STROKE_TEXT:
+      default:
+        switch (gstate->source_fill.type)
         {
-          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);
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
+    //      rasterizer->fragment = NULL;
+            break;
+          default:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+            break;
+        }
+        break;
+    }
+#endif
+#endif
+}
 
-           CtxIntRectangle shape_rect = {
-              rasterizer->x, rasterizer->y - height,
-              width, height * 2
-           };
+#endif
+#if CTX_ENABLE_CMYKA8
 
-#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);
-
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+static void
+ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
+{
+  for (int i = 0; i < count; i ++)
+    {
+      for (int c = 0; c < 4; c ++)
+        { dst[c] = ctx_u8_to_float ( (255-src[c]) ); }
+      dst[4] = ctx_u8_to_float (src[4]);
+      for (int c = 0; c < 4; c++)
+        { dst[c] *= dst[4]; }
+      src += 5;
+      dst += 5;
+    }
+}
+static void
+ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+{
+  for (int i = 0; i < count; i ++)
+    {
+      int a = ctx_float_to_u8 (src[4]);
+      if (a != 0 && a != 255)
+      {
+        float recip = 1.0f/src[4];
+        for (int c = 0; c < 4; c++)
+        {
+          dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip);
         }
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_GLYPH:
-         {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+      }
+      else
+      {
+        for (int c = 0; c < 4; c++)
+          dst[c] = 255 - ctx_float_to_u8 (src[c]);
+      }
+      dst[4]=a;
 
-          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);
+      src += 5;
+      dst += 5;
+    }
+}
 
-          float tx = rasterizer->x;
-          float ty = rasterizer->y;
-          float tw = width;
-          float th = height * 2;
+static void
+ctx_composite_CMYKA8 (CTX_COMPOSITE_ARGUMENTS)
+{
+  float pixels[count * 5];
+  ctx_CMYKA8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count);
+}
 
-          _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};
+#endif
+#if CTX_ENABLE_CMYK8
 
+static void
+ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
+{
+  for (int i = 0; i < count; i ++)
+    {
+      dst[0] = ctx_u8_to_float (255-src[0]);
+      dst[1] = ctx_u8_to_float (255-src[1]);
+      dst[2] = ctx_u8_to_float (255-src[2]);
+      dst[3] = ctx_u8_to_float (255-src[3]);
+      dst[4] = 1.0f;
+      src += 4;
+      dst += 5;
+    }
+}
+static void
+ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+{
+  for (int i = 0; i < count; i ++)
+    {
+      float c = src[0];
+      float m = src[1];
+      float y = src[2];
+      float k = src[3];
+      float a = src[4];
+      if (a != 0.0f && a != 1.0f)
+        {
+          float recip = 1.0f/a;
+          c *= recip;
+          m *= recip;
+          y *= recip;
+          k *= recip;
+        }
+      c = 1.0 - c;
+      m = 1.0 - m;
+      y = 1.0 - y;
+      k = 1.0 - k;
+      dst[0] = ctx_float_to_u8 (c);
+      dst[1] = ctx_float_to_u8 (m);
+      dst[2] = ctx_float_to_u8 (y);
+      dst[3] = ctx_float_to_u8 (k);
+      src += 5;
+      dst += 4;
+    }
+}
 
-#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);
+static void
+ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS)
+{
+  float pixels[count * 5];
+  ctx_CMYK8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], src, x0, coverage, count);
+  ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count);
+}
 #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;
+#if CTX_ENABLE_RGB8
 
-      case CTX_FILL:
-        {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
-          char ctx_sha1_hash[20];
+inline static void
+ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (const uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[1];
+      rgba[2] = pixel[2];
+      rgba[3] = 255;
+      pixel+=3;
+      rgba +=4;
+    }
+}
 
-          /* 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 - 2,
-          rasterizer->scan_min / aa - 2,
-          3+(rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV,
-          3+(rasterizer->scan_max - rasterizer->scan_min + 1) / aa
-        };
+inline static void
+ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = rgba[0];
+      pixel[1] = rgba[1];
+      pixel[2] = rgba[2];
+      pixel+=3;
+      rgba +=4;
+    }
+}
 
-        hash ^= (rasterizer->state->gstate.fill_rule * 23);
+#endif
+#if CTX_ENABLE_GRAY1
 
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = 255 * (*pixel & (1<< (x&7) ) );
+      rgba[1] = 255;
+      pixel+= ( (x&7) ==7);
+      x++;
+      rgba +=2;
+    }
+}
 
+inline static void
+ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  *pixel = 0;
+  while (count--)
+    {
+      int gray = rgba[0];
+      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
+      if (gray >= 127)
         {
-          int is = rasterizer->state->gstate.image_smoothing;
-          ctx_sha1_process(&sha1, (uint8_t*)&is, sizeof(int));
+          *pixel = *pixel | ((1<< (x&7) ) * (gray >= 127));
         }
-
-          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;
+#if 0
+      else
+      {
+          *pixel = *pixel & (~ (1<< (x&7) ) );
+      }
+#endif
+      if ( (x&7) ==7)
+        { pixel+=1;
+          if(count>0)*pixel = 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
-        };
-
-        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;
-
-        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
-        hash ^= (rasterizer->state->gstate.line_cap * 23);
-        hash ^= (rasterizer->state->gstate.source_stroke.type * 117);
-
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
-
-        uint32_t color;
-        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
-
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+      x++;
+      rgba +=2;
+    }
+}
 
-          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
-         */
+#else
 
-      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);
+inline static void
+ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      *((uint32_t*)(rgba))=0xff000000 + 0x00ffffff * ((*pixel & (1<< (x&7) ) )!=0);
+      pixel+= ( (x&7) ==7);
+      x++;
+      rgba +=4;
+    }
+}
 
-        
-        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:
+inline static void
+ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  *pixel = 0;
+  while (count--)
+    {
+      int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
+      if (gray <= 127)
         {
-        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?
+          //*pixel = *pixel & (~ (1<< (x&7) ) );
         }
-        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:
+      else
         {
-          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);
-          }
+          *pixel = *pixel | (1<< (x&7) );
         }
-        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));
+      if ( (x&7) ==7)
+        { pixel+=1;
+          if(count>0)*pixel = 0;
         }
-        break;
-#endif
+      x++;
+      rgba +=4;
     }
-  if (command->code == CTX_LINE_WIDTH)
+}
+#endif
+
+#endif
+#if CTX_ENABLE_GRAY2
+
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
     {
-      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;
+      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
+      val <<= 6;
+      rgba[0] = val;
+      rgba[1] = 255;
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
     }
 }
 
-static CtxRasterizer *
-ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width, int height, int cols, int 
rows)
+inline static void
+ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  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          = 15;
-
-  hasher->rows = rows;
-  hasher->cols = cols;
-
-  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
-  ctx_sha1_init (&hasher->sha1_fill);
-  ctx_sha1_init (&hasher->sha1_stroke);
-
-  return rasterizer;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = rgba[0];
+      val >>= 6;
+      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
+      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
+#else
 
-Ctx *ctx_hasher_new (int width, int height, int cols, int rows)
+inline static void
+ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  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;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
+      val <<= 6;
+      rgba[0] = val;
+      rgba[1] = val;
+      rgba[2] = val;
+      rgba[3] = 255;
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
-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];
+inline static void
+ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      val >>= 6;
+      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
+      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
+#endif
 
 #endif
-#if CTX_EVENTS
+#if CTX_ENABLE_GRAY4
 
-#if !__COSMOPOLITAN__
-#include <termios.h>
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
+      val <<= 4;
+      rgba[0] = val;
+      rgba[1] = 255;
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
+}
 
-#include <fcntl.h>
-#include <sys/ioctl.h>
+inline static void
+ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = rgba[0];
+      val >>= 4;
+      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
+      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
+}
+#else
+inline static void
+ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
+      val <<= 4;
+      rgba[0] = val;
+      rgba[1] = val;
+      rgba[2] = val;
+      rgba[3] = 255;
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
+}
+
+inline static void
+ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      val >>= 4;
+      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
+      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
+}
 #endif
 
-int ctx_terminal_width (void)
+#endif
+#if CTX_ENABLE_GRAY8
+
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  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);
-#if __COSMOPOLITAN__
-  /// XXX ?
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = 255;
+      pixel+=1;
+      rgba +=2;
+    }
+}
+
+inline static void
+ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = rgba[0];
+      pixel+=1;
+      rgba +=2;
+    }
+}
 #else
-  tcdrain(STDIN_FILENO);
+inline static void
+ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[0];
+      rgba[2] = pixel[0];
+      rgba[3] = 255;
+      pixel+=1;
+      rgba +=4;
+    }
+}
+
+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;
+  for (int i = 0; i < count; i ++)
+    {
+      pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4);
+    }
+}
 #endif
-  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;
 
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+#endif
+#if CTX_ENABLE_GRAYA8
+
+inline static void
+ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (const uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[0];
+      rgba[2] = pixel[0];
+      rgba[3] = pixel[1];
+      pixel+=2;
+      rgba +=4;
+    }
+}
+
+inline static void
+ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      pixel[1] = rgba[3];
+      pixel+=2;
+      rgba +=4;
+    }
+}
+
+#if CTX_NATIVE_GRAYA8
+CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out)
+{
+  out[0] = ctx_u8_color_rgb_to_gray (state, in);
+  out[1] = in[3];
+}
+
+#if CTX_GRADIENTS
+static void
+ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+        uint8_t *dst = (uint8_t*)out;
+  for (int i = 0; i < count;i ++)
   {
-    length += read (STDIN_FILENO, &buf[length], 1);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  if (length == -1)
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
   {
-    return 0;
+    uint8_t rgba[4];
+    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
+    ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst);
+   
   }
-  char *semi = strchr (buf, ';');
-  buf[length]=0;
-  if (semi) {semi++; semi = strchr (semi, ';');}
-  if (semi)
-  {
-    return atoi(semi + 1);
+
+
+#if CTX_DITHER
+  ctx_dither_graya_u8 ((uint8_t*)dst, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
+  dst += 2;
+  x += dx;
+  y += dy;
   }
-  return 0;
 }
 
-int ctx_terminal_height (void)
+#if 0
+static void
+ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  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);
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
+  uint8_t *rgba = (uint8_t *) out;
+  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);
+#if CTX_DITHER
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
+}
 #endif
-  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;
 
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+
+static void
+ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+        uint8_t *dst = (uint8_t*)out;
+  for (int i = 0; i < count;i ++)
   {
-    length += read (STDIN_FILENO, &buf[length], 1);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  if (length == -1)
+  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);
   {
-    return 0;
+    uint8_t rgba[4];
+    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
+    ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst);
   }
-  char *semi = strchr (buf, ';');
-  buf[length]=0;
-  if (semi)
-  {
-    return atoi(semi + 1);
+#if CTX_DITHER
+  ctx_dither_graya_u8 ((uint8_t*)dst, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
+  dst += 2;
+  x += dx;
+  y += dy;
   }
-  return 0;
 }
+#endif
 
-int ctx_terminal_cols (void)
+static void
+ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 80;
-  return ws.ws_col;
-} 
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  uint16_t *dst = (uint16_t*)out;
+  uint16_t pix;
+  ctx_color_get_graya_u8 (rasterizer->state, &g->color, (void*)&pix);
+  for (int i = 0; i <count; i++)
+  {
+    dst[i]=pix;
+  }
+}
 
-int ctx_terminal_rows (void)
+static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 25;
-  return ws.ws_row;
+  uint8_t rgba[4*count];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
+    {
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+    }
+  for (int i = 0; i < count; i++)
+    ctx_rgba_to_graya_u8 (rasterizer->state, &rgba[i*4], &((uint8_t*)out)[i*2]);
 }
 
+static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+    {
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYA8;
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYA8;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8;
+#endif
+    }
+  return ctx_fragment_color_GRAYA8;
+}
 
+//ctx_u8_porter_duff(GRAYA8, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
+#if CTX_INLINED_NORMAL
+//ctx_u8_porter_duff(GRAYA8, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
 
+static void
+ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
+}
 
-#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"
+static void
+ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
+}
 
-/*************************** input handling *************************/
+static void
+ctx_GRAYA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_source_over_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
 
-#if !__COSMOPOLITAN__
-#include <termios.h>
-#include <errno.h>
-#include <signal.h>
+static void
+ctx_GRAYA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
 #endif
 
-#define DELAY_MS  100  
+inline static int
+ctx_is_opaque_color (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  if (gstate->global_alpha_u8 != 255)
+    return 0;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  {
+    uint8_t ga[2];
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
+    return ga[1] == 255;
+  }
+  return 0;
+}
 
-#ifndef MIN
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#endif
+static void
+ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 2;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer);
+  rasterizer->comp_op  = ctx_GRAYA8_porter_duff_generic;
+  rasterizer->comp = CTX_COV_PATH_FALLBACK;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      ctx_fragment_color_GRAYA8 (rasterizer, 0,0, rasterizer->color, 1, 0,0);
+      if (gstate->global_alpha_u8 != 255)
+        for (int c = 0; c < components; c ++)
+          rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255;
+    }
 
-static int  size_changed = 0;       /* XXX: global state */
-static int  signal_installed = 0;   /* XXX: global state */
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_GRAYA8_clear_normal;
+  else
+    switch (gstate->blend_mode)
+    {
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+        {
+          rasterizer->comp_op = ctx_GRAYA8_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = ctx_RGBA8_nop;
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (rasterizer->color[components-1] == 0)
+                rasterizer->comp_op = ctx_RGBA8_nop;
+              else if (rasterizer->color[components-1] == 255)
+                rasterizer->comp_op = ctx_GRAYA8_source_copy_normal_color;
+              else
+                rasterizer->comp_op = ctx_GRAYA8_source_over_normal_color;
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal;
+            }
+            break;
+          default:
+            rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic;
+        break;
+    }
+#endif
+}
+#endif
 
-static const char *mouse_modes[]=
-{TERMINAL_MOUSE_OFF,
- TERMINAL_MOUSE_ON_BASIC,
- TERMINAL_MOUSE_ON_DRAG,
- TERMINAL_MOUSE_ON_FULL,
- NULL};
+#endif
+#if CTX_ENABLE_RGB332
 
-/* 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[]={  
+inline static void
+ctx_332_unpack (uint8_t pixel,
+                uint8_t *red,
+                uint8_t *green,
+                uint8_t *blue)
+{
+  *blue   = (pixel & 3) <<6;
+  *green = ( (pixel >> 2) & 7) <<5;
+  *red   = ( (pixel >> 5) & 7) <<5;
+  if (*blue > 223)  { *blue  = 255; }
+  if (*green > 223) { *green = 255; }
+  if (*red > 223)   { *red   = 255; }
+}
 
-  {"up",                  "↑",     "\033[A"},
-  {"down",                "↓",     "\033[B"},
-  {"right",               "→",     "\033[C"},
-  {"left",                "←",     "\033[D"},
+static inline uint8_t
+ctx_332_pack (uint8_t red,
+              uint8_t green,
+              uint8_t blue)
+{
+  uint8_t c  = (red >> 5) << 5;
+  c |= (green >> 5) << 2;
+  c |= (blue >> 6);
+  return c;
+}
 
-  {"shift-up",            "⇧↑",    "\033[1;2A"},
-  {"shift-down",          "⇧↓",    "\033[1;2B"},
-  {"shift-right",         "⇧→",    "\033[1;2C"},
-  {"shift-left",          "⇧←",    "\033[1;2D"},
+static inline void
+ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]);
+#if CTX_RGB332_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
+      else
+#endif
+        { rgba[3] = 255; }
+      pixel+=1;
+      rgba +=4;
+    }
+}
 
-  {"alt-up",              "^↑",    "\033[1;3A"},
-  {"alt-down",            "^↓",    "\033[1;3B"},
-  {"alt-right",           "^→",    "\033[1;3C"},
-  {"alt-left",            "^←",    "\033[1;3D"},
-
-  {"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"},
-
-  {"control-up",          "^↑",    "\033[1;5A"},
-  {"control-down",        "^↓",    "\033[1;5B"},
-  {"control-right",       "^→",    "\033[1;5C"},
-  {"control-left",        "^←",    "\033[1;5D"},
-
-  /* putty */
-  {"control-up",          "^↑",    "\033OA"},
-  {"control-down",        "^↓",    "\033OB"},
-  {"control-right",       "^→",    "\033OC"},
-  {"control-left",        "^←",    "\033OD"},
-
-  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
-  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
-  {"control-shift-right", "^⇧→",   "\033[1;6C"},
-  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
-
-  {"control-up",          "^↑",    "\033Oa"},
-  {"control-down",        "^↓",    "\033Ob"},
-  {"control-right",       "^→",    "\033Oc"},
-  {"control-left",        "^←",    "\033Od"},
-
-  {"shift-up",            "⇧↑",    "\033[a"},
-  {"shift-down",          "⇧↓",    "\033[b"},
-  {"shift-right",         "⇧→",    "\033[c"},
-  {"shift-left",          "⇧←",    "\033[d"},
-
-  {"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~"},
-
-  {"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 inline void
+ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+#if CTX_RGB332_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_332_pack (255, 0, 255); }
+      else
+#endif
+        { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); }
+      pixel+=1;
+      rgba +=4;
+    }
+}
 
-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;
+#endif
+#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED
 
-static void _nc_noraw (void)
+static inline void
+ctx_565_unpack (const uint16_t pixel,
+                uint8_t *red,
+                uint8_t *green,
+                uint8_t *blue,
+                const int byteswap)
 {
-  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
-    nc_is_raw = 0;
+  uint16_t byteswapped;
+  if (byteswap)
+    { byteswapped = (pixel>>8) | (pixel<<8); }
+  else
+    { byteswapped  = pixel; }
+  *blue   =  (byteswapped & 31) <<3;
+  *green = ( (byteswapped>>5) & 63) <<2;
+  *red   = ( (byteswapped>>11) & 31) <<3;
+#if 0
+  if (*blue > 248) { *blue = 255; }
+  if (*green > 248) { *green = 255; }
+  if (*red > 248) { *red = 255; }
+#endif
 }
 
-void
-nc_at_exit (void)
+static inline uint32_t
+ctx_565_unpack_32 (const uint16_t pixel,
+                   const int byteswap)
 {
-  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");
+  uint16_t byteswapped;
+  if (byteswap)
+    { byteswapped = (pixel>>8) | (pixel<<8); }
+  else
+    { byteswapped  = pixel; }
+  uint8_t blue   = (byteswapped & 31) <<3;
+  uint8_t green = ( (byteswapped>>5) & 63) <<2;
+  uint8_t red   = ( (byteswapped>>11) & 31) <<3;
+#if 0
+  if (*blue > 248) { *blue = 255; }
+  if (*green > 248) { *green = 255; }
+  if (*red > 248) { *red = 255; }
+#endif
+  return red +  (green << 8) + (blue << 16) + (0xff << 24);
 }
 
-static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
+static inline uint16_t
+ctx_565_pack (const uint8_t  red,
+              const uint8_t  green,
+              const uint8_t  blue,
+              const int      byteswap)
 {
-  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];
+  uint32_t c = (red >> 3) << 11;
+  c |= (green >> 2) << 5;
+  c |= blue >> 3;
+  if (byteswap)
+    { return (c>>8) | (c<<8); } /* swap bytes */
+  return c;
+}
 
-  n->mouse_x += relx * 0.1;
-  n->mouse_y += rely * 0.1;
+static inline uint16_t
+ctx_888_to_565 (uint32_t in, int byteswap)
+{
+  uint8_t *rgb=(uint8_t*)(&in);
+  return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap);
+}
 
-  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 inline uint32_t
+ctx_565_to_888 (uint16_t in, int byteswap)
+{
+  uint32_t ret = 0;
+  uint8_t *rgba=(uint8_t*)&ret;
+  ctx_565_unpack (in,
+                  &rgba[0],
+                  &rgba[1],
+                  &rgba[2],
+                  byteswap);
+  return ret;
+}
 
-  if (x) *x = n->mouse_x;
-  if (y) *y = n->mouse_y;
+#endif
+#if CTX_ENABLE_RGB565
 
-  if ((prev_state & 1) != (buf[0] & 1))
-    {
-      if (buf[0] & 1) ret = "mouse-press";
-    }
-  else if (buf[0] & 1)
-    ret = "mouse-drag";
 
-  if ((prev_state & 2) != (buf[0] & 2))
+static inline void
+ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      if (buf[0] & 2) ret = "mouse2-press";
+      ((uint32_t*)(rgba))[0] = ctx_565_unpack_32 (*pixel, 0);
+#if CTX_RGB565_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
+#endif
+      pixel+=1;
+      rgba +=4;
     }
-  else if (buf[0] & 2)
-    ret = "mouse2-drag";
+}
 
-  if ((prev_state & 4) != (buf[0] & 4))
+static inline void
+ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      if (buf[0] & 4) ret = "mouse1-press";
+#if CTX_RGB565_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_565_pack (255, 0, 255, 0); }
+      else
+#endif
+        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); }
+      pixel+=1;
+      rgba +=4;
     }
-  else if (buf[0] & 4)
-    ret = "mouse1-drag";
-
-  prev_state = buf[0];
-  return ret;
 }
 
-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)
-{
-  if (!mev_q)
-    return NULL;
-  *x = mev_x;
-  *y = mev_y;
-  mev_q = 0;
-  return mev_type;
-}
+static void
+ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS);
+static void
+ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS);
 
-static int mouse_has_event (Ctx *n)
+static void
+ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS)
 {
-  struct timeval tv;
-  int retval;
-
-  if (mouse_mode == NC_MOUSE_NONE)
-    return 0;
-
-  if (mev_q)
-    return 1;
-
-  if (n->mouse_fd == 0)
-    return 0;
-  return 0;
+#if 0
+  if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color))
+  {
+     int byteswap = 0;
+     uint32_t si    = *((uint32_t*)(src));
+     uint16_t si_16 = ctx_888_to_565 (si, byteswap);
+     uint32_t sval  = (si_16 & ( (31 << 11 ) | 31));
+     uint32_t sg = (si_16 & (63 << 5)) >> 5;
+     uint32_t si_a = si >> (24 + 3);
+     while (count--)
+     {
+        uint32_t di_16 = *((uint16_t*)(dst));
+        uint32_t cov = (*coverage) >> 3;
+        uint32_t racov = (32-((31+si_a*cov)>>5));
+        uint32_t dval = (di_16 & ( (31 << 11 ) | 31));
+        uint32_t dg = (di_16 >> 5) & 63; // faster outside than
+                                         // remerged as part of dval
+        *((uint16_t*)(dst)) =
+                ((               
+                  (((sval * cov) + (dval * racov)) >> 5)
+                 ) & ((31 << 11 )|31)) |
+                  ((((sg * cov) + (dg * racov)) & 63) );
 
-  {
-    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);
+     }
+     return;
   }
-
-  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;
-    }
-  return retval != 0;
+#endif
+  uint8_t pixels[count * 4];
+  ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count);
 }
+#endif
+#if CTX_ENABLE_RGB565_BYTESWAPPED
 
-
-static int _nc_raw (void)
+static inline void
+ctx_RGB565_BS_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  struct termios raw;
-  if (!isatty (STDIN_FILENO))
-    return -1;
-  if (!atexit_registered)
+  const uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      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;
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
-  tcflush(STDIN_FILENO, 1);
+      //ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 1);
+      ((uint32_t*)(rgba))[0] = ctx_565_unpack_32 (*pixel, 1);
+#if CTX_RGB565_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
+      else
+        { rgba[3] = 255; }
 #endif
-  return 0;
+      pixel+=1;
+      rgba +=4;
+    }
 }
 
-static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
+static inline void
+ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  int i;
-  int matches = 0;
-
-  if (!strncmp (buf, "\033[M", MIN(length,3)))
+  uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      if (length >= 6)
-        return 9001;
-      return 2342;
+#if CTX_RGB565_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_565_pack (255, 0, 255, 1); }
+      else
+#endif
+        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 1); }
+      pixel+=1;
+      rgba +=4;
     }
-  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_has_event (Ctx  *n, int delay_ms)
+static void
+ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS)
 {
-  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;
+  uint8_t pixels[count * 4];
+  ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count);
 }
+#endif
 
-const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
+static CtxPixelFormatInfo ctx_pixel_formats[]=
 {
-  unsigned char buf[20];
-  int length;
-
-
-  if (x) *x = -1;
-  if (y) *y = -1;
-
-  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]);
-
+#if CTX_ENABLE_RGBA8
   {
-    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_nct_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);
-  }
-
-  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";
-}
-
-int ctx_nct_consume_events (Ctx *ctx)
-{
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
-
+    CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
+    NULL, NULL, NULL, ctx_setup_RGBA8
+  },
+#endif
+#if CTX_ENABLE_BGRA8
   {
-    float x, y;
-    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
-
-    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);
+    CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8,
+  },
 #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, "return", 0);
-      else if (!strcmp (event, "idle"))
-      {
-      }
-      else
-      ctx_key_press (ctx, 0, event, 0);
-    }
+#if CTX_ENABLE_GRAYF
+  {
+    CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
+    NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF,
+  },
+#endif
+#if CTX_ENABLE_GRAYAF
+  {
+    CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
+    NULL, NULL, NULL, ctx_setup_GRAYAF,
+  },
+#endif
+#if CTX_ENABLE_RGBAF
+  {
+    CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF,
+    NULL, NULL, NULL, ctx_setup_RGBAF,
+  },
+#endif
+#if CTX_ENABLE_RGB8
+  {
+    CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_GRAY1
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8,
+    ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8,
+    ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAY2
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8,
+    ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8,
+    ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAY4
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8,
+    ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8,
+    ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAY8
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8,
+    ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAYA8
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8,
+    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_RGB332
+  {
+    CTX_FORMAT_RGB332, 3, 8, 4, 10, 12, CTX_FORMAT_RGBA8,
+    ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332,
+    ctx_composite_convert, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_RGB565
+  {
+    CTX_FORMAT_RGB565, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
+    ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565,
+    ctx_composite_RGB565, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_RGB565_BYTESWAPPED
+  {
+    CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
+    ctx_RGB565_BS_to_RGBA8,
+    ctx_RGBA8_to_RGB565_BS,
+    ctx_composite_RGB565_BS, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_CMYKAF
+  {
+    CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, NULL, ctx_setup_CMYKAF,
+  },
+#endif
+#if CTX_ENABLE_CMYKA8
+  {
+    CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKAF,
+  },
+#endif
+#if CTX_ENABLE_CMYK8
+  {
+    CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYKAF,
+  },
+#endif
+#if CTX_ENABLE_YUV420
+  {
+    CTX_FORMAT_YUV420, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
+    NULL, NULL, ctx_composite_convert, ctx_setup_RGBA8,
+  },
+#endif
+  {
+    CTX_FORMAT_NONE
   }
+};
 
-  return 1;
-}
 
-const char *ctx_native_get_event (Ctx *n, int timeoutms)
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
 {
-  static unsigned char buf[256];
-  int length;
-
-  if (!signal_installed)
+  for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++)
     {
-      _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]);
-
-    int got_event = 0;
-  {
-    int elapsed = 0;
-
-    do {
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      got_event = ctx_nct_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
-      if (size_changed)
+      if (ctx_pixel_formats[i].pixel_format == format)
         {
-          size_changed = 0;
-          return "size-changed";
+          return &ctx_pixel_formats[i];
         }
-      /* 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_nct_has_event (n, 5);
     }
   return NULL;
 }
 
-const char *ctx_key_get_label (Ctx  *n, const char *nick)
-{
-  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;
-}
+#endif
+#if CTX_RASTERIZER
+#define CTX_AA_HALFSTEP2   (CTX_FULL_AA/2)
+#define CTX_AA_HALFSTEP    ((CTX_FULL_AA/2)+1)
 
-void _ctx_mouse (Ctx *term, int mode)
+static void
+ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
+
+static inline void
+_ctx_setup_compositor (CtxRasterizer *rasterizer)
 {
-  //if (term->is_st && mode > 1)
-  //  mode = 1;
-  if (mode != mouse_mode)
+  if (CTX_UNLIKELY (rasterizer->comp_op==0))
   {
-    printf ("%s", mouse_modes[mode]);
-    fflush (stdout);
-  }
-  mouse_mode = mode;
-}
-
-
-#endif
-
-#if !__COSMOPOLITAN__
-#include <sys/time.h>
-#endif
+    rasterizer->format->setup (rasterizer);
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
+  switch (rasterizer->state->gstate.source_fill.type)
+  {
+    case CTX_SOURCE_LINEAR_GRADIENT:
+    case CTX_SOURCE_RADIAL_GRADIENT:
+      ctx_gradient_cache_prime (rasterizer);
+      break;
+    case CTX_SOURCE_TEXTURE:
 
+      _ctx_matrix_multiply (&rasterizer->state->gstate.source_fill.transform,
+                            &rasterizer->state->gstate.source_fill.set_transform,
+                            &rasterizer->state->gstate.transform);
 
-#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
+      ctx_matrix_invert (&rasterizer->state->gstate.source_fill.transform);
 
-#if CTX_EVENTS
-#if !__COSMOPOLITAN__
-#if CTX_THREADS
-#include <threads.h>
+      if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed)
+        _ctx_texture_prepare_color_management (rasterizer,
+        rasterizer->state->gstate.source_fill.texture.buffer);
+      break;
+  }
 #endif
 #endif
-static struct timeval start_time;
-
-static void
-_ctx_init_ticks (void)
-{
-  static int done = 0;
-  if (done)
-    return;
-  done = 1;
-  gettimeofday (&start_time, NULL);
+  }
 }
 
-static inline unsigned long
-_ctx_ticks (void)
+#define CTX_FULL_AA 15
+inline static void
+ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
+                               uint8_t * dst,
+                               int       x,
+                               uint8_t * coverage,
+                               int       count)
 {
-  struct timeval measure_time;
-  gettimeofday (&measure_time, NULL);
-  return usecs (measure_time) - usecs (start_time);
+  if (CTX_UNLIKELY(rasterizer->format->apply_coverage))
+    rasterizer->format->apply_coverage(rasterizer, dst, rasterizer->color, x, coverage, count);
+  else
+    /* it is faster to dispatch in this condition, than using a shared
+     * direct trampoline
+     */
+    rasterizer->comp_op (rasterizer, dst, rasterizer->color, x, coverage, count);
 }
 
-unsigned long
-ctx_ticks (void)
+static void
+ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
 {
-  _ctx_init_ticks ();
-  return _ctx_ticks ();
+  /* FIXME XXX we only have one gradient, but might need separate gradients
+   * for fill/stroke !
+   * 
+   */
+  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++; }
 }
 
-uint32_t ctx_ms (Ctx *ctx)
+static inline int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
 {
-  return _ctx_ticks () / 1000;
-}
-
-
-typedef enum _CtxFlags CtxFlags;
-
-enum _CtxFlags {
-   CTX_FLAG_DIRECT = (1<<0),
-};
+  CtxSegment entry = {CTX_EDGE, {{0},}};
+  rasterizer->scan_min = ctx_mini (y1, rasterizer->scan_min);
+  rasterizer->scan_max = ctx_maxi (y1, rasterizer->scan_max);
 
+  rasterizer->col_min = ctx_mini (x1, rasterizer->col_min);
+  rasterizer->col_max = ctx_maxi (x1, rasterizer->col_max);
 
-int _ctx_max_threads = 1;
-int _ctx_enable_hash_cache = 1;
-#if CTX_SHAPE_CACHE
-extern int _ctx_shape_cache_enabled;
-#endif
+  entry.data.s16[0]=rasterizer->inner_x;
+  entry.data.s16[1]=rasterizer->inner_y;
 
-#if CTX_THREADS
-static mtx_t _ctx_texture_mtx;
-#endif
+  entry.data.s16[2]=x1;
+  entry.data.s16[3]=y1;
 
-void _ctx_texture_lock (void)
-{
-#if CTX_THREADS
-  mtx_lock (&_ctx_texture_mtx);
+  rasterizer->inner_x = x1;
+  rasterizer->inner_y = y1;
+#if 0
+  if (entry.data.s16[3] < entry.data.s16[1])
+  {
+    entry = ctx_segment_s16 (CTX_EDGE_FLIPPED,
+                            entry.data.s16[2], entry.data.s16[3],
+                            entry.data.s16[0], entry.data.s16[1]);
+  }
 #endif
-}
 
-void _ctx_texture_unlock (void)
-{
-#if CTX_THREADS
-  mtx_unlock (&_ctx_texture_mtx);
-#endif
+  return ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry);
 }
 
-
-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
-  }
+#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
-}
 
-int ctx_count (Ctx *ctx)
-{
-  return ctx->drawlist.count;
-}
-
-
-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);
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
-#endif
-  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;
+float ctx_shape_cache_rate = 0.0;
+#if CTX_SHAPE_CACHE
+int   _ctx_shape_cache_enabled = 1;
 
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
-  {
-    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;
-}
+//static CtxShapeCache ctx_cache = {{NULL,}, 0};
 
-extern int _ctx_damage_control;
+static long ctx_shape_cache_hits   = 0;
+static long ctx_shape_cache_misses = 0;
 
-static void ctx_list_backends()
-{
-    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");
-}
 
-Ctx *ctx_new_ui (int width, int height)
+/* this returns the buffer to use for rendering, it always
+   succeeds..
+ */
+static inline CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int 
height)
 {
-#if CTX_TILED
-  if (getenv ("CTX_DAMAGE_CONTROL"))
+  /* use both some high and some low bits  */
+  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
+  int i;
   {
-    const char * val = getenv ("CTX_DAMAGE_CONTROL");
-    if (!strcmp (val, "0") ||
-        !strcmp (val, "off"))
-      _ctx_damage_control = 0;
-    else
-      _ctx_damage_control = 1;
+    static int i = 0;
+    i++;
+    if (i>1000)
+      {
+        ctx_shape_cache_rate = ctx_shape_cache_hits * 100.0  / (ctx_shape_cache_hits+ctx_shape_cache_misses);
+        i = 0;
+        ctx_shape_cache_hits = 0;
+        ctx_shape_cache_misses = 0;
+      }
   }
-#endif
+// 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;
 
-  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;
-  }
-#if CTX_SHAPE_CACHE
-  if (getenv ("CTX_SHAPE_CACHE"))
-  {
-    const char * val = getenv ("CTX_SHAPE_CACHE");
-    if (!strcmp (val, "0"))
-      _ctx_shape_cache_enabled = 0;
-    if (!strcmp (val, "off"))
-      _ctx_shape_cache_enabled = 0;
-  }
-#endif
+  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++; }
+          ctx_shape_cache_hits ++;
+          return entry;
+        }
 
-  if (getenv ("CTX_THREADS"))
-  {
-    int val = atoi (getenv ("CTX_THREADS"));
-    _ctx_max_threads = val;
-  }
+      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
-  {
-    _ctx_max_threads = 2;
-#ifdef _SC_NPROCESSORS_ONLN
-    _ctx_max_threads = sysconf (_SC_NPROCESSORS_ONLN) / 2;
-#endif
-  }
-  
-#if CTX_THREADS
-  mtx_init (&_ctx_texture_mtx, mtx_plain);
-#endif
+    {
+        rasterizer->shape_cache.entries[i] = (CtxShapeEntry *) calloc (size, 1);
+    }
 
-  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
-  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
+  ctx_shape_cache_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];
+}
 
-  //fprintf (stderr, "ctx using %i threads\n", _ctx_max_threads);
-  const char *backend = getenv ("CTX_BACKEND");
+#endif
 
-  if (backend && !strcmp (backend, ""))
-    backend = NULL;
-  if (backend && !strcmp (backend, "auto"))
-    backend = NULL;
-  if (backend && !strcmp (backend, "list"))
-  {
-    ctx_list_backends ();
-    exit (-1);
-  }
+static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
+{
+  int x = 0;
+  int y = 0;
 
-  Ctx *ret = NULL;
+  CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0];
 
-  /* we do the query on auto but not on directly set ctx
-   *
-   */
-  if ((backend && !strcmp(backend, "ctx")) ||
-      (backend == NULL && is_in_ctx ()))
-  {
-    if (!backend || !strcmp (backend, "ctx"))
+  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++)
     {
-      // full blown ctx protocol - in terminal or standalone
-      ret = ctx_new_ctx (width, height);
+      CtxSegment *entry = &(((CtxSegment*)(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;
+}
 
-#if CTX_SDL
-  if (!ret && getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "SDL")))
-      ret = ctx_new_sdl (width, height);
-  }
+static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
+{
+  int x = 0;
+  int y = 0;
+  int count = rasterizer->edge_list.count;
+  if (CTX_UNLIKELY (count == 0))
+     return 0;
+  CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0];
+#if CTX_SHAPE_CACHE
+#if 1
+  int ox = entry->data.s16[2];
+  int oy = entry->data.s16[3];
 #endif
-
-#if CTX_FB
-  if (!ret && !getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "drm")))
-    ret = ctx_new_fb (width, height, 1);
-
-    if (!ret)
+  uint32_t hash = rasterizer->edge_list.count;
+  hash = (ox & CTX_SUBDIV);
+  hash *= CTX_SHAPE_CACHE_PRIME1;
+  hash += (oy & CTX_SUBDIV);
+#endif
+  //CtxSegment *entry = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0];
+  for (int i = 0; i < count; i++)
     {
-      if ((backend==NULL) || (!strcmp (backend, "fb")))
-        ret = ctx_new_fb (width, height, 0);
-    }
-  }
+#if CTX_SHAPE_CACHE
+      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;
 #endif
-
-#if CTX_RASTERIZER
-  // braille in terminal
-  if (!ret)
-  {
-    if ((backend==NULL) || (!strcmp (backend, "term")))
-    ret = ctx_new_term (width, height);
-  }
-  if (!ret)
-  {
-    if ((backend==NULL) || (!strcmp (backend, "termimg")))
-    ret = ctx_new_termimg (width, height);
-  }
+#if 1
+      if (entry->data.s16[3] < entry->data.s16[1])
+        {
+          *entry = ctx_segment_s16 (CTX_EDGE_FLIPPED,
+                            entry->data.s16[2], entry->data.s16[3],
+                            entry->data.s16[0], entry->data.s16[1]);
+        }
 #endif
-  if (!ret)
-  {
-    fprintf (stderr, "no interactive ctx backend\n");
-    ctx_list_backends ();
-    exit (2);
-  }
-  ctx_get_event (ret); // enables events
-  return ret;
-}
+      entry++;
+    }
+#if CTX_SHAPE_CACHE
+  return hash;
 #else
-void _ctx_texture_unlock (void)
-{
-}
-void _ctx_texture_lock (void)
-{
-}
-
+  return 0;
 #endif
-void _ctx_resized (Ctx *ctx, int width, int height, long time);
+}
 
-void ctx_set_size (Ctx *ctx, int width, int height)
+static inline void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
 {
-#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);
-  }
-#endif
+  if (rasterizer->has_shape && rasterizer->has_prev)
+    {
+      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
+      rasterizer->has_prev = 0;
+    }
 }
 
-#if CTX_EVENTS
-typedef struct CtxIdleCb {
-  int (*cb) (Ctx *ctx, void *idle_data);
-  void *idle_data;
+static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  float tx = x; float ty = y;
+  int aa = 15;//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);
+    }
 
-  void (*destroy_notify)(void *destroy_data);
-  void *destroy_data;
+  tx = (tx - rasterizer->blit_x) * CTX_SUBDIV;
+  ty = ty * aa;
 
-  int   ticks_full;
-  int   ticks_remaining;
-  int   is_idle;
-  int   id;
-} CtxIdleCb;
+  rasterizer->inner_x = tx;
+  rasterizer->inner_y = ty;
 
-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  */
+  rasterizer->scan_min = ctx_mini (ty, rasterizer->scan_min);
+  rasterizer->scan_max = ctx_maxi (ty, rasterizer->scan_max);
 
-  events->tap_delay_hold = 1000;
-  events->tap_hysteresis = 32;  /* XXX: should be ppi dependent */
+  rasterizer->col_min = ctx_mini (tx, rasterizer->col_min);
+  rasterizer->col_max = ctx_maxi (tx, rasterizer->col_max);
 }
 
-
-void _ctx_idle_iteration (Ctx *ctx)
+static inline void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y)
 {
-  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)
-  {
-    return;
-  }
-  for (l = ctx->events.idles; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
+  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 (item->ticks_remaining >= 0)
-      item->ticks_remaining -= tick_delta;
+  ty = ctx_maxf (MIN_Y, ty);
+  ty = ctx_minf (MAX_Y, ty);
+  
+  ctx_rasterizer_add_point (rasterizer, tx * CTX_SUBDIV, ty * CTX_FULL_AA);//rasterizer->aa);
 
-    if (item->ticks_remaining < 0)
+  if (CTX_UNLIKELY(rasterizer->has_prev<=0))
     {
-      if (item->cb (ctx, item->idle_data) == 0)
-        ctx_list_prepend (&to_remove, item);
-      else
-        item->ticks_remaining = item->ticks_full;
+#if 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);
+      }
+#endif
+      //ox -= rasterizer->blit_x;
+      //oy = ctx_maxf (oy, MIN_Y);
+      //oy = ctx_minf (oy, MAX_Y);
+      CtxSegment *entry = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->edge_list.count-1];
+      //entry->data.s16[0] = ox * CTX_SUBDIV;
+      //entry->data.s16[1] = oy * CTX_FULL_AA;
+      entry->code = CTX_NEW_EDGE;
+      rasterizer->has_prev = 1;
     }
-  }
-  for (l = to_remove; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
-  }
+  rasterizer->has_shape = 1;
+  rasterizer->y         = y;
+  rasterizer->x         = x;
 }
 
 
-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)
+CTX_INLINE static float
+ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
 {
-  CtxEvents *events = &ctx->events;
-  if (events->n_bindings +1 >= CTX_MAX_KEYBINDINGS)
-  {
-    fprintf (stderr, "warning: binding overflow\n");
-    return;
-  }
-  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++;
+  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);
 }
 
-void ctx_add_key_binding (Ctx *ctx,
-                          const char *key,
-                          const char *action,
-                          const char *label,
-                          CtxCb       cb,
-                          void       *cb_data)
+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)
 {
-  ctx_add_key_binding_full (ctx, key, action, label, cb, cb_data, NULL, NULL);
+  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
+  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
 }
 
-void ctx_clear_bindings (Ctx *ctx)
+static inline 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)
 {
-  CtxEvents *events = &ctx->events;
-  int i;
-  for (i = 0; events->bindings[i].nick; i ++)
+  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 (CTX_UNLIKELY( (dx*dx+dy*dy) < tolerance))
+        /* bailing - because for the mid-point straight line difference is
+           tiny */
+        { return; }
+      dx = sx - ex;
+      dy = ey - ey;
+      if (CTX_UNLIKELY( (dx*dx+dy*dy) < tolerance))
+        /* bailing on tiny segments */
+        { return; }
+    }
+  if (iteration < 8)
   {
-    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);
+  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);
   }
-  memset (&events->bindings, 0, sizeof (events->bindings));
-  events->n_bindings = 0;
 }
 
 static void
-ctx_collect_events (CtxEvent *event, void *data, void *data2);
-static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2)
+ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
+                         float x0, float y0,
+                         float x1, float y1,
+                         float x2, float y2)
 {
-  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))
+  //float tolerance =
+  //  1.0f*(ctx_pow2 (rasterizer->state->gstate.transform.m[0][0]) +
+  //  ctx_pow2 (rasterizer->state->gstate.transform.m[1][1]));
+  float tolerance = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+  float ox = rasterizer->x;
+  float oy = rasterizer->y;
+  //tolerance *= tolerance;
+  tolerance = 2.0/(tolerance*tolerance);
+  ox = rasterizer->state->x;
+  oy = rasterizer->state->y;
+  //tolerance = 10.0/(tolerance*tolerance);
+  //tolerance = 10.0f/tolerance;
+#if 0 // 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 1
+    if(
+        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
+        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
+        (maxx < rasterizer->blit_x) ||
+        (maxy < rasterizer->blit_y) )
     {
-      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"))
+    else
+#endif
     {
-      if (events->bindings[i].cb)
-      {
-        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
-        if (event->stop_propagate)
-          return;
-      }
+      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_collect_events (event, data1, data2);
+  }
+  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);
 }
 
-CtxBinding *ctx_get_bindings (Ctx *ctx)
+static void
+ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
 {
-  return &ctx->events.bindings[0];
+  //if (CTX_UNLIKELY(x == 0.f && y == 0.f))
+  //{ return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_move_to (rasterizer, x, y);
 }
 
-void ctx_remove_idle (Ctx *ctx, int handle)
+static void
+ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
 {
-  CtxList *l;
-  CtxList *to_remove = NULL;
+  //if (CTX_UNLIKELY(x== 0.f && y==0.f))
+  //  { return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_line_to (rasterizer, x, y);
+}
 
-  if (!ctx->events.idles)
-  {
-    return;
-  }
-  for (l = ctx->events.idles; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
-    if (item->id == handle)
-      ctx_list_prepend (&to_remove, item);
-  }
-  for (l = to_remove; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
-  }
+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);
 }
 
-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)
+
+static int
+ctx_rasterizer_find_texture (CtxRasterizer *rasterizer,
+                             const char *eid)
 {
-  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;
+  int no;
+  for (no = 0; no < CTX_MAX_TEXTURES; no++)
+  {
+    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 ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+static void
+ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
+                            const char *eid,
+                            float x,
+                            float y)
 {
-  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
+  int is_stroke = (rasterizer->state->source != 0);
+  CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)?
+                        &rasterizer->state->gstate.source_stroke:
+                        &rasterizer->state->gstate.source_fill;
+  rasterizer->state->source = 0;
+
+  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
+  {
+    rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame;
+  }
+  source->type = CTX_SOURCE_TEXTURE;
+  source->texture.buffer = &rasterizer->texture_source->texture[no];
+  ctx_matrix_identity (&source->set_transform);
+  ctx_matrix_translate (&source->set_transform, x, y);
 }
 
-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)
+
+static void ctx_rasterizer_define_texture (CtxRasterizer *rasterizer,
+                                           const char *eid,
+                                           int width,
+                                           int height,
+                                           int format,
+                                           char unsigned *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;
+  _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
+                     */
+
+  ctx_rasterizer_set_texture (rasterizer, eid, 0.0, 0.0);
+  _ctx_texture_unlock ();
 }
 
-int ctx_add_idle (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+
+CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp)
 {
-  return ctx_add_idle_full (ctx, idle_cb, idle_data, NULL, NULL);
+  const CtxSegment *a = (const CtxSegment *) ap;
+  const CtxSegment *b = (const CtxSegment *) bp;
+  return a->data.s16[1] - b->data.s16[1];
 }
 
-#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)
+CTX_INLINE static int ctx_edge_qsort_partition (CtxSegment *A, int low, int high)
 {
-  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;
+  CtxSegment pivot = A[ (high+low) /2];
+  int i = low;
+  int j = high;
+  while (i <= j)
+    {
+      while (ctx_compare_edges (&A[i], &pivot) < 0) { i ++; }
+      while (ctx_compare_edges (&pivot, &A[j]) < 0) { j --; }
+      if (i <= j)
+        {
+          CtxSegment tmp = A[i];
+          A[i] = A[j];
+          A[j] = tmp;
+          i++;
+          j--;
+        }
     }
-  }
-#endif
-  return ret;
+  return i;
 }
 
-#if CTX_EVENTS
-void _ctx_item_ref (CtxItem *item)
+static inline void ctx_edge_qsort (CtxSegment *entries, int low, int high)
 {
-  if (item->ref_count < 0)
   {
-    fprintf (stderr, "EEEEK!\n");
+    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); }
   }
-  item->ref_count++;
 }
 
+static CTX_INLINE void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
+{
+  ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
+}
 
-void _ctx_item_unref (CtxItem *item)
+static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
 {
-  if (item->ref_count <= 0)
-  {
-    fprintf (stderr, "EEEEK!\n");
-    return;
-  }
-  item->ref_count--;
-  if (item->ref_count <=0)
-  {
+  int scanline = rasterizer->scanline;
+  int next_scanline = rasterizer->scanline + CTX_FULL_AA;
+  int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
+  //if (rasterizer->fast_aa)
+    limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA;
+  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
+  int *edges = rasterizer->edges;
+  for (int i = 0; i < rasterizer->active_edges; i++)
     {
-      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);
-      }
+      CtxSegment *segment = segments + edges[i];
+      int edge_end = segment->data.s16[3]-1;
+      if ((edge_end < scanline))
+        {
+
+          int dx_dy = abs(segment->delta);
+          rasterizer->needs_aa3  -= (dx_dy > limit3);
+          rasterizer->needs_aa5  -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5);
+          rasterizer->needs_aa15 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15);
+          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
+          rasterizer->active_edges--;
+          i--;
+        }
+      else if (edge_end < next_scanline)
+        rasterizer->ending_edges++;
     }
-    if (item->path)
+#if 0
+  // we should - but for 99% of the cases we do not need to, so we skip it
+  for (int i = 0; i < rasterizer->pending_edges; i++)
     {
-      //cairo_path_destroy (item->path);
+      int edge_end = 
((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[3]-1;
+      if (edge_end < scanline + CTX_FULL_AA)
+        rasterizer->ending_edges++;
     }
-    free (item);
-  }
+#endif
 }
 
+inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
+{
+  rasterizer->scanline += count;
+  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
+  for (int i = 0; i < rasterizer->active_edges; i++)
+    {
+      CtxSegment *segment = segments + rasterizer->edges[i];
+      segment->val += segment->delta * count;
+    }
+  for (int i = 0; i < rasterizer->pending_edges; i++)
+    {
+      CtxSegment *segment = segments + rasterizer->edges[CTX_MAX_EDGES-1-i];
+      segment->val += segment->delta * count;
+    }
+}
 
-static int
-path_equal (void *path,
-            void *path2)
+/* 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
+*/
+CTX_INLINE static void ctx_edge2_insertion_sort (CtxSegment *segments, int *entries, int count)
 {
-  //  XXX
-  return 0;
+  for(int i=1; i<count; i++)
+   {
+     int temp = entries[i];
+     int j = i-1;
+     while (j >= 0 && segments[temp].val - segments[entries[j]].val < 0)
+     {
+       entries[j+1] = entries[j];
+       j--;
+     }
+     entries[j+1] = temp;
+   }
 }
 
-void ctx_listen_set_cursor (Ctx      *ctx,
-                            CtxCursor cursor)
+CTX_INLINE static int ctx_edge2_compare2 (CtxSegment *segments, int a, int b)
 {
-  if (ctx->events.last_item)
-  {
-    ctx->events.last_item->cursor = cursor;
-  }
+  CtxSegment *seg_a = &segments[a];
+  CtxSegment *seg_b = &segments[b];
+  int minval_a = ctx_mini (seg_a->val - seg_a->delta * CTX_AA_HALFSTEP2, seg_a->val + seg_a->delta * 
CTX_AA_HALFSTEP);
+  int minval_b = ctx_mini (seg_b->val - seg_b->delta * CTX_AA_HALFSTEP2, seg_b->val + seg_b->delta * 
CTX_AA_HALFSTEP);
+  return minval_a - minval_b;
 }
 
-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_INLINE static void ctx_edge2_insertion_sort2 (CtxSegment *segments, int *entries, int count)
 {
-  if (!ctx->events.frozen)
-  {
-    CtxItem *item;
+  for(int i=1; i<count; i++)
+   {
+     int temp = entries[i];
+     int j = i-1;
+     while (j >= 0 && ctx_edge2_compare2 (segments, temp, entries[j]) < 0)
+     {
+       entries[j+1] = entries[j];
+       j--;
+     }
+     entries[j+1] = temp;
+   }
+}
 
-    /* early bail for listeners outside screen  */
-    /* XXX: fixme respect clipping */
+inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int apply2_sort)
+{
+  int miny;
+  CtxSegment *entries = (CtxSegment*)&rasterizer->edge_list.entries[0];
+  rasterizer->horizontal_edges = 0;
+  rasterizer->ending_edges = 0;
+  for (int i = 0; i < rasterizer->pending_edges; i++)
     {
-      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;
-      }
+      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[1] - 1 <= rasterizer->scanline)
+        {
+          if (CTX_LIKELY(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--;
+            }
+        }
     }
+  int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
+  //if (rasterizer->fast_aa)
+    limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA;
+  int scanline = rasterizer->scanline;
+  int next_scanline = scanline + CTX_FULL_AA;
+  int edge_pos = rasterizer->edge_pos;
+  int edge_count = rasterizer->edge_list.count;
+  int *edges = rasterizer->edges;
+  while ((edge_pos < edge_count &&
+         (miny=entries[edge_pos].data.s16[1]-1)  <= next_scanline))
+    {
+      int maxy=entries[edge_pos].data.s16[3]-1;
+      if ((rasterizer->active_edges < CTX_MAX_EDGES-2 &&
+          maxy >= scanline))
+        {
+          int dy = (entries[edge_pos].data.s16[3] - 1 - miny);
+          if ((dy))
+            {
+              int yd = scanline - miny;
+              int no = rasterizer->active_edges;
+              rasterizer->active_edges++;
+              int index = edges[no] = edge_pos;
+              int x0 = entries[index].data.s16[0];
+              int x1 = entries[index].data.s16[2];
+              int dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
+              entries[index].delta = dx_dy;
+              entries[index].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER +
+                                         (yd * dx_dy);
 
-    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);
-
-    if (ctx->events.items)
-    {
-      CtxList *l;
-      for (l = ctx->events.items; l; l = l->next)
-      {
-        CtxItem *item2 = l->data;
+              {
+                int abs_dx_dy = abs(dx_dy);
+                rasterizer->needs_aa3  += (abs_dx_dy > limit3);
+                rasterizer->needs_aa5  += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5);
+                rasterizer->needs_aa15 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15);
+              }
 
-        /* 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;
+              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))
+                  {
+                    edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
+                    rasterizer->edges[no];
+                    rasterizer->pending_edges++;
+                    rasterizer->active_edges--;
+                  }
+                }
+            }
+          else
+            rasterizer->horizontal_edges ++;
         }
-      }
+      edge_pos++;
     }
-    item->ref_count       = 1;
-    ctx->events.last_item = item;
-    ctx_list_prepend_full (&ctx->events.items, item, (void*)_ctx_item_unref, NULL);
-  }
+    rasterizer->edge_pos = edge_pos;
+    ctx_rasterizer_discard_edges (rasterizer);
+    if (apply2_sort)
+      ctx_edge2_insertion_sort2 ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, 
rasterizer->active_edges);
+    else
+      ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, 
rasterizer->active_edges);
 }
 
-void ctx_event_stop_propagate (CtxEvent *event)
-{
-  if (event)
-    event->stop_propagate = 1;
-}
 
-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
+#undef CTX_CMPSWP
+
+static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, int minx, int maxx, uint8_t 
*coverage, int *first_col, int *last_col)
+{
+  int scanline     = rasterizer->scanline - CTX_FULL_AA; // we do the
+                                                 // post process after
+                                                 // coverage generation icnrement
+#if CTX_ENABLE_SHADOW_BLUR
+  if (CTX_UNLIKELY(rasterizer->in_shadow))
   {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
+    float radius = rasterizer->state->gstate.shadow_blur;
+    int dim = 2 * radius + 1;
+    if (CTX_UNLIKELY (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;
+    }
   }
+#endif
 
-  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)
+#if CTX_ENABLE_CLIP
+  if (CTX_UNLIKELY(rasterizer->clip_buffer &&  !rasterizer->clip_rectangle))
   {
-    x = 0;
-    y = 0;
-    width = 0;
-    height = 0;
+    /* perhaps not working right for clear? */
+    int y = scanline / CTX_FULL_AA;//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] = (255 + coverage[x] * clip_line[x-rasterizer->blit_x])>>8;
+#endif
+    }
   }
-  else
+  if (CTX_UNLIKELY(rasterizer->aa == 1))
   {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
+    for (int x = minx; x <= maxx; x ++)
+     coverage[x] = coverage[x] > 127?255:0;
   }
-
-  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
 }
 
+#define CTX_EDGE(no)      entries[edges[no]]
+#define CTX_EDGE_YMIN     (segment->data.s16[1]-1)
 
-static void ctx_report_hit_region (CtxEvent *event,
-                       void     *data,
-                       void     *data2)
-{
-  const char *id = data;
+#define UPDATE_PARITY \
+        { \
+          if (scanline!=CTX_EDGE_YMIN)\
+            parity = (is_winding)? \
+             parity + -1+2*(segment->code == CTX_EDGE_FLIPPED) : \
+                        1 - parity;\
+        }
 
-  fprintf (stderr, "hit region %s\n", id);
-  // XXX: NYI
+inline static void
+ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
+                                  int            minx,
+                                  int            maxx,
+                                  uint8_t       *coverage,
+                                  int            is_winding,
+                                  const uint8_t  aa_factor)
+{
+  CtxSegment *entries      = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
+  int        *edges        = rasterizer->edges;
+  int         scanline     = rasterizer->scanline;
+  int         active_edges = rasterizer->active_edges;
+  int         parity       = 0;
+  coverage -= minx;
+  uint8_t fraction = 255/aa_factor;
+  for (int t = 0; t < active_edges -1;t++)
+    {
+      CtxSegment *segment = &entries[edges[t]];
+      UPDATE_PARITY;
+
+      if (parity)
+        {
+          CtxSegment *next_segment = &entries[edges[t+1]];
+          const int x0 = segment->val;
+          const int x1 = next_segment->val;
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart >> 8;
+          int last      = grayend   >> 8;
+
+          if ((first < minx))
+          { 
+            first = minx;
+            graystart=0;
+          }
+          if ((last > maxx))
+          {
+            last = maxx;
+            grayend=255;
+          }
+
+          graystart = fraction- (graystart&0xff)/aa_factor;
+          grayend   = (grayend & 0xff) / aa_factor;
+
+          if (first < last)
+          {
+              coverage[first] += graystart;
+              for (int x = first + 1; x < last; x++)
+                coverage[x]  += fraction;
+              coverage[last] += grayend;
+          }
+          else if (first == last)
+            coverage[first] += (graystart-(fraction-grayend));
+        }
+   }
 }
 
-void ctx_add_hit_region (Ctx *ctx, const char *id)
+inline static void
+ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer,
+                                      int            minx,
+                                      int            maxx,
+                                      uint8_t       *coverage,
+                                      int            is_winding)
 {
-  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);
-}
+  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
+  int      *edges = rasterizer->edges;
+  int scanline     = rasterizer->scanline;
+  int active_edges = rasterizer->active_edges;
+  int parity = 0;
+  coverage -= minx;
+  for (int t = 0; t < active_edges -1;t++)
+    {
+      CtxSegment *segment = &entries[edges[t]];
+      UPDATE_PARITY;
 
-typedef struct _CtxGrab CtxGrab;
+      if (parity)
+        {
+          CtxSegment *next_segment = &entries[edges[t+1]];
+          const int x0        = segment->val;
+          const int x1        = next_segment->val;
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart >> 8;
+          int last      = grayend   >> 8;
 
-struct _CtxGrab
-{
-  CtxItem *item;
-  int      device_no;
-  int      timeout_id;
-  int      start_time;
-  float    x; // for tap and hold
-  float    y;
-  CtxEventType  type;
-};
+          if ((first < minx))
+          { 
+            first = minx;
+            graystart=0;
+          }
+          if ((last > maxx))
+          {
+            last = maxx;
+            grayend=255;
+          }
 
-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);
+          graystart = 255 - (graystart&0xff);
+          grayend   = (grayend & 0xff);
+
+          if (first < last)
+          {
+              coverage[first] += graystart;
+#if 0
+              for (int x = first + 1; x < last; x++)
+                coverage[x] = 255;
+#else
+              memset(&coverage[first+1], 255, last-(first+1));
+#endif
+              coverage[last]  += grayend;
+          }
+          else if (first == last)
+            coverage[first] += (graystart-(255-grayend));
+        }
+   }
 }
 
-static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
+static inline uint32_t
+ctx_over_RGBA8 (uint32_t dst, uint32_t src, uint32_t cov)
 {
-  ctx_list_remove (&ctx->events.grabs, grab);
-  grab_free (ctx, grab);
+  uint32_t si_ga = (src & 0xff00ff00) >> 8;
+  uint32_t si_rb = src & 0x00ff00ff;
+  uint32_t si_a  = si_ga >> 16;
+  uint32_t rcov  = ((255+si_a * cov)>>8)^255;
+  uint32_t di_ga = ( dst & 0xff00ff00) >> 8;
+  uint32_t di_rb = dst & 0x00ff00ff;
+  return
+     ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
+      (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
 }
 
-static CtxGrab *device_add_grab (Ctx *ctx, int device_no, CtxItem *item, CtxEventType type)
+
+static inline uint32_t
+ctx_over_RGBA8_full (uint32_t dst, uint32_t src)
 {
-  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;
+  uint32_t si_ga = (src & 0xff00ff00) >> 8;
+  uint32_t si_rb = src & 0x00ff00ff;
+  uint32_t si_a  = si_ga >> 16;
+  uint32_t rcov  = si_a^255;
+  uint32_t di_ga = (dst & 0xff00ff00) >> 8;
+  uint32_t di_rb = dst & 0x00ff00ff;
+  return
+     ((((si_rb * 255) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
+      (((si_ga * 255) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
 }
 
-static CtxList *_ctx_device_get_grabs (Ctx *ctx, int device_no)
+static inline uint32_t
+ctx_over_RGBA8_2 (uint32_t dst, uint32_t si_ga, uint32_t si_rb, uint32_t si_a, uint32_t cov)
 {
-  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;
+  uint32_t rcov  = ((si_a * cov)/255)^255;
+  uint32_t di_ga = (dst & 0xff00ff00) >> 8;
+  uint32_t di_rb = dst & 0x00ff00ff;
+  return
+     ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
+      (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
 }
 
-static void _mrg_restore_path (Ctx *ctx, void *path)  //XXX
+static inline uint32_t
+ctx_over_RGBA8_full_2 (uint32_t dst, uint32_t si_ga_full, uint32_t si_rb_full, uint32_t si_a)
 {
-  //int i;
-  //cairo_path_data_t *data;
-  //cairo_new_path (cr);
-  //cairo_append_path (cr, path);
+  uint32_t rcov = si_a^255;
+  uint32_t di_ga = ( dst & 0xff00ff00) >> 8;
+  uint32_t di_rb = dst & 0x00ff00ff;
+  return
+     ((((si_rb_full) + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
+      (((si_ga_full) + (di_ga * rcov)) & 0xff00ff00);
 }
 
-CtxList *_ctx_detect_list (Ctx *ctx, float x, float y, CtxEventType type)
+static inline void ctx_span_set_color (uint32_t *dst_pix, uint32_t val, int count)
 {
-  CtxList *a;
-  CtxList *ret = NULL;
+  if (count>0)
+  while(count--)
+    *dst_pix++=val;
+}
 
-  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))
-  {
-    for (a = ctx->events.items; a; a = a->next)
+inline static void
+ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer,
+                                        int            minx,
+                                        int            maxx,
+                                        uint8_t       *coverage,
+                                        int            is_winding,
+                                        CtxCovPath     comp)
+{
+  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
+  int *edges  = rasterizer->edges;
+  int scanline        = rasterizer->scanline;
+  const int bpp             = rasterizer->format->bpp/8;
+  int active_edges    = rasterizer->active_edges;
+  int parity        = 0;
+  const uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
+  const uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
+  const uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
+  const uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
+  const uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
+  const uint32_t si_a  = si_ga >> 16;
+
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf) +
+         (rasterizer->blit_stride * (scanline / CTX_FULL_AA));
+  int accumulator_x=0;
+  uint8_t accumulated = 0;
+  for (int t = 0; t < active_edges -1;t++)
     {
-      CtxItem *item = a->data;
-      if (item->types & type)
-      {
-        ctx_list_prepend (&ret, item);
-        return ret;
-      }
-    }
-    return NULL;
-  }
-
-  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);
+      CtxSegment *segment = &entries[edges[t]];
+      UPDATE_PARITY;
 
-    if (u >= item->x0 && v >= item->y0 &&
-        u <  item->x1 && v <  item->y1 && 
-        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
-        item->cursor)))
-    {
-      if (item->path)
-      {
-        _mrg_restore_path (ctx, item->path);
-        if (ctx_in_fill (ctx, u, v))
+       if (parity)
         {
-          ctx_begin_path (ctx);
-          ctx_list_prepend (&ret, item);
+          CtxSegment   *next_segment = &entries[edges[t+1]];
+          const int x0        = segment->val;
+          const int x1        = next_segment->val;
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart >> 8;
+          int last      = grayend   >> 8;
+
+          if ((first < minx))
+          { 
+            first = minx;
+            graystart=0;
+          }
+          if ((last > maxx))
+          {
+            last = maxx;
+            grayend=255;
+          }
+
+          graystart = 255 - (graystart&0xff);
+          grayend   = (grayend & 0xff);
+
+          if (accumulated)
+          {
+            if (accumulator_x == first)
+            {
+              graystart += accumulated;
+            }
+            else
+            {
+              uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)]);
+              switch (comp)
+              {
+                case CTX_COV_PATH_COPY:
+                  *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated);
+                  break;
+                case CTX_COV_PATH_OVER:
+                  *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated);
+                  break;
+                default:
+                  ctx_rasterizer_apply_coverage (rasterizer, (uint8_t*)dst_pix, accumulator_x, &accumulated, 
1);
+              }
+            }
+            accumulated = 0;
+          }
+
+          if (first < last)
+          {
+            switch (comp)
+            {
+              case CTX_COV_PATH_COPY:
+            {
+              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
+              *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, graystart);
+
+              dst_pix++;
+              ctx_span_set_color (dst_pix, src_pix, last - first - 1);
+#if 0
+              for (int i = first + 1; i < last; i++)
+              {
+                *dst_pix = src_pix;
+                dst_pix++;
+              }
+#endif
+            }
+            break;
+              case CTX_COV_PATH_OVER:
+            {
+              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
+              *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, graystart);
+              dst_pix++;
+              for (int i = first + 1; i < last; i++)
+              {
+                *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a);
+                dst_pix++;
+              }
+            }
+            break;
+              case CTX_COV_PATH_COPY_FRAGMENT:
+            {
+              float u0 = 0; float v0 = 0;
+              float ud = 0; float vd = 0;
+              uint8_t gs = graystart;
+              ctx_RGBA8_source_copy_normal_fragment (rasterizer, &dst[(first * bpp)], NULL, first, &gs, 1);
+              ctx_init_uv (rasterizer, first+1, last-first-1, &u0, &v0, &ud, &vd);
+              rasterizer->fragment (rasterizer, u0, v0, &dst[(first+1)*bpp], last-first-1, ud, vd);
+            }
+            break;
+              case CTX_COV_PATH_OVER_FRAGMENT:
+            {
+              uint8_t gs = graystart;
+              ctx_RGBA8_source_over_normal_fragment (rasterizer, &dst[(first * bpp)], NULL, first, &gs, 1);
+              ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
+                                                     &dst[((first+1)*bpp)], NULL, first + 1, NULL, 
last-first-1);
+            }
+            break;
+              default:
+            {
+              uint8_t opaque[last-first];
+              memset (opaque, 255, sizeof (opaque));
+              opaque[0] = graystart;
+              ctx_rasterizer_apply_coverage (rasterizer,
+                                             &dst[(first * bpp)], first, opaque, last-first);
+            }
+            }
+            accumulated = grayend;
+          }
+          else if (first == last)
+          {
+            accumulated = (graystart-(255-grayend));
+          }
+          accumulator_x = last;
         }
-        ctx_begin_path (ctx);
-      }
-      else
-      {
-        ctx_list_prepend (&ret, item);
-      }
-    }
-  }
-  return ret;
+   }
+
+   if (accumulated)
+   {
+     uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)]);
+     switch (comp)
+     {
+       case CTX_COV_PATH_COPY:
+         *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated);
+         break;
+       case CTX_COV_PATH_OVER:
+         *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated);
+         break;
+       default:
+         ctx_rasterizer_apply_coverage (rasterizer, (uint8_t*)dst_pix, accumulator_x, &accumulated, 1);
+     }
+   }
 }
 
-CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
+inline static int ctx_rasterizer_is_simple (CtxRasterizer *rasterizer)
 {
-  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 (rasterizer->fast_aa == 0 ||
+      rasterizer->ending_edges ||
+      rasterizer->pending_edges)
+   return 0;
+  int *edges  = rasterizer->edges;
+  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
+
+  int active_edges = rasterizer->active_edges;
+  for (int t = 0; t < active_edges -1;t++)
+    {
+      CtxSegment *segment0 = segments + edges[t];
+      CtxSegment *segment1 = segments + edges[t+1];
+      const int delta0    = segment0->delta;
+      const int delta1    = segment1->delta;
+      const int x0        = segment0->val;
+      const int x1        = segment1->val;
+      int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
+      int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
+      int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
+      int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
+      if (x1_end < x0_end   ||
+          x1_start < x0_end ||
+          x1_end < x0_start
+         )
+         return 0;
+    }
+  return 1;
 }
 
-static int
-_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
-{
-  static CtxEvent s_event;
-  CtxEvent transformed_event;
-  int i;
 
+inline static void
+ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer,
+                                         int            minx,
+                                         int            maxx,
+                                         uint8_t       *coverage,
+                                         int            is_winding)
+{
+  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
+  int *edges  = rasterizer->edges;
+  int scanline        = rasterizer->scanline;
+  int active_edges    = rasterizer->active_edges;
+  int parity        = 0;
 
-  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;
+  coverage -= minx;
 
-  {
-    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;
+  const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
+  const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
 
-    if ((type & CTX_DRAG_PRESS) ||
-        (type & CTX_DRAG_MOTION) ||
-        (type & CTX_MOTION))   /* probably a worthwhile check for the performance 
-                                  benefit
-                                */
+  for (int t = 0; t < active_edges -1;t++)
     {
-      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;
-    }
+      CtxSegment   *segment = &entries[edges[t]];
+      UPDATE_PARITY;
 
+       if (parity)
+        {
+          CtxSegment   *next_segment = &entries[edges[t+1]];
+          const int x0        = segment->val;
+          const int x1        = next_segment->val;
+          const int delta0    = segment->delta;
+          const int delta1    = next_segment->delta;
 
-    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;
-  }
+          int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
+          int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
+          int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
+          int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
 
-  transformed_event.state = ctx->events.modifier_state;
-  transformed_event.type = type;
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart >> 8;
+          int last      = grayend   >> 8;
 
-  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;
+          first = ctx_maxi (first, minx);
+          last  = ctx_mini (last, maxx);
+
+          if (first < last)
+          {
+            int pre = 1;
+            int post = 1;
+
+            if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
+            {
+              graystart = 255 - (graystart&0xff);
+              coverage[first] += graystart;
+            }
+            else
+            {
+              int u0 = ctx_mini (x0_start, x0_end);
+              int u1 = ctx_maxi (x0_start, x0_end);
+              u0 = ctx_maxi (u0, minx_);
+              u1 = ctx_mini (u1, maxx_);
+              u1 = ctx_maxi (u1, minx_);
+              u0 = ctx_mini (u0, maxx_);
+  
+              int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
+              int count = 0;
+
+              int mod = (255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)) *
+                         (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255);
+              int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);
+
+              for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
+              {
+                coverage[us + count] = (u - u0 + mod) / sum;
+                count++;
+              }
+              pre = (us+count-1)-first+1;
+            }
+  
+            if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
+            {
+               grayend   = (grayend & 0xff);
+               coverage[last] += grayend;
+            }
+            else
+            {
+              int u0 = ctx_mini (x1_start, x1_end);
+              int u1 = ctx_maxi (x1_start, x1_end);
+              u0 = ctx_maxi (u0, minx_);
+              u1 = ctx_mini (u1, maxx_);
+              u1 = ctx_maxi (u1, minx_);
+              u0 = ctx_mini (u0, maxx_);
+              int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
+              int count = 0;
+              int mod = ((255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)+64) *
+                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255));
+              int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 1.25)/255);
+              for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
+              {
+                coverage[us + count] = 255-((u - u0 + mod)/ sum);
+                count++;
+              }
+              post = last-us+1;
+            }
+            for (int i = first + pre; i <= last - post; i++)
+              coverage[i] = 255;
+          }
+          else if (first == last)
+          {
+            graystart = 255 - (graystart&0xff);
+            grayend   = (grayend & 0xff);
+            coverage[last]+=(graystart-(255-grayend));
+          }
+        }
+   }
 }
-#endif
 
-#if CTX_EVENTS
 
-#if !__COSMOPOLITAN__
-#include <stdatomic.h>
-#endif
+inline static void
+ctx_rasterizer_generate_coverage_apply2 (CtxRasterizer *rasterizer,
+                                         int            minx,
+                                         int            maxx,
+                                         uint8_t       *coverage,
+                                         int            is_winding,
+                                         CtxCovPath     comp)
+{
+  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
+  int *edges          = rasterizer->edges;
+  int  scanline       = rasterizer->scanline;
+  const int  bpp      = rasterizer->format->bpp/8;
+  int  active_edges   = rasterizer->active_edges;
+  int  parity         = 0;
+
+  const uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
+  const uint32_t si_ga      = ((uint32_t*)rasterizer->color)[1];
+  const uint32_t si_rb      = ((uint32_t*)rasterizer->color)[2];
+  const uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
+  const uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
+  const uint32_t si_a  = src_pix >> 24;
+
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf) +
+         (rasterizer->blit_stride * (scanline / CTX_FULL_AA));
 
-int ctx_native_events = 0;
-#if CTX_SDL
-int ctx_sdl_events = 0;
-int ctx_sdl_consume_events (Ctx *ctx);
-#endif
+  coverage -= minx;
 
-#if CTX_FB
-int ctx_fb_events = 0;
-int ctx_fb_consume_events (Ctx *ctx);
-#endif
+  const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
+  const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
 
-int ctx_nct_consume_events (Ctx *ctx);
-int ctx_nct_has_event (Ctx  *n, int delay_ms);
-int ctx_ctx_consume_events (Ctx *ctx);
+  int accumulated_x0 = 65538;
+  int accumulated_x1 = 65536;
 
+  for (int t = 0; t < active_edges -1;t++)
+    {
+      CtxSegment   *segment = &entries[edges[t]];
+      UPDATE_PARITY;
 
+       if (parity)
+        {
+          CtxSegment   *next_segment = &entries[edges[t+1]];
+          const int x0        = segment->val;
+          const int x1        = next_segment->val;
+          const int delta0    = segment->delta;
+          const int delta1    = next_segment->delta;
 
-void ctx_consume_events (Ctx *ctx)
-{
-#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);
-}
+          int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
+          int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
+          int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
+          int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
 
-int ctx_has_event (Ctx *ctx, int timeout)
-{
-#if CTX_SDL
-  if (ctx_sdl_events)
-  {
-    return SDL_WaitEventTimeout (NULL, timeout);
-  }
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
-  else
-#endif
-  if (ctx_native_events)
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
-  else
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart >> 8;
+          int last      = grayend   >> 8;
 
-  ctx_consume_events (ctx);
-  if (ctx->events.events)
-    return 1;
-  return 0;
-}
+          first = ctx_maxi (first, minx);
+          last = ctx_mini (last, maxx);
+          graystart = 255 - (graystart&0xff);
+          grayend   = (grayend & 0xff);
 
-#if CTX_FB
-static int ctx_fb_get_mice_fd (Ctx *ctx);
-#endif
+          if (first < last)
+          {
+            int pre = 1;
+            int post = 1;
 
-void ctx_get_event_fds (Ctx *ctx, int *fd, int *count)
-{
-#if CTX_SDL
-  if (ctx_sdl_events)
-  {
-    *count = 0;
-  }
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-  {
-    int mice_fd = ctx_fb_get_mice_fd (ctx);
-    fd[0] = STDIN_FILENO;
-    if (mice_fd)
-    {
-      fd[1] = mice_fd;
-      *count = 2;
-    }
-    else
-    {
-      *count = 1;
-    }
-  }
-  else
+          if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
+          {
+             coverage[first] += graystart;
+
+            accumulated_x1 = first;
+            accumulated_x0 = ctx_mini (accumulated_x0, first);
+          }
+          else
+          {
+            int u0 = ctx_mini (x0_start, x0_end);
+            int u1 = ctx_maxi (x0_start, x0_end);
+            u0 = ctx_maxi (u0, minx_);
+            u1 = ctx_mini (u1, maxx_);
+            u1 = ctx_maxi (u1, minx_);
+            u0 = ctx_mini (u0, maxx_);
+
+            int mod = (255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)) *
+                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255);
+            int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);
+
+            int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
+            int count = 0;
+            for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
+            {
+              coverage[us + count] = (u - u0 + mod) / sum;
+              count++;
+            }
+            pre = (us+count-1)-first+1;
+
+#if 0 // the CTX_UNLIKELY helps - but this is a big constant overhead
+      // which ends up penalizing us in benchmarks, it needs to be
+      // a shape with really large interior emptiness for this
+      // to be worthwhile
+          if (CTX_UNLIKELY(
+              us - accumulated_x1 > 16 &&
+              accumulated_x1-accumulated_x0>=0
+                          ))
+          {
+             switch (comp)
+             {
+                case CTX_COV_PATH_OVER:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                    {
+                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
+                      dst_i++;
+                    }
+                }
+                break;
+                case CTX_COV_PATH_COPY:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                  {
+                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
+                    dst_i++;
+                  }
+                }
+                  break;
+                default:
+                ctx_rasterizer_apply_coverage (rasterizer,
+                          &dst[((accumulated_x0) * bpp)],
+                          accumulated_x0,
+                          &coverage[accumulated_x0],
+                          accumulated_x1-accumulated_x0+1);
+             }
+          }
 #endif
-  if (ctx_native_events)
-  {
-    fd[0] = STDIN_FILENO;
-    *count = 1;
-  }
-  else
-  {
-    fd[0] = STDIN_FILENO;
-    *count = 1;
-  }
-}
 
-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;
+            accumulated_x0 = ctx_mini (accumulated_x0, us);
+            accumulated_x1 = us + count - 1;
+          }
 
-  ctx_consume_events (ctx);
+   if (accumulated_x1-accumulated_x0>=0)
+          {
+             switch (comp)
+             {
+                case CTX_COV_PATH_OVER:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                    {
+                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
+                      dst_i++;
+                    }
+                }
+                break;
+                case CTX_COV_PATH_COPY:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                  {
+                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
+                    dst_i++;
+                  }
+                }
+                  break;
+                default:
+                ctx_rasterizer_apply_coverage (rasterizer,
+                          &dst[((accumulated_x0) * bpp)],
+                          accumulated_x0,
+                          &coverage[accumulated_x0],
+                          accumulated_x1-accumulated_x0+1);
+             }
+             accumulated_x0 = 65538;
+             accumulated_x1 = 65536;
+          }
 
-  if (ctx->events.events)
-    {
-      event_copy = *((CtxEvent*)(ctx->events.events->data));
-      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
-      return &event_copy;
-    }
-  return NULL;
+          if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
+          {
+             coverage[last] += grayend;
+             accumulated_x1 = last;
+             accumulated_x0 = last;
+          }
+          else
+          {
+            int u0 = ctx_mini (x1_start, x1_end);
+            int u1 = ctx_maxi (x1_start, x1_end);
+            u0 = ctx_maxi (u0, minx_);
+            u1 = ctx_mini (u1, maxx_);
+            u1 = ctx_maxi (u1, minx_);
+            u0 = ctx_mini (u0, maxx_);
+            int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
+            int count = 0;
+
+            int mod = ((255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)+64) *
+                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255));
+            int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 1.25)/255);
+
+            for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
+            {
+                coverage[us + count] = 255-((u - u0 + mod)/ sum);
+              count++;
+            }
+            post = last-us+1;
+
+            accumulated_x1 = us + count;
+            accumulated_x0 = us;
+          }
+            switch (comp)
+            {
+              case CTX_COV_PATH_COPY:
+            {
+              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
+              dst_pix+=pre;
+              ctx_span_set_color (dst_pix, src_pix, last-first-pre-post + 1);
+            }
+            break;
+              case CTX_COV_PATH_OVER:
+            {
+              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
+              dst_pix+=pre;
+              for (int i = first + pre; i <= last - post; i++)
+              {
+                *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a);
+                dst_pix++;
+              }
+            }
+            break;
+              case CTX_COV_PATH_COPY_FRAGMENT:
+              {
+              int width = last-first-pre-post+1;
+            if (width>0)
+            {
+              {
+                float u0 = 0; float v0 = 0;
+                float ud = 0; float vd = 0;
+                ctx_init_uv (rasterizer, first+pre, width, &u0, &v0, &ud, &vd);
+                rasterizer->fragment (rasterizer, u0, v0, &dst[(first+pre)*bpp],                             
         width, ud, vd);
+              }
+            }
+              }
+            break;
+              case CTX_COV_PATH_OVER_FRAGMENT:
+              {
+                int width = last-first-pre-post+1;
+                if (width>0)
+                ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
+                               &dst[((first+pre)*bpp)],
+                               NULL,
+                               first + pre,
+                               NULL,
+                               width);
+              }
+              break;
+              default:
+              {
+                int width = last-first-pre-post+1;
+                if (width > 0)
+                {
+                uint8_t opaque[width];
+                memset (opaque, 255, sizeof (opaque));
+                ctx_rasterizer_apply_coverage (rasterizer,
+                            &dst[((first + pre) * bpp)],
+                            first + pre,
+                            opaque,
+                            width);
+                }
+              }
+            }
+            //}
+          }
+          else if (first == last)
+          {
+            coverage[last]+=(graystart-(255-grayend));
+
+            accumulated_x1 = last;
+            accumulated_x0 = ctx_mini (accumulated_x0, last);
+          }
+        }
+   }
+
+   if (accumulated_x1-accumulated_x0>=0)
+   {
+             switch (comp)
+             {
+                case CTX_COV_PATH_OVER:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                    {
+                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
+                      dst_i++;
+                    }
+                }
+                break;
+                case CTX_COV_PATH_COPY:
+                {
+                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
+                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
+                  {
+                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
+                    dst_i++;
+                  }
+                }
+                  break;
+                default:
+                ctx_rasterizer_apply_coverage (rasterizer,
+                          &dst[((accumulated_x0) * bpp)],
+                          accumulated_x0,
+                          &coverage[accumulated_x0],
+                          accumulated_x1-accumulated_x0+1);
+             }
+   }
 }
 
-static int
-_ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y)
+#undef CTX_EDGE_Y0
+#undef CTX_EDGE
+
+static inline void
+ctx_rasterizer_reset (CtxRasterizer *rasterizer)
 {
-  CtxList *l;
-  event->stop_propagate = 0;
-  for (l = items; l; l = l->next)
+  rasterizer->pending_edges   = 0;
+  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->scanline        = 0;
+  if (CTX_LIKELY(!rasterizer->preserve))
   {
-    _ctx_emit_cb_item (ctx, l->data, event, type, x, y);
-    if (event->stop_propagate)
-      return event->stop_propagate;
+    rasterizer->scan_min      = 5000;
+    rasterizer->scan_max      = -5000;
+    rasterizer->col_min       = 5000;
+    rasterizer->col_max       = -5000;
   }
-  return 0;
+  //rasterizer->comp_op       = NULL; // keep comp_op cached 
+  //     between rasterizations where rendering attributes are
+  //     nonchanging
 }
 
-/*
- * 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)
+static void
+ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, const int fill_rule 
+#if CTX_SHAPE_CACHE
+                                ,CtxShapeEntry *shape
+#endif
+                               )
 {
-  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      is_winding = fill_rule == CTX_FILL_RULE_WINDING;
+  const CtxCovPath comp = rasterizer->comp;
+  const int real_aa = rasterizer->aa;
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-  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 scan_start = rasterizer->blit_y * CTX_FULL_AA;
+  int scan_end   = scan_start + (rasterizer->blit_height - 1) * CTX_FULL_AA;
+  const int blit_width = rasterizer->blit_width;
+  const 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;
+  const int blit_stride = rasterizer->blit_stride;
 
-    //int focus_radius = 2;
-    if (current)
-      _ctx_item_ref (current);
+  rasterizer->prev_active_edges = -1;
+  if (
+#if CTX_SHAPE_CACHE
+    !shape &&
+#endif
+    maxx > blit_max_x - 1)
+    { maxx = blit_max_x - 1; }
 
-    if (ctx->events.prev[device_no])
+  minx = ctx_maxi (rasterizer->state->gstate.clip_min_x, minx);
+  maxx = ctx_mini (rasterizer->state->gstate.clip_max_x, maxx);
+  minx = ctx_maxi (0, minx); // redundant?
+  if (minx >= maxx)
     {
-      {
-#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_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_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;
+  int coverage_size = 
+#if CTX_SHAPE_CACHE
+                  shape?shape->width:
+#endif
+                  sizeof (_coverage);
+
+#if CTX_SHAPE_CACHE
+  if (shape)
+    {
+      coverage = &shape->data[0];
     }
-    if (current)
+#endif
+  //ctx_assert (coverage);
+  rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA);
+#if CTX_SHAPE_CACHE
+  if (shape)
     {
-#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);
-      }
+      scan_start = rasterizer->scan_min;
+      scan_end   = rasterizer->scan_max;
+    }
+  else
 #endif
-      _ctx_emit_cb_item (ctx, current, NULL, CTX_ENTER, x, y);
-      ctx->events.prev[device_no] = current;
+    {
+      if (rasterizer->scan_min > scan_start)
+        {
+          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA);
+          scan_start = rasterizer->scan_min;
+        }
+      scan_end = ctx_mini (rasterizer->scan_max, scan_end);
+    }
+  if (CTX_UNLIKELY(rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start ))
+    { 
+       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / 
CTX_FULL_AA);
+       scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; 
     }
+  scan_end = ctx_mini (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA, scan_end);
+  if (CTX_UNLIKELY(scan_start > scan_end ||
+      (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) ||
+      (scan_end < (rasterizer->blit_y) * CTX_FULL_AA)))
+  { 
+    /* not affecting this rasterizers scanlines */
+    ctx_rasterizer_reset (rasterizer);
+    return;
   }
-  current = _ctx_detect (ctx, x, y, type);
-  //fprintf (stderr, "%p\n", current);
-  return current;
-}
-
-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, };
 
-  event.ctx = ctx;
-  event.time = ctx_ms (ctx);
+  rasterizer->horizontal_edges = 0;
+  {
+    rasterizer->needs_aa3  = 0;
+    rasterizer->needs_aa5  = 0;
+    rasterizer->needs_aa15 = 0;
+    ctx_rasterizer_sort_edges (rasterizer);
+    rasterizer->scanline = scan_start;
+    ctx_rasterizer_feed_edges (rasterizer, 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];
+      int avoid_direct = (0 
+#if CTX_ENABLE_CLIP
+         || rasterizer->clip_buffer
+#endif
+#if CTX_ENABLE_SHADOW_BLUR
+         || rasterizer->in_shadow
+#endif
+#if CTX_SHAPE_CACHE
+         || shape != NULL
+#endif
+         );
 
-  // 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]);
+  for (; rasterizer->scanline <= scan_end;)
+    {
 
-  ctx_list_free (&list);
+      int needs_full_aa =
+          ( (rasterizer->horizontal_edges!=0) 
+          | (rasterizer->active_edges != rasterizer->prev_active_edges)
+          | (rasterizer->active_edges + rasterizer->pending_edges == rasterizer->ending_edges)
+          );
 
-  grab->timeout_id = 0;
+    if ((needs_full_aa))
+    {
+        int increment = CTX_FULL_AA/real_aa;
+        memset (coverage, 0, coverage_size);
+        for (int i = 0; i < real_aa; i++)
+        {
+          ctx_rasterizer_feed_edges (rasterizer, 0);
+          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, real_aa);
+          ctx_rasterizer_increment_edges (rasterizer, increment);
+        }
+    }
+    else if (! avoid_direct & (rasterizer->needs_aa3 == 0))
+    {
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
+      ctx_rasterizer_feed_edges (rasterizer, 0);
 
-  return 0;
+      ctx_rasterizer_generate_coverage_apply (rasterizer, minx, maxx, coverage, is_winding,
+                      comp);
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
 
-  return ret;
-}
+      dst += blit_stride;
+      rasterizer->prev_active_edges = rasterizer->active_edges;
+      continue;
+    }
+    else if (avoid_direct & (rasterizer->needs_aa3 == 0))
+    {
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
+      ctx_rasterizer_feed_edges (rasterizer, 0);
 
-int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
-                      char *string)
-{
-  CtxList *l;
-  CtxList *hitlist = NULL;
+      memset (coverage, 0, coverage_size);
+      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, is_winding);
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
+    }
+    else if (ctx_rasterizer_is_simple (rasterizer))
+    {
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
+      ctx_rasterizer_feed_edges (rasterizer, 1);
+      memset (coverage, 0, coverage_size);
+      if (!avoid_direct)
+      {
 
-  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;
-  }
+        ctx_rasterizer_generate_coverage_apply2 (rasterizer, minx, maxx, coverage, is_winding,
+                      comp);
+        ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
 
-  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];
+        dst += blit_stride;
+        rasterizer->prev_active_edges = rasterizer->active_edges;
+        continue;
+      }
+      ctx_rasterizer_generate_coverage_set2 (rasterizer, minx, maxx, coverage, is_winding);
+      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
+    }
+    else 
+    {
+      int aa = 3;
+      if (rasterizer->needs_aa5 && real_aa >=5)
+      {
+         aa = 5;
+         if (rasterizer->needs_aa15 && real_aa >=15)
+           aa = 15;
+      }
+      int scanline_increment = 15/aa;
 
-  if (time == 0)
-    time = ctx_ms (ctx);
+      memset (coverage, 0, coverage_size);
+      for (int i = 0; i < CTX_FULL_AA; i+= scanline_increment)
+      {
+        ctx_rasterizer_feed_edges (rasterizer, 0);
+        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, aa);
+        ctx_rasterizer_increment_edges (rasterizer, scanline_increment);
+      }
+    }
 
-  event->ctx = ctx;
-  event->x = x;
-  event->y = y;
+  ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx,
+                  NULL, NULL);
+#if CTX_SHAPE_CACHE
+  if (shape == NULL)
+#endif
+  {
+    ctx_rasterizer_apply_coverage (rasterizer,
+                         &dst[(minx * rasterizer->format->bpp) /8],
+                         minx,
+                         coverage,
+                         maxx-minx+ 1);
+  }
+#if CTX_SHAPE_CACHE
+      if (shape)
+        {
+          coverage += shape->width;
+        }
+#endif
+      dst += blit_stride;
+      rasterizer->prev_active_edges = rasterizer->active_edges;
+    }
+  }
 
-  event->delta_x = event->delta_y = 0;
+  if (CTX_UNLIKELY(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_DESTINATION_ATOP ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_CLEAR))
+  {
+     /* 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 * CTX_FULL_AA;
+     int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
+     int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_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);
 
-  event->device_no = device_no;
-  event->string    = string;
-  event->time      = time;
-  event->stop_propagate = 0;
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_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 += CTX_FULL_AA;
+       dst += rasterizer->blit_stride;
+     }
+     if (minx < startx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_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 += blit_stride;
+     }
+     }
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_DROP, &hitlist);
+     if (endx > maxx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_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);
 
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
-    _ctx_emit_cb_item (ctx, item, event, CTX_DROP, x, y);
+       rasterizer->scanline += CTX_FULL_AA;
+       dst += rasterizer->blit_stride;
+     }
+     }
+#if 1
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA);
+     // XXX valgrind/asan this
+     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, clipw-1);
 
-    if (event->stop_propagate)
-    {
-      ctx_list_free (&hitlist);
-      return 0;
-    }
+       rasterizer->scanline += CTX_FULL_AA;
+       dst += blit_stride;
+     }
+#endif
   }
-
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-
-  return 0;
+  ctx_rasterizer_reset (rasterizer);
 }
 
-int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+inline static int
+ctx_is_transparent (CtxRasterizer *rasterizer, int stroke)
 {
-  CtxEvents *events = &ctx->events;
-  CtxList *hitlist = NULL;
-  events->pointer_x[device_no] = x;
-  events->pointer_y[device_no] = y;
-  if (device_no <= 3)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  if (gstate->global_alpha_u8 == 0)
+    return 1;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
   {
-    events->pointer_x[0] = x;
-    events->pointer_y[0] = y;
+    uint8_t ga[2];
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
+    if (ga[1] == 0)
+      return 1;
   }
+  return 0;
+}
 
-  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);
+#define CTX_RECT_FILL 1
 
-  event->x = event->start_x = event->prev_x = x;
-  event->y = event->start_y = event->prev_y = y;
+#if CTX_RECT_FILL
+static void
+ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
+                          int          x0,
+                          int          y0,
+                          int          x1,
+                          int          y1,
+                          uint8_t      cov)
+{
+  int blit_x = rasterizer->blit_x;
+  int blit_y = rasterizer->blit_y;
+  int blit_width = rasterizer->blit_width;
+  int blit_height = rasterizer->blit_height;
+  int blit_stride = rasterizer->blit_stride;
+  x0 = ctx_maxi (x0, blit_x);
+  x1 = ctx_mini (x1, blit_x + blit_width);
 
-  event->delta_x = event->delta_y = 0;
+  int width = x1 - x0;
 
-  event->device_no = device_no;
-  event->time      = time;
-  event->stop_propagate = 0;
+  if (CTX_UNLIKELY(width <=0))
+    return;
 
-  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;
-  }
+  void (*comp_op)(CTX_COMPOSITE_ARGUMENTS) = rasterizer->comp_op;
 
-  CtxGrab *grab = NULL;
-  CtxList *l;
+  y0 = ctx_maxi (y0, blit_y);
+  y1 = ctx_mini (y1, blit_y + blit_height);
+  rasterizer->scanline = y0 * CTX_FULL_AA;
+  _ctx_setup_compositor (rasterizer);
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-  _ctx_update_item (ctx, device_no, x, y, 
-      CTX_PRESS | CTX_DRAG_PRESS | CTX_TAP | CTX_TAP_AND_HOLD, &hitlist);
+  dst += (y0 - blit_y) * blit_stride;
+  dst += (x0) * rasterizer->format->bpp/8;
 
-  for (l = hitlist; l; l = l?l->next:NULL)
+  if (cov == 255)
   {
-    CtxItem *item = l->data;
-    if (item &&
-        ((item->types & CTX_DRAG)||
-         (item->types & CTX_TAP) ||
-         (item->types & CTX_TAP_AND_HOLD)))
+    if (comp_op == ctx_RGBA8_source_copy_normal_color)
     {
-      grab = device_add_grab (ctx, device_no, item, item->types);
-      grab->start_time = time;
+      uint32_t color = *((uint32_t*)rasterizer->color);
+      if ((width == 1))
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          *dst_i = color;
+          dst += blit_stride;
+        }
+        return;
+      }
+      else
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          ctx_span_set_color ((uint32_t*)&dst[0], color, width);
+          dst += blit_stride;
+        }
+        return;
+      }
+    }
+    else if (comp_op == ctx_RGBA8_source_over_normal_color)
+    {
+      uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
+      uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
+      uint32_t si_a  = rasterizer->color[3];
 
-      if (item->types & CTX_TAP_AND_HOLD)
+      if ((width == 1))
       {
-         grab->timeout_id = ctx_add_timeout (ctx, events->tap_delay_hold, tap_and_hold_fire, grab);
+        for (int y = y0; y < y1; y++)
+        {
+          ((uint32_t*)(dst))[0] = ctx_over_RGBA8_full_2 (
+             ((uint32_t*)(dst))[0], si_ga_full, si_rb_full, si_a);
+          dst += blit_stride;
+        }
+        return;
+      }
+      else
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          for (int i = 0; i < width; i++)
+          {
+            dst_i[i] = ctx_over_RGBA8_full_2 (dst_i[i], si_ga_full, si_rb_full, si_a);
+          }
+          dst += blit_stride;
+        }
+        return;
       }
     }
-    _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);
+    else if (comp_op == ctx_RGBA8_source_copy_normal_fragment)
+    {
+      for (int y = y0; y < y1; y++)
+      {
+        float u0 = 0; float v0 = 0;
+        float ud = 0; float vd = 0;
+        ctx_init_uv (rasterizer, x0, width, &u0, &v0, &ud, &vd);
+        rasterizer->fragment (rasterizer, u0, v0, &dst[0], width, ud, vd);
+        rasterizer->scanline += CTX_FULL_AA;
+        dst += blit_stride;
+      }
+      return;
+    }
+    else if (comp_op == ctx_RGBA8_source_over_normal_fragment)
+    {
+      for (int y = y0; y < y1; y++)
+      {
+        ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
+                                &dst[0], NULL, x0, NULL, width);
+        rasterizer->scanline += CTX_FULL_AA;
+        dst += blit_stride;
+      }
+      return;
+    }
+  }
+  else
+  {
+  if (comp_op == ctx_RGBA8_source_copy_normal_color)
+    {
+      uint32_t color = *((uint32_t*)rasterizer->color);
+      if ((width == 1))
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          *dst_i = ctx_lerp_RGBA8 (*dst_i, color, cov);
+          dst += blit_stride;
+        }
+        return;
+      }
+      else
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          for (int i = 0; i < width; i++)
+          {
+            dst_i[i] = ctx_lerp_RGBA8 (dst_i[i], color, cov);
+          }
+          dst += blit_stride;
+        }
+        return;
+      }
+    }
+    else if (comp_op == ctx_RGBA8_source_over_normal_color)
+    {
+      uint32_t color = *((uint32_t*)rasterizer->color);
+      if ((width == 1))
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          *dst_i = ctx_over_RGBA8 (*dst_i, color, cov);
+          dst += blit_stride;
+        }
+        return;
+      }
+      else
+      {
+        for (int y = y0; y < y1; y++)
+        {
+          uint32_t *dst_i = (uint32_t*)&dst[0];
+          for (int i = 0; i < width; i++)
+          {
+            dst_i[i] = ctx_over_RGBA8 (dst_i[i], color, cov);
+          }
+          dst += blit_stride;
+        }
+        return;
+      }
+    }
+  }
 
-    if (event->stop_propagate)
+  {
+    uint8_t coverage[width];
+    memset (coverage, cov, sizeof (coverage) );
+    for (int y = y0; y < y1; y++)
     {
-      ctx_list_free (&hitlist);
-      return 0;
+      ctx_rasterizer_apply_coverage (rasterizer, &dst[0], x0, coverage, width);
+      rasterizer->scanline += CTX_FULL_AA;
+      dst += blit_stride;
     }
   }
+}
+#endif
 
-  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-  return 0;
+static inline float ctx_fmod1f (float val)
+{
+  int vali = val;
+  return val - vali;
 }
 
-void _ctx_resized (Ctx *ctx, int width, int height, long time)
+static void
+ctx_rasterizer_fill (CtxRasterizer *rasterizer)
 {
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
-  CtxEvent event = {0, };
+  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
+  int blit_x = rasterizer->blit_x;
+  int blit_y = rasterizer->blit_y;
+  int blit_width = rasterizer->blit_width;
+  int blit_height = rasterizer->blit_height;
+  int blit_stride = rasterizer->blit_stride;
 
-  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?
-   */
+  CtxSegment temp[count]; /* copy of already built up path's poly line
+                          XXX - by building a large enough path
+                          the stack can be smashed!
+                         */
+  if (CTX_UNLIKELY(rasterizer->preserve))
+    { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); }
 
-  if (item)
+#if CTX_ENABLE_SHADOW_BLUR
+  if (CTX_UNLIKELY(rasterizer->in_shadow))
   {
-    event.stop_propagate = 0;
-    _ctx_emit_cb_item (ctx, item, &event, CTX_KEY_PRESS, 0, 0);
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
+    {
+      CtxSegment *entry = &((CtxSegment*)rasterizer->edge_list.entries)[i];
+      entry->data.s16[2] += rasterizer->shadow_x * CTX_SUBDIV;
+      entry->data.s16[3] += rasterizer->shadow_y * CTX_FULL_AA;
+    }
+    rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_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 (CTX_UNLIKELY(ctx_is_transparent (rasterizer, 0) ||
+      rasterizer->scan_min > CTX_FULL_AA * (blit_y + blit_height) ||
+      rasterizer->scan_max < CTX_FULL_AA * blit_y ||
+      rasterizer->col_min > CTX_SUBDIV * (blit_x + blit_width) ||
+      rasterizer->col_max < CTX_SUBDIV * blit_x))
+    {
+      ctx_rasterizer_reset (rasterizer);
+    }
+  else
+  {
+    _ctx_setup_compositor (rasterizer);
 
-int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
-{
-  CtxEvents *events = &ctx->events;
-  if (time == 0)
-    time = ctx_ms (ctx);
+#if 1
+    rasterizer->state->min_x = ctx_mini (rasterizer->state->min_x, rasterizer->col_min / CTX_SUBDIV);
+    rasterizer->state->max_x = ctx_maxi (rasterizer->state->min_x, rasterizer->col_max / CTX_SUBDIV);
+    rasterizer->state->min_y = ctx_mini (rasterizer->state->min_y, rasterizer->scan_min / CTX_FULL_AA);
+    rasterizer->state->max_y = ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / CTX_FULL_AA);
+#else
+    if (CTX_UNLIKELY ( rasterizer->col_min / CTX_SUBDIV < rasterizer->state->min_x))
+       rasterizer->state->min_x = rasterizer->col_min / CTX_SUBDIV;
+    if (CTX_UNLIKELY ( rasterizer->col_min / CTX_SUBDIV > rasterizer->state->max_x))
+       rasterizer->state->min_x = rasterizer->col_min / CTX_SUBDIV;
+
+    if (CTX_UNLIKELY ( rasterizer->scan_min / CTX_FULL_AA < rasterizer->state->min_y))
+       rasterizer->state->min_y = rasterizer->scan_min / CTX_FULL_AA;
+    if (CTX_UNLIKELY ( rasterizer->scan_min / CTX_FULL_AA > rasterizer->state->max_y))
+       rasterizer->state->max_y = rasterizer->scan_max / CTX_FULL_AA;
+#endif
+
+#if CTX_RECT_FILL
+  if (rasterizer->edge_list.count == 5)
+    {
+      CtxSegment *entry0 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0];
+      CtxSegment *entry1 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[1];
+      CtxSegment *entry2 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[2];
+      CtxSegment *entry3 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[3];
+
+      if ((!rasterizer->state->gstate.clipped != 0) &
+          (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])
+#if CTX_ENABLE_SHADOW_BLUR
+           && !rasterizer->in_shadow
+#endif
+         )
+       {
+         if(((entry1->data.s16[2] % (CTX_SUBDIV))  == 0) &
+            ((entry1->data.s16[3] % (CTX_FULL_AA)) == 0) &
+            ((entry3->data.s16[2] % (CTX_SUBDIV))  == 0) &
+            ((entry3->data.s16[3] % (CTX_FULL_AA)) == 0))
+         {
+           /* best-case axis aligned rectangle */
+           int x0 = entry3->data.s16[2] / CTX_SUBDIV;
+           int y0 = entry3->data.s16[3] / CTX_FULL_AA;
+           int x1 = entry1->data.s16[2] / CTX_SUBDIV;
+           int y1 = entry1->data.s16[3] / CTX_FULL_AA;
+
+           ctx_rasterizer_fill_rect (rasterizer, x0, y0, x1, y1, 255);
+           ctx_rasterizer_reset (rasterizer);
+           goto done;
+         }
+        else
+         {
+           float x0 = entry3->data.s16[2] * (1.0f / CTX_SUBDIV);
+           float y0 = entry3->data.s16[3] * (1.0f / CTX_FULL_AA);
+           float x1 = entry1->data.s16[2] * (1.0f / CTX_SUBDIV);
+           float y1 = entry1->data.s16[3] * (1.0f / CTX_FULL_AA);
+
+           x0 = ctx_maxf (x0, blit_x);
+           y0 = ctx_maxf (y0, blit_y);
+           x1 = ctx_minf (x1, blit_x + blit_width);
+           y1 = ctx_minf (y1, blit_y + blit_height);
+
+           uint8_t left = 255-ctx_fmod1f (x0) * 255;
+           uint8_t top  = 255-ctx_fmod1f (y0) * 255;
+           uint8_t right  = ctx_fmod1f (x1) * 255;
+           uint8_t bottom = ctx_fmod1f (y1) * 255;
+
+           x0 = ctx_floorf (x0);
+           y0 = ctx_floorf (y0);
+           x1 = ctx_floorf (x1+7/8.0f);
+           y1 = ctx_floorf (y1+14/15.0f);
+
+           int has_top    = (top < 255);
+           int has_bottom = (bottom <255);
+           int has_right  = (right >0);
+           int has_left   = (left >0);
+
+           int width = x1 - x0;
+
+           if (CTX_LIKELY(width >0))
+           {
+              uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+              uint8_t coverage[width+2];
+              dst += (((int)y0) - blit_y) * blit_stride;
+              dst += ((int)x0) * rasterizer->format->bpp/8;
 
-  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 (has_top)
+              {
+                int i = 0;
+                if (has_left)
+                {
+                  coverage[i++] = top * left / 255;
+                }
+                for (int x = x0 + has_left; x < x1 - has_right; x++)
+                  coverage[i++] = top;
+                coverage[i++]= top * right / 255;
 
-  event->time = time;
-  event->x = x;
-  event->ctx = ctx;
-  event->y = y;
-  event->device_no = device_no;
-  event->stop_propagate = 0;
+                  ctx_rasterizer_apply_coverage (rasterizer,dst,
+                                                 x0,
+                                                 coverage, width);
+                 dst += blit_stride;
+               }
 
-  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;
-  }
+#if 0
+           if (!(
+            (rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_COPY||
+             rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OVER) &&
+             rasterizer->state->gstate.blend_mode == CTX_BLEND_NORMAL &&
+             rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR
+             ))
+           {
+             int i = 0;
+             if (has_left)
+             {
+               coverage[i++] = left;
+             }
+             for (int x = x0 + has_left; x < x1 - has_right; x++)
+               coverage[i++] = 255;
+             coverage[i++] = right;
 
-  //events_queue_draw (mrg, NULL); /* in case of style change */
+             for (int ty = y0+has_top; ty < y1-has_bottom; ty++)
+             {
+               ctx_rasterizer_apply_coverage (rasterizer, dst, x0, coverage, width);
+               dst += blit_stride;
+             }
+           }
+           else
+#endif
+           {
+             if (has_left)
+               ctx_rasterizer_fill_rect (rasterizer, x0, y0 + has_top, x0+1, y1 - has_bottom, left);
+             if (has_right)
+               ctx_rasterizer_fill_rect (rasterizer, x1-1, y0 + has_top, x1, y1 - has_bottom, right);
+             x0 += has_left;
+             y0 += has_top;
+             y1 -= has_bottom;
+             x1 -= has_right;
+             ctx_rasterizer_fill_rect (rasterizer, x0,y0,x1,y1,255);
+
+             dst += blit_stride * ((((int)y1)-has_bottom) - (((int)y0)+has_top));
+           }
 
-  if (events->pointer_down[device_no] == 0)
-  {
-    fprintf (stderr, "device %i already up\n", device_no);
-  }
-  events->pointer_down[device_no] = 0;
+           if (has_bottom)
+           {
+             int i = 0;
+             if (has_left)
+               coverage[i++] = bottom * left / 255;
+             for (int x = x0 + has_left; x < x1 - has_right; x++)
+               coverage[i++] = bottom;
+             coverage[i++]= bottom * right / 255;
+
+             ctx_rasterizer_apply_coverage (rasterizer,dst, x0, coverage, width);
+           }
+           }
 
-  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_rasterizer_reset (rasterizer);
+           goto done;
+         }
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_RELEASE | CTX_DRAG_RELEASE, &hitlist);
-  grablist = _ctx_device_get_grabs (ctx, device_no);
+       }
+    }
+#endif
+    ctx_rasterizer_finish_shape (rasterizer);
 
-  for (g = grablist; g; g = g->next)
-  {
-    grab = g->data;
+    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
+    if (hash){};
 
-    if (!event->stop_propagate)
-    {
-      if (grab->item->types & CTX_TAP)
+#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 + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / 
CTX_FULL_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
+        )
       {
-        long delay = time - grab->start_time;
+        int scan_min = rasterizer->scan_min;
+        int col_min = rasterizer->col_min;
+        scan_min -= (scan_min % CTX_FULL_AA);
+        int y0 = scan_min / CTX_FULL_AA;
+        int y1 = y0 + height;
+        int x0 = col_min / CTX_SUBDIV;
+        int ymin = y0;
+        int x1 = x0 + width;
+        int clip_x_min = blit_x;
+        int clip_x_max = blit_x + blit_width - 1;
+        int clip_y_min = blit_y;
+        int clip_y_max = blit_y + blit_height - 1;
 
-        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)
-            )
+        int dont_cache = 0;
+        if (CTX_UNLIKELY(x1 >= clip_x_max))
+          { x1 = clip_x_max;
+            dont_cache = 1;
+          }
+        int xo = 0;
+        if (CTX_UNLIKELY(x0 < clip_x_min))
+          {
+            xo = clip_x_min - x0;
+            x0 = clip_x_min;
+            dont_cache = 1;
+          }
+        if (CTX_UNLIKELY(y0 < clip_y_min || y1 >= clip_y_max))
+          dont_cache = 1;
+        if (dont_cache || !_ctx_shape_cache_enabled)
         {
-          _ctx_emit_cb_item (ctx, grab->item, event, CTX_TAP, x, y);
+          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 (!event->stop_propagate && grab->item->types & CTX_DRAG_RELEASE)
-      {
-        _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_RELEASE, x, y);
+        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;
+          }
+
+        int ewidth = x1 - x0;
+        if (ewidth>0)
+        {
+          rasterizer->scanline = scan_min;
+          int bpp = rasterizer->format->bpp;
+          if (rasterizer->clip_buffer && !rasterizer->clip_rectangle)
+          {
+          uint8_t composite[ewidth];
+          uint8_t *clip_data = (uint8_t*)rasterizer->clip_buffer->data;
+          int shape_width = shape->width;
+          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*(clip_data) [
+                              ((y-blit_y) * blit_width) + x0 + x])/255;
+                      composite[x] = val;
+                    }
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + 
(int) (x0) * bpp/8,
+                                                 x0, // is 0
+                                                 composite,
+                                                 ewidth );
+               rasterizer->scanline += CTX_FULL_AA;
+            }
+          }
+          }
+          else
+          for (int y = y0; y < y1; y++)
+            {
+              if (CTX_LIKELY((y >= clip_y_min) && (y <= clip_y_max) ))
+                {
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + 
(int) (x0) * bpp/8,
+                                                 x0,
+                                                 &shape->data[shape->width * (int) (y-ymin) + xo],
+                                                 ewidth );
+                }
+               rasterizer->scanline += CTX_FULL_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
+                                   );
     }
-
-    device_remove_grab (ctx, grab);
   }
-
-  if (hitlist)
+done:
+  if (CTX_UNLIKELY(rasterizer->preserve))
+    {
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
+    }
+#if CTX_ENABLE_SHADOW_BLUR
+  if (CTX_UNLIKELY(rasterizer->in_shadow))
   {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_RELEASE, x, y);
-    ctx_list_free (&hitlist);
+    rasterizer->scan_min -= rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->scan_max -= rasterizer->shadow_y * CTX_FULL_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;
   }
-  ctx_list_free (&grablist);
-  return 0;
+#endif
+  rasterizer->preserve = 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)
+#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)
 {
-  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);
+}
+#endif
 
-  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;
-  }
+typedef struct _CtxTermGlyph CtxTermGlyph;
 
-  grablist = _ctx_device_get_grabs (ctx, device_no);
-  _ctx_update_item (ctx, device_no, x, y, CTX_MOTION, &hitlist);
+struct _CtxTermGlyph
+{
+  uint32_t unichar;
+  int      col;
+  int      row;
+  uint8_t  rgba_bg[4];
+  uint8_t  rgba_fg[4];
+};
 
-  {
-    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;
-  }
+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);
 
-  event->delta_x = x - event->prev_x;
-  event->delta_y = y - event->prev_y;
-  event->prev_x  = x;
-  event->prev_y  = y;
+  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;
 
-  CtxList *remove_grabs = NULL;
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  int ch = 1;
+  int cw = 1;
 
-  for (g = grablist; g; g = g->next)
+  if (rasterizer->term_glyphs)
   {
-    grab = g->data;
+    float tx = 0;
+    float ty = rasterizer->state->gstate.font_size;
+    float txb = 0;
+    float tyb = 0;
 
-    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, ":");
-      }
-    }
+    ch = ctx_term_get_cell_height (rasterizer->ctx);
+    cw = ctx_term_get_cell_width (rasterizer->ctx);
 
-    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);
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    _ctx_user_to_device (rasterizer->state, &txb, &tyb);
+    font_size = ty-tyb;
   }
-  if (hitlist)
+  if (rasterizer->term_glyphs && !stroke &&
+      fabs (font_size - ch) < 0.5)
   {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_MOTION, x, y);
-    ctx_list_free (&hitlist);
+    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]);
   }
-  ctx_list_free (&grablist);
-  return 0;
+  else
+#endif
+  _ctx_glyph (rasterizer->ctx, unichar, stroke);
 }
 
-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)
+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)
 {
-  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)
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  if (rasterizer->term_glyphs)
   {
-    CtxItem *item = l->data;
-
-    _ctx_emit_cb_item (ctx, item, event, CTX_SCROLL, x, y);
-
-    if (event->stop_propagate)
-      l = NULL;
+  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);
 
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-  return 0;
+  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++)
+    {
+      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
+#endif
+  {
+    _ctx_text (rasterizer->ctx, string, stroke, 1);
+  }
 }
 
-static int ctx_str_has_prefix (const char *string, const char *prefix)
+void
+_ctx_font (Ctx *ctx, const char *name);
+static void
+ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
 {
-  for (int i = 0; prefix[i]; i++)
-  {
-    if (!string[i]) return 0;
-    if (string[i] != prefix[i]) return 0;
-  }
-  return 0;
+  _ctx_font (rasterizer->ctx, font_name);
 }
 
-int ctx_key_press (Ctx *ctx, unsigned int keyval,
-                   const char *string, uint32_t time)
+static void
+ctx_rasterizer_arc (CtxRasterizer *rasterizer,
+                    float          x,
+                    float          y,
+                    float          radius,
+                    float          start_angle,
+                    float          end_angle,
+                    int            anticlockwise)
 {
-  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);
+  int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS;
+  full_segments = radius * CTX_PI * 2 / 4.0;
+  if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS)
+    { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; }
+  if (full_segments < 24) full_segments = 24;
+  float step = CTX_PI*2.0/full_segments;
+  int steps;
 
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
-  CtxEvent event = {0,};
+  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;
 
-  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;
+  if (radius <= 0.0001)
+          return;
 
-    for (i = 0; i < item->cb_count; i++)
+  if (end_angle == start_angle)
+          // XXX also detect arcs fully outside render view
     {
-      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;
-        }
-      }
+    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;
     }
-    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 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 )  )
     {
-      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;
-        }
-      }
+      start_angle = start_angle;
+      steps = full_segments - 1;
     }
-    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++)
+  else
+#endif
     {
-      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)
+      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++)
         {
-          free ((void*)event.string);
-          return event.stop_propagate;
+          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;
         }
-      }
     }
-    free ((void*)event.string);
-  }
-  return 0;
-
-  return 0;
+  ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                          y + ctx_sinf (end_angle) * radius);
 }
 
-void ctx_freeze           (Ctx *ctx)
+static void
+ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
+                        float        cx,
+                        float        cy,
+                        float        x,
+                        float        y)
 {
-  ctx->events.frozen ++;
+  /* 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_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)
+static void
+ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
+                            float cx, float cy,
+                            float x,  float y)
 {
-  return ctx->events.height;
+  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
+                          x  + rasterizer->x, y  + rasterizer->y);
 }
 
-float ctx_pointer_x (Ctx *ctx)
+#define LENGTH_OVERSAMPLE 1
+static void
+ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
 {
-  return ctx->events.pointer_x[0];
+  // 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_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
+    {
+      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);
+        }
+    }
+  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
 }
 
-float ctx_pointer_y (Ctx *ctx)
+static void
+ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
 {
-  return ctx->events.pointer_y[0];
-}
+  int count = rasterizer->edge_list.count;
+  CtxSegment *temp = (CtxSegment*)rasterizer->edge_list.entries;
+  float prev_x = 0.0f;
+  float prev_y = 0.0f;
+  int aa = 15;//rasterizer->aa;
+  int start = 0;
+  int end = 0;
+#if 0
+  float factor = ctx_matrix_get_scale (&state->gstate.transform);
+#endif
 
-int ctx_pointer_is_down (Ctx *ctx, int no)
-{
-  if (no < 0 || no > CTX_MAX_DEVICES) return 0;
-  return ctx->events.pointer_down[no];
+  while (start < count)
+    {
+      int started = 0;
+      int i;
+      for (i = start; i < count; i++)
+        {
+          CtxSegment *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_rasterizer_reset (rasterizer);
 }
 
-void _ctx_debug_overlays (Ctx *ctx)
+static void
+ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
 {
-  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)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxSource source_backup;
+  if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL)
   {
-    float current_x = ctx_pointer_x (ctx);
-    float current_y = ctx_pointer_y (ctx);
-    CtxItem *item = a->data;
-    CtxMatrix matrix = item->inv_matrix;
+    source_backup = gstate->source_fill;
+    gstate->source_fill = rasterizer->state->gstate.source_stroke;
+  }
+  int count = rasterizer->edge_list.count;
+  int preserved = rasterizer->preserve;
+  float factor = ctx_matrix_get_scale (&gstate->transform);
+  float line_width = gstate->line_width * factor;
 
-    ctx_matrix_apply_transform (&matrix, &current_x, &current_y);
+  CtxSegment temp[count]; /* copy of already built up path's poly line  */
+  memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) );
 
-    if (current_x >= item->x0 && current_x < item->x1 &&
-        current_y >= item->y0 && current_y < item->y1)
+#if CTX_RECT_FILL
+  if (rasterizer->edge_list.count == 5)
     {
-      ctx_matrix_invert (&matrix);
-      ctx_set_matrix (ctx, &matrix);
-      _mrg_restore_path (ctx, item->path);
-      ctx_stroke (ctx);
-    }
-  }
-  ctx_restore (ctx);
-}
+      CtxSegment *entry0 = &((CtxSegment*)rasterizer->edge_list.entries)[0];
+      CtxSegment *entry1 = &((CtxSegment*)rasterizer->edge_list.entries)[1];
+      CtxSegment *entry2 = &((CtxSegment*)rasterizer->edge_list.entries)[2];
+      CtxSegment *entry3 = &((CtxSegment*)rasterizer->edge_list.entries)[3];
+      //fprintf (stderr, "{%i %.2f %.2f}", lw, lwmod, line_width);
 
-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;
-}
+      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])
+#if CTX_ENABLE_SHADOW_BLUR
+           && !rasterizer->in_shadow
+#endif
+         )
+       {
+      float lwmod = ctx_fmod1f (line_width);
+      int lw = ctx_floorf (line_width + 0.5f);
+      int is_compat_even = (lw % 2 == 0) && (lwmod < 0.1); // only even linewidths implemented properly
+      int is_compat_odd = (lw % 2 == 1) && (lwmod < 0.1); // only even linewidths implemented properly
 
-int ctx_is_dirty (Ctx *ctx)
-{
-  return ctx->dirty;
-}
-void ctx_set_dirty (Ctx *ctx, int dirty)
-{
-  ctx->dirty = dirty;
-}
+      int off_x = 0;
+      int off_y = 0;
 
-/*
- * centralized global API for managing file descriptors that
- * wake us up, this to remove sleeping and polling
- */
 
-#define CTX_MAX_LISTEN_FDS 128 // becomes max clients..
+      if (is_compat_odd)
+      {
+        off_x = CTX_SUBDIV/2;
+        off_y = CTX_FULL_AA/2;
+      }
 
-static int _ctx_listen_fd[CTX_MAX_LISTEN_FDS];
-static int _ctx_listen_fds    = 0;
-static int _ctx_listen_max_fd = 0;
+      if((is_compat_odd || is_compat_even) &&
+         (((entry1->data.s16[2]-off_x) % (CTX_SUBDIV))  == 0)  &&
+         (((entry1->data.s16[3]-off_y) % (CTX_FULL_AA)) == 0) &&
+         (((entry3->data.s16[2]-off_x) % (CTX_SUBDIV))  == 0)  &&
+         (((entry3->data.s16[3]-off_y) % (CTX_FULL_AA)) == 0))
+      {
+        float x0 = entry3->data.s16[2] * 1.0f / CTX_SUBDIV;
+        float y0 = entry3->data.s16[3] * 1.0f / CTX_FULL_AA;
+        float x1 = entry1->data.s16[2] * 1.0f / CTX_SUBDIV;
+        float y1 = entry1->data.s16[3] * 1.0f / CTX_FULL_AA;
 
-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;
-}
+        int bw = lw/2+1;
+        int bwb = lw/2;
 
-void _ctx_remove_listen_fd (int fd)
-{
-  for (int i = 0; i < _ctx_listen_fds; i++)
-  {
-    if (_ctx_listen_fd[i] == fd)
+        if (is_compat_even)
+        {
+          bw = lw/2;
+        }
+        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y0-bwb, x1+bw, y0+bw, 255);
+        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y1-bwb, x1-bwb, y1+bw, 255);
+        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y0, x0+bw, y1, 255);
+        ctx_rasterizer_fill_rect (rasterizer, x1-bwb, y0, x1+bw, y1+bw, 255);
+        ctx_rasterizer_reset (rasterizer);
+        goto done;
+      }
+       }
+    }
+#endif
+  
     {
-      _ctx_listen_fd[i] = _ctx_listen_fd[_ctx_listen_fds-1];
-      _ctx_listen_fds--;
-      return;
+
+  int aa = CTX_FULL_AA;
+#if 0
+  if (CTX_UNLIKELY(gstate->line_width * factor <= 0.0f &&
+      gstate->line_width * factor > -10.0f))
+    {
+      ctx_rasterizer_stroke_1px (rasterizer);
     }
-  }
-}
+  else
+#endif
+    {
+      if (line_width < 5.0f)
+      {
+      factor *= 0.89; /* this hack adjustment makes sharp 1px and 2px strokewidths
+      //                 end up sharp without erronious AA; we seem to be off by
+      //                 one somewhere else, causing the need for this
+      //                 */
+      line_width *= 0.89f;
+      }
+      ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape  */
+      CtxMatrix transform_backup = gstate->transform;
+      _ctx_matrix_identity (&gstate->transform);
+      float prev_x = 0.0f;
+      float prev_y = 0.0f;
+      float half_width_x = line_width/2;
+      float half_width_y = line_width/2;
+      if (CTX_UNLIKELY(line_width <= 0.0f))
+        { // makes 0 width be hairline
+          half_width_x = .5f;
+          half_width_y = .5f;
+        }
+      int start = 0;
+      int end   = 0;
+      while (start < count)
+        {
+          int started = 0;
+          int i;
+          for (i = start; i < count; i++)
+            {
+              CtxSegment *entry = &temp[i];
+              float x, y;
+              if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
+                {
+                  if (CTX_LIKELY(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 (CTX_LIKELY(length>0.001f))
+                {
+                  float recip_length = 1.0/length;
+                  dx = dx * recip_length * half_width_x;
+                  dy = dy * recip_length * half_width_y;
+                  if (CTX_UNLIKELY(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
 
-int ctx_input_pending (Ctx *ctx, 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);
-  }
-  int input_fds[5];
-  int n_fds;
-  ctx_get_event_fds (ctx, input_fds, &n_fds);
-  for (int i = 0; i < n_fds; i++)
-  {
-    FD_SET (input_fds[i], &fdset);
-  }
+                  // 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);
 
-  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 + 1, &fdset, NULL, NULL, &tv);
-  if (retval == -1)
-  {
-    perror ("select");
-    return 0;
+
+                  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--)
+            {
+              CtxSegment *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);
+              float recip_length = 1.0f/length;
+              dx = dx * recip_length * half_width_x;
+              dy = dy * recip_length * half_width_y;
+              if (CTX_LIKELY(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 (CTX_UNLIKELY(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);
+                  recip_length = 1.0f/length;
+                  if (CTX_LIKELY(length>0.001f))
+                    {
+                      dx = dx * recip_length * half_width_x;
+                      dy = dy * recip_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 (gstate->line_cap)
+        {
+          case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in
+                               //                  reverse order - rotation would be off
+                               //                  better implement correct here
+            {
+              float x = 0, y = 0;
+              int has_prev = 0;
+              for (int i = 0; i < count; i++)
+                {
+                  CtxSegment *entry = &temp[i];
+                  if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
+                    {
+                      if (has_prev)
+                        {
+                          ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, 
half_width_x, half_width_y);
+                          ctx_rasterizer_finish_shape (rasterizer);
+                        }
+                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                      y = entry->data.s16[1] * 1.0f / aa;
+                      ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x 
* 2, half_width_y * 2);
+                      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_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, 
half_width_y * 2);
+              ctx_rasterizer_finish_shape (rasterizer);
+            }
+            break;
+          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++)
+                {
+                  CtxSegment *entry = &temp[i];
+                  if (CTX_UNLIKELY(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 (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++)
+                {
+                  CtxSegment *entry = &temp[i];
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  if (CTX_UNLIKELY(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 = gstate->fill_rule;
+      gstate->fill_rule = CTX_FILL_RULE_WINDING;
+      rasterizer->preserve = 0; // so fill isn't tripped
+      ctx_rasterizer_fill (rasterizer);
+      gstate->fill_rule = rule_backup;
+      gstate->transform = transform_backup;
+    }
   }
-  return retval;
+done:
+  if (preserved)
+    {
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
+      rasterizer->preserve = 0;
+    }
+  if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL)
+    gstate->source_fill = source_backup;
 }
 
-void ctx_sdl_set_title (void *self, const char *new_title);
-void ctx_set_title (Ctx *ctx, const char *title)
+#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)
 {
-#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);
+#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;
 }
 
-#endif
-/* the parser comes in the end, nothing in ctx knows about the parser  */
+static void
+ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
+                           CtxSegment    *edges)
+{
+  int count = edges[0].data.u32[0];
 
-#if CTX_PARSER
+  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;
 
-/* ctx parser, */
+  int aa = 15;//rasterizer->aa;
+  float coords[6][2];
 
-#define CTX_ID_MAXLEN 64 // in use should not be more than 40!
-                         // to offer headroom for multiplexing
+  for (int i = 0; i < count; i++)
+    {
+      CtxSegment *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 (i < 6)
+      {
+        coords[i][0] = x;
+        coords[i][1] = y;
+      }
+    }
 
+#if CTX_ENABLE_CLIP
 
-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;
+  if ((rasterizer->clip_rectangle==1
+       || !rasterizer->clip_buffer)
+      )
+  {
+    if (count == 5)
+    {
+      if (coords[0][0] == coords[1][0] &&
+          coords[0][1] == coords[4][1] &&
+          coords[0][1] == coords[3][1] &&
+          coords[1][1] == coords[2][1] &&
+          coords[3][0] == coords[4][0]
+          )
+      {
+#if 0
+        printf ("%d,%d %dx%d\n", minx, miny,
+                                       maxx-minx+1, maxy-miny+1);
 #endif
-  int        hold_len;
-  int        pos;
 
+         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);
 
-  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
-  CtxColorModel   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;
+         rasterizer->clip_rectangle = 1;
 
-  int        translate_origin;
+#if 0
+         if (!rasterizer->clip_buffer)
+           rasterizer->clip_buffer = ctx_buffer_new (blit_width,
+                                                     blit_height,
+                                                     CTX_CLIP_FORMAT);
 
-  CtxColorSpace   color_space_slot;
+         memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
+         int i = 0;
+         for (int y = rasterizer->state->gstate.clip_min_y;
+                  y <= rasterizer->state->gstate.clip_max_y;
+                  y++)
+         for (int x = rasterizer->state->gstate.clip_min_x;
+                  x <= rasterizer->state->gstate.clip_max_x;
+                  x++, i++)
+         {
+           ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255;
+         }
+#endif
 
-  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;
-};
+         return;
+      }
+#if 0
+      else
+      {
+        printf ("%d,%d %dx%d  0,0:%.2f 0,1:%.2f 1,0:%.2f 11:%.2f 20:%.2f 21:%2.f 30:%.2f 31:%.2f 40:%.2f 
41:%.2f\n", minx, miny,
+                                       maxx-minx+1, maxy-miny+1
+                                       
+         ,coords[0][0] ,  coords[0][1]
+         ,coords[1][0] ,  coords[1][1]
+         ,coords[2][0] ,  coords[2][1]
+         ,coords[3][0] ,  coords[3][1]
+         ,coords[4][0] ,  coords[4][1]
+         );
+      }
+#endif
+    }
+  }
+  rasterizer->clip_rectangle = 0;
 
-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;
-}
+  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
+  {
+    ctx_rasterizer_clip_reset (rasterizer);
+    return;//goto done;
+  }
 
-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;
-}
+  int we_made_it = 0;
+  CtxBuffer *clip_buffer;
 
-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);
-}
+  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);
+  }
 
-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
+  int prev_x = 0;
+  int prev_y = 0;
 
-static int ctx_arguments_for_code (CtxCode code)
-{
-  switch (code)
+    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height,
+       blit_width,
+       CTX_CLIP_FORMAT);
+
+  for (int i = 0; i < count; i++)
     {
-      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:
-        case CTX_STROKE_SOURCE:
-#endif
-        return 0;
+      CtxSegment *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);
+  }
 
-static int ctx_parser_set_command (CtxParser *parser, CtxCode code)
-{
-  if (code < 150 && code >= 32)
+  int maybe_rect = 1;
+  rasterizer->clip_rectangle = 0;
+
+  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
   {
-  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)
+    int count = blit_width * blit_height / 8;
+    for (int i = 0; i < count; i++)
     {
-      parser->expected_args = (parser->expected_args % 100) + parser->color_components;
+      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
+      (((uint8_t*)rasterizer->clip_buffer->data)[i] &
+      ((uint8_t*)clip_buffer->data)[i]);
     }
   }
-  return code;
-}
+  else
+  {
+    int count = blit_width * blit_height;
 
-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 */
+    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;
 
-  /* 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])
+    i=0;
+    /* find upper left */
+    for (; i < count && maybe_rect && !next_stage; i++)
     {
-      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);
+      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;
+      }
     }
-  }
 
-  if (str[0] && str[1])
+    next_stage = 0;
+    /* figure out with */
+    for (; i < count && !next_stage && maybe_rect; i++)
     {
-      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] == '_') )
+      int x = i % blit_width;
+      int y = i / blit_width;
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
+
+      if (y == y0)
+      {
+        switch (val)
         {
-          str += 4;
+          case 255:
+            width = x - x0 + 1;
+            break;
+          case 0:
+            next_stage = 1;
+            break;
+          default:
+            maybe_rect = 0;
+            break;
         }
-      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);
+        if (x % blit_width == blit_width - 1) next_stage = 1;
+      }
+      else next_stage = 1;
+    }
 
-          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);
+    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;
 
-          case CTX_strokeSource:
-            return ctx_parser_set_command (parser, CTX_STROKE_SOURCE);
+      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; }
+      }
+    }
 
-          /* 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);
+    next_stage = 0;
+    /* foot */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-          /* 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);
+      if (val != 0){ maybe_rect = 0; next_stage = 1; }
+    }
 
-          /* 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;
-        }
+    for (; i < count; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
     }
-  if (ret == CTX_CLOSE_PATH2)
-   {
-     ret = CTX_CLOSE_PATH;
-   }
 
-  return ctx_parser_set_command (parser, (CtxCode) ret);
+    if (maybe_rect)
+       rasterizer->clip_rectangle = 1;
+  }
+  if (!we_made_it)
+   ctx_buffer_free (clip_buffer);
+#else
+  if (coords[0][0]){};
+#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);
 }
 
-enum
+static void
+ctx_rasterizer_clip (CtxRasterizer *rasterizer)
 {
-  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;
+  int count = rasterizer->edge_list.count;
+  CtxSegment 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;
+    }
+}
 
-static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke)
+
+#if 0
+static void
+ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
+                           const char  *path,
+                           float x,
+                           float y)
 {
-  parser->color_model      = color_model;
-  parser->color_stroke     = stroke;
-  parser->color_components = ctx_color_model_get_components (color_model);
+  // 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_parser_get_color_rgba (CtxParser *parser, int offset, float *red, float *green, float *blue, 
float *alpha)
+
+CTX_INLINE void
+ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
+                          float x,
+                          float y,
+                          float width,
+                          float height)
 {
-  /* 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;
-    }
+  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, width/2, 0);
+  ctx_rasterizer_finish_shape (rasterizer);
 }
 
-static void ctx_parser_dispatch_command (CtxParser *parser)
+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)
 {
-  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)
+  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);
+  rasterizer->comp_op = NULL;
+#if 0
+  // XXX : doesn't take transforms into account - and has
+  // received less testing than code paths part of protocol,
+  // using rectangle properly will trigger the fillrect fastpath
+  ctx_rasterizer_pset (rasterizer, x, y, 255);
+#else
+  ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0);
+  ctx_rasterizer_fill (rasterizer);
+#endif
+}
+
+#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)
+{
+  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++)
     {
-      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);
+      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
 
-#define arg(a)  (parser->numbers[a])
-  parser->command = CTX_NOP;
-  //parser->n_args = 0;
-  switch (cmd)
+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*0.5f) radius = width/2;
+  if (radius > height*0.5f) 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);
+
+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)
+{
+  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);
+}
+
+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--;
+
+  if (no < 0)
+    return;
+
+  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_u32 (CTX_COMPOSITING_MODE, comp,  0),
+    ctx_u32 (CTX_BLEND_MODE,       blend, 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);
+  {
+     const char *eid = ".ctx-group";
+     int   eid_len = strlen (eid);
+
+     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;
+
+     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+  }
+  {
+    CtxEntry commands[2]=
     {
-      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_STROKE_SOURCE:
-        ctx_stroke_source (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 = (CtxColorSpace) arg(0);
-          parser->command = CTX_COLOR_SPACE; // did this work without?
-        }
-        else
-        {
-          ctx_colorspace (ctx, (CtxColorSpace)parser->color_space_slot,
-                               parser->holding, parser->pos);
-        }
-        break;
+      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
-      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);
-             CtxPixelFormat format = (CtxPixelFormat)arg(2);
-             int stride = ctx_pixel_format_get_stride (format, width);
 
+#if CTX_ENABLE_SHADOW_BLUR
+static void
+ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
+{
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-             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;
+  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
+      }
+  }
+  //free (kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+}
 
-      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 = (CtxColorSpace)unichar;
-        }
-        parser->command = CTX_DEFINE_GLYPH;
-        break;             
+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);
 
-      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:
-              case CTX_DCMYKA:
-                {
-                  float rgba[4] = {1,1,1,1.0f};
+  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_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
-              case CTX_LAB:
-              case CTX_LCH:
-              default:
-                break;
+  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);
+
+  {
+      {
+        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);
+}
+
+static void
+ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer)
+{
+  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);
+
+  {
+    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;
+  ctx_interpret_style (state, entry, NULL);
+  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 (parser->n_numbers)
-        {
-          ctx_line_dash (ctx, parser->numbers, parser->n_numbers);
-        }
+        if (c->line_dash.count)
+          {
+            ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data);
+          }
         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);
+        ctx_rasterizer_line_dash (rasterizer, 0, NULL);
         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);
+
+      case CTX_LINE_TO:
+        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
         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);
+      case CTX_REL_LINE_TO:
+        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
         break;
-      case CTX_ARC:
-        ctx_arc (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+      case CTX_MOVE_TO:
+        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
         break;
-      case CTX_APPLY_TRANSFORM:
-        ctx_apply_transform (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+      case CTX_REL_MOVE_TO:
+        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
         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;
+        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:
-        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) );
+        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:
-        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;
+        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
         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);
+        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
         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; }
+      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_REL_LINE_TO:
-        ctx_rel_line_to (ctx, arg(0), arg(1) );
-        parser->pcx += arg(0);
-        parser->pcy += arg(1);
+      case CTX_RECTANGLE:
+        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                  c->rectangle.width, c->rectangle.height);
         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);
+      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_LINE_WIDTH:
-        ctx_line_width (ctx, arg(0));
+      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_LINE_DASH_OFFSET:
-        ctx_line_dash_offset (ctx, arg(0));
+      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);
+          rasterizer->comp_op = NULL;
+          rasterizer->fragment = NULL;
+        }
         break;
-      case CTX_IMAGE_SMOOTHING:
-        ctx_image_smoothing (ctx, arg(0));
+      case CTX_TEXTURE:
+        ctx_rasterizer_set_texture (rasterizer, c->texture.eid,
+                                    c->texture.x, c->texture.y);
+        rasterizer->comp_op = NULL;
+        rasterizer->fragment = NULL;
         break;
-      case CTX_SHADOW_COLOR:
-        ctx_shadow_rgba (ctx, arg(0), arg(1), arg(2), arg(3));
+      case CTX_SOURCE_TRANSFORM:
+        ctx_matrix_set (&state->gstate.source_fill.set_transform,
+                        ctx_arg_float (0), ctx_arg_float (1),
+                        ctx_arg_float (2), ctx_arg_float (3),
+                        ctx_arg_float (4), ctx_arg_float (5));
+        rasterizer->comp_op = NULL;
         break;
-      case CTX_SHADOW_BLUR:
-        ctx_shadow_blur (ctx, arg(0) );
+#if 0
+      case CTX_LOAD_IMAGE:
+        ctx_rasterizer_load_image (rasterizer, ctx_arg_string(),
+                                   ctx_arg_float (0), ctx_arg_float (1) );
         break;
-      case CTX_SHADOW_OFFSET_X:
-        ctx_shadow_offset_x (ctx, arg(0) );
+#endif
+#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_rasterizer_gradient_add_stop (rasterizer,
+                                            ctx_arg_float (0), rgba);
+          rasterizer->comp_op = NULL;
+        }
         break;
-      case CTX_SHADOW_OFFSET_Y:
-        ctx_shadow_offset_y (ctx, arg(0) );
+      case CTX_LINEAR_GRADIENT:
+        ctx_state_gradient_clear_stops (state);
+        rasterizer->comp_op = NULL;
         break;
-      case CTX_LINE_JOIN:
-        ctx_line_join (ctx, (CtxLineJoin) arg(0) );
+      case CTX_RADIAL_GRADIENT:
+        ctx_state_gradient_clear_stops (state);
+        rasterizer->comp_op = NULL;
         break;
-      case CTX_LINE_CAP:
-        ctx_line_cap (ctx, (CtxLineCap) arg(0) );
+#endif
+      case CTX_PRESERVE:
+        rasterizer->preserve = 1;
         break;
+      case CTX_COLOR:
       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) );
+        rasterizer->comp_op = NULL;
+        //_ctx_setup_compositor (rasterizer);
         break;
-      case CTX_TEXT_BASELINE:
-        ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) );
+#if CTX_COMPOSITING_GROUPS
+      case CTX_START_GROUP:
+        ctx_rasterizer_start_group (rasterizer);
         break;
-      case CTX_TEXT_DIRECTION:
-        ctx_text_direction (ctx, (CtxTextDirection) arg(0) );
+      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_IDENTITY:
-        ctx_identity (ctx);
+      case CTX_SAVE:
+        rasterizer->comp_op = NULL;
+        rasterizer->uses_transforms = 1;
+        ctx_interpret_transforms (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 (state->keydb[i].key == CTX_clip)
+          {
+            int idx = ctx_float_to_string_index (state->keydb[i].value);
+            if (idx >=0)
+            {
+              CtxSegment *edges = (CtxSegment*)&state->stringpool[idx];
+              ctx_rasterizer_clip_apply (rasterizer, edges);
+            }
+          }
+        }
+        }
+        break;
+      case CTX_STROKE:
+#if CTX_ENABLE_SHADOW_BLUR
+        if (state->gstate.shadow_blur > 0.0 &&
+            !rasterizer->in_text)
+          ctx_rasterizer_shadow_stroke (rasterizer);
+#endif
+        {
+        int count = rasterizer->edge_list.count;
+        if (state->gstate.n_dashes)
+        {
+          int n_dashes = state->gstate.n_dashes;
+          float *dashes = state->gstate.dashes;
+          float factor = ctx_matrix_get_scale (&state->gstate.transform);
+
+          int aa = 15;//rasterizer->aa;
+          CtxSegment 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 = state->gstate.transform;
+      _ctx_matrix_identity (&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 = state->gstate.line_dash_offset * factor;
+      int   is_down = 0;
+
+          while (start < count)
+          {
+            int started = 0;
+            int i;
+            is_down = 0;
+
+            if (!is_down)
+            {
+              CtxSegment *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++)
+            {
+              CtxSegment *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
+              {
+                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;
+        }
+        state->gstate.transform = transform_backup;
+        }
+        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 (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 (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;
+      case CTX_IMAGE_SMOOTHING:
+        rasterizer->comp_op = NULL;
+        break;
+    }
+  ctx_interpret_pos_bare (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);
+}
+
+
+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;
+
+  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;
+  }
+}
+
+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;
+
+  ((CtxRasterizer*)(ctx->renderer))->aa = 
+     _ctx_antialias_to_aa (antialias);
+  ((CtxRasterizer*)(ctx->renderer))->fast_aa = 0;
+  if (antialias == CTX_ANTIALIAS_DEFAULT||
+      antialias == CTX_ANTIALIAS_FAST)
+    ((CtxRasterizer*)(ctx->renderer))->fast_aa = 1;
+}
+
+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);
+
+  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->fast_aa = (antialias == CTX_ANTIALIAS_DEFAULT||antialias == CTX_ANTIALIAS_FAST);
+  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;
+
+  if (pixel_format == CTX_FORMAT_BGRA8)
+  {
+    pixel_format = CTX_FORMAT_RGBA8;
+    rasterizer->swap_red_green = 1;
+  }
+
+  rasterizer->format = ctx_pixel_format_info (pixel_format);
+
+  return rasterizer;
+}
+
+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;
+}
+
+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;
+}
+
+// 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)
+{
+  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_format_info (CtxPixelFormat format);
+
+#else
+
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
+{
+  return NULL;
+}
+#endif
+
+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)
+    {
+      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; }
+}
+
+float ctx_x (Ctx *ctx)
+{
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return x;
+}
+
+float ctx_y (Ctx *ctx)
+{
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return y;
+}
+
+static void
+ctx_process (Ctx *ctx, CtxEntry *entry)
+{
+#if CTX_CURRENT_PATH
+  switch (entry->code)
+    {
+      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;
+    }
+#endif
+#if CTX_RASTERIZER
+  if (CTX_LIKELY(ctx->renderer && ctx->renderer->process == ctx_rasterizer_process))
+    {
+      ctx_rasterizer_process (ctx->renderer, (CtxCommand *) entry);
+    }
+  else
+#endif
+  if (CTX_LIKELY(ctx->renderer && ctx->renderer->process))
+    {
+      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
+    }
+  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);
+    }
+}
+
+
+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;
+}
+
+
+/****  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)
+{
+  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;
+}
+
+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;
+}
+
+void ctx_buffer_pixels_free (void *pixels, void *userdata)
+{
+  free (pixels);
+}
+
+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);
+  int data_len = stride * height;
+  if (pixel_format == CTX_FORMAT_YUV420)
+    data_len = width * height + ((width/2) * (height/2)) * 2;
+
+  uint8_t *pixels = (uint8_t*)ctx_calloc (data_len, 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)
+  {
+    free (buffer->eid);
+  }
+  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);
+    }
+    buffer->color_managed = NULL;
+  }
+}
+
+void ctx_buffer_free (CtxBuffer *buffer)
+{
+  ctx_buffer_deinit (buffer);
+  free (buffer);
+}
+
+static int
+ctx_texture_check_eid (Ctx *ctx, const char *eid, int *tw, int *th)
+{
+  for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+  {
+    if (ctx->texture[i].data &&
+        ctx->texture[i].eid  &&
+        !strcmp (ctx->texture[i].eid, eid))
+    {
+      if (tw) *tw = ctx->texture[i].width;
+      if (th) *th = ctx->texture[i].height;
+      ctx->texture[i].frame = ctx->texture_cache->frame;
+      return i;
+    }
+  }
+  return -1;
+}
+
+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 id = 0;
+  if (eid)
+  {
+    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+    {
+      if (ctx->texture[i].data &&
+          ctx->texture[i].eid &&
+          !strcmp (ctx->texture[i].eid, eid))
+      {
+        ctx->texture[i].frame = ctx->texture_cache->frame;
+        if (freefunc && user_data != (void*)23)
+          freefunc (pixels, user_data);
+        return ctx->texture[i].eid;
+      }
+      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);
+  }
+
+  int data_len = stride * height;
+  if (format == CTX_FORMAT_YUV420)
+          data_len = width * height +
+                  2 * ((width/2)*(height/2));
+
+  if (freefunc == ctx_buffer_pixels_free && user_data == (void*)23)
+  {
+     uint8_t *tmp = (uint8_t*)malloc (data_len);
+     memcpy (tmp, pixels, data_len);
+     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
+_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
+                                      CtxBuffer     *buffer)
+{
+   switch (buffer->format->pixel_format)
+   {
+#ifndef NO_BABL
+#if CTX_BABL
+     case CTX_FORMAT_RGBA8:
+       if (buffer->space == rasterizer->state->gstate.device_space)
+       {
+         buffer->color_managed = buffer;
+       }
+       else
+       {
+          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:
+       if (buffer->space == rasterizer->state->gstate.device_space)
+       {
+         buffer->color_managed = buffer;
+       }
+       else
+       {
+         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_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++)
+    {
+      if ( (*s & 0xC0) != 0x80)
+        { count++; }
+      if (count == utf8_length + 1)
+        { return s; }
+    }
+  return s;
+}
+
+//  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;
+}
+
+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;
+}
+
+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;
+}
+
+#if CTX_RASTERIZER
+
+
+
+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;
+}
+
+static void
+_ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
+{
+  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];
+      }
+    }
+}
+
+static int ctx_str_count_lines (const char *str)
+{
+  int count = 0;
+  for (const char *p = str; *p; p++)
+    if (*p == '\n') count ++;
+  return count;
+}
+
+static void
+ctx_hasher_process (void *user_data, CtxCommand *command)
+{
+  CtxEntry *entry = &command->entry;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
+  CtxHasher *hasher = (CtxHasher*) user_data;
+  CtxState *state = rasterizer->state;
+  CtxCommand *c = (CtxCommand *) entry;
+  int aa = 15;//rasterizer->aa;
+
+  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());
+
+
+          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 * (ctx_str_count_lines (ctx_arg_string()) + 1.5);
+          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);
+
+          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);
+
+           CtxIntRectangle shape_rect = {
+              (int)rasterizer->x, (int)(rasterizer->y - height),
+              (int)width, (int)(height * 2)
+           };
+
+#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);
+
+          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));
+
+          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);
+
+          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 = {(int)tx,(int)(ty-th/2),(int)tw,(int)th};
+
+
+#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;
+
+      case CTX_FILL:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+
+          /* 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 = {
+          (int)(rasterizer->col_min / CTX_SUBDIV - 2),
+          (int)(rasterizer->scan_min / aa - 2),
+          (int)(3+(rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV),
+          (int)(3+(rasterizer->scan_max - rasterizer->scan_min + 1) / aa)
+        };
+
+        hash ^= (rasterizer->state->gstate.fill_rule * 23);
+
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+
+        {
+          int is = rasterizer->state->gstate.image_smoothing;
+          ctx_sha1_process(&sha1, (uint8_t*)&is, sizeof(int));
+        }
+
+          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;
+      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 = {
+          (int)(rasterizer->col_min / CTX_SUBDIV - rasterizer->state->gstate.line_width),
+          (int)(rasterizer->scan_min / aa - rasterizer->state->gstate.line_width),
+          (int)((rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV + 
rasterizer->state->gstate.line_width),
+          (int)((rasterizer->scan_max - rasterizer->scan_min + 1) / aa + 
rasterizer->state->gstate.line_width)
+        };
+
+        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;
+
+        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
+        hash ^= (rasterizer->state->gstate.line_cap * 23);
+        hash ^= (rasterizer->state->gstate.source_stroke.type * 117);
+
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+
+        uint32_t color;
+        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
+
+          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);
+        }
+        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
+         */
+
+      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;
+    }
+}
+
+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          = 15;
+
+  hasher->rows = rows;
+  hasher->cols = cols;
+
+  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
+  ctx_sha1_init (&hasher->sha1_fill);
+  ctx_sha1_init (&hasher->sha1_stroke);
+
+  return rasterizer;
+}
+
+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];
+}
+
+#endif
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <termios.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+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);
+#if __COSMOPOLITAN__
+  /// XXX ?
+#else
+  tcdrain(STDIN_FILENO);
+#endif
+  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;
+
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    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++; semi = strchr (semi, ';');}
+  if (semi)
+  {
+    return atoi(semi + 1);
+  }
+  return 0;
+}
+
+int ctx_terminal_height (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);
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
+#endif
+  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;
+
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    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)
+  {
+    return atoi(semi + 1);
+  }
+  return 0;
+}
+
+int ctx_terminal_cols (void)
+{
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 80;
+  return ws.ws_col;
+} 
+
+int ctx_terminal_rows (void)
+{
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 25;
+  return ws.ws_row;
+}
+
+
+
+
+
+#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 *************************/
+
+#if !__COSMOPOLITAN__
+#include <termios.h>
+#include <errno.h>
+#include <signal.h>
+#endif
+
+#define DELAY_MS  100  
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+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};
+
+/* 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"},
+
+  {"shift-up",            "⇧↑",    "\033[1;2A"},
+  {"shift-down",          "⇧↓",    "\033[1;2B"},
+  {"shift-right",         "⇧→",    "\033[1;2C"},
+  {"shift-left",          "⇧←",    "\033[1;2D"},
+
+  {"alt-up",              "^↑",    "\033[1;3A"},
+  {"alt-down",            "^↓",    "\033[1;3B"},
+  {"alt-right",           "^→",    "\033[1;3C"},
+  {"alt-left",            "^←",    "\033[1;3D"},
+
+  {"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"},
+
+  {"control-up",          "^↑",    "\033[1;5A"},
+  {"control-down",        "^↓",    "\033[1;5B"},
+  {"control-right",       "^→",    "\033[1;5C"},
+  {"control-left",        "^←",    "\033[1;5D"},
+
+  /* putty */
+  {"control-up",          "^↑",    "\033OA"},
+  {"control-down",        "^↓",    "\033OB"},
+  {"control-right",       "^→",    "\033OC"},
+  {"control-left",        "^←",    "\033OD"},
+
+  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
+  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
+  {"control-shift-right", "^⇧→",   "\033[1;6C"},
+  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
+
+  {"control-up",          "^↑",    "\033Oa"},
+  {"control-down",        "^↓",    "\033Ob"},
+  {"control-right",       "^→",    "\033Oc"},
+  {"control-left",        "^←",    "\033Od"},
+
+  {"shift-up",            "⇧↑",    "\033[a"},
+  {"shift-down",          "⇧↓",    "\033[b"},
+  {"shift-right",         "⇧→",    "\033[c"},
+  {"shift-left",          "⇧←",    "\033[d"},
+
+  {"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~"},
+
+  {"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 _nc_noraw (void)
+{
+  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
+    nc_is_raw = 0;
+}
+
+void
+nc_at_exit (void)
+{
+  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");
+}
+
+static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
+{
+  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;
+
+  if (x) *x = n->mouse_x;
+  if (y) *y = n->mouse_y;
+
+  if ((prev_state & 1) != (buf[0] & 1))
+    {
+      if (buf[0] & 1) ret = "mouse-press";
+    }
+  else if (buf[0] & 1)
+    ret = "mouse-drag";
+
+  if ((prev_state & 2) != (buf[0] & 2))
+    {
+      if (buf[0] & 2) ret = "mouse2-press";
+    }
+  else if (buf[0] & 2)
+    ret = "mouse2-drag";
+
+  if ((prev_state & 4) != (buf[0] & 4))
+    {
+      if (buf[0] & 4) ret = "mouse1-press";
+    }
+  else if (buf[0] & 4)
+    ret = "mouse1-drag";
+
+  prev_state = buf[0];
+  return ret;
+}
+
+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)
+{
+  if (!mev_q)
+    return NULL;
+  *x = mev_x;
+  *y = mev_y;
+  mev_q = 0;
+  return mev_type;
+}
+
+static int mouse_has_event (Ctx *n)
+{
+  struct timeval tv;
+  int retval;
+
+  if (mouse_mode == NC_MOUSE_NONE)
+    return 0;
+
+  if (mev_q)
+    return 1;
+
+  if (n->mouse_fd == 0)
+    return 0;
+  return 0;
+
+  {
+    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 (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;
+    }
+  return retval != 0;
+}
+
+
+static int _nc_raw (void)
+{
+  struct termios raw;
+  if (!isatty (STDIN_FILENO))
+    return -1;
+  if (!atexit_registered)
+    {
+      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;
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
+  tcflush(STDIN_FILENO, 1);
+#endif
+  return 0;
+}
+
+static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
+{
+  int i;
+  int matches = 0;
+
+  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_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)
+{
+  unsigned char buf[20];
+  int length;
+
+
+  if (x) *x = -1;
+  if (y) *y = -1;
+
+  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]);
+
+  {
+    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_nct_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);
+  }
+
+  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";
+}
+
+int ctx_nct_consume_events (Ctx *ctx)
+{
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
+
+  {
+    float x, y;
+    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
+
+    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, "return", 0);
+      else if (!strcmp (event, "idle"))
+      {
+      }
+      else
+      ctx_key_press (ctx, 0, event, 0);
+    }
+  }
+
+  return 1;
+}
+
+const char *ctx_native_get_event (Ctx *n, int timeoutms)
+{
+  static unsigned char buf[256];
+  int length;
+
+  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]);
+
+    int got_event = 0;
+  {
+    int elapsed = 0;
+
+    do {
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      got_event = ctx_nct_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_nct_has_event (n, 5);
+    }
+  return NULL;
+}
+
+const char *ctx_key_get_label (Ctx  *n, const char *nick)
+{
+  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;
+}
+
+void _ctx_mouse (Ctx *term, int mode)
+{
+  //if (term->is_st && mode > 1)
+  //  mode = 1;
+  if (mode != mouse_mode)
+  {
+    printf ("%s", mouse_modes[mode]);
+    fflush (stdout);
+  }
+  mouse_mode = mode;
+}
+
+
+#endif
+
+#if !__COSMOPOLITAN__
+#include <sys/time.h>
+#endif
+
+
+#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
+
+#if !__COSMOPOLITAN__
+static struct timeval start_time;
+
+static void
+_ctx_init_ticks (void)
+{
+  static int done = 0;
+  if (done)
+    return;
+  done = 1;
+  gettimeofday (&start_time, NULL);
+}
+
+static inline unsigned long
+_ctx_ticks (void)
+{
+  struct timeval measure_time;
+  gettimeofday (&measure_time, NULL);
+  return usecs (measure_time) - usecs (start_time);
+}
+
+unsigned long
+ctx_ticks (void)
+{
+  _ctx_init_ticks ();
+  return _ctx_ticks ();
+}
+
+
+
+enum _CtxFlags {
+   CTX_FLAG_DIRECT = (1<<0),
+};
+typedef enum _CtxFlags CtxFlags;
+
+
+int _ctx_max_threads = 1;
+int _ctx_enable_hash_cache = 1;
+#if CTX_SHAPE_CACHE
+extern int _ctx_shape_cache_enabled;
+#endif
+
+#if CTX_THREADS
+static mtx_t _ctx_texture_mtx;
+#endif
+
+void _ctx_texture_lock (void)
+{
+#if CTX_THREADS
+  mtx_lock (&_ctx_texture_mtx);
+#endif
+}
+
+void _ctx_texture_unlock (void)
+{
+#if CTX_THREADS
+  mtx_unlock (&_ctx_texture_mtx);
+#endif
+}
+
+
+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
+}
+
+
+#if 0
+int ctx_count (Ctx *ctx)
+{
+  return ctx->drawlist.count;
+}
+#endif
+
+extern int _ctx_damage_control;
+
+static void ctx_list_backends()
+{
+    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");
+}
+
+#if CTX_EVENTS
+
+static uint32_t ctx_ms (Ctx *ctx)
+{
+  return _ctx_ticks () / 1000;
+}
+
+
+static int is_in_ctx (void);
+Ctx *ctx_new_ui (int width, int height)
+{
+#if CTX_TILED
+  if (getenv ("CTX_DAMAGE_CONTROL"))
+  {
+    const char * val = getenv ("CTX_DAMAGE_CONTROL");
+    if (!strcmp (val, "0") ||
+        !strcmp (val, "off"))
+      _ctx_damage_control = 0;
+    else
+      _ctx_damage_control = 1;
+  }
+#endif
+
+  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;
+  }
+#if CTX_SHAPE_CACHE
+  if (getenv ("CTX_SHAPE_CACHE"))
+  {
+    const char * val = getenv ("CTX_SHAPE_CACHE");
+    if (!strcmp (val, "0"))
+      _ctx_shape_cache_enabled = 0;
+    if (!strcmp (val, "off"))
+      _ctx_shape_cache_enabled = 0;
+  }
+#endif
+
+  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
+  }
+  
+#if CTX_THREADS
+  mtx_init (&_ctx_texture_mtx, mtx_plain);
+#endif
+
+  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
+  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
+
+  //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"))
+  {
+    ctx_list_backends ();
+    exit (-1);
+  }
+
+  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 ()))
+  {
+    if (!backend || !strcmp (backend, "ctx"))
+    {
+      // full blown ctx protocol - in terminal or standalone
+      ret = ctx_new_ctx (width, height);
+    }
+  }
+
+#if CTX_SDL
+  if (!ret && getenv ("DISPLAY"))
+  {
+    if ((backend==NULL) || (!strcmp (backend, "SDL")))
+      ret = ctx_new_sdl (width, height);
+  }
+#endif
+
+#if CTX_FB
+  if (!ret && !getenv ("DISPLAY"))
+  {
+    if ((backend==NULL) || (!strcmp (backend, "drm")))
+    ret = ctx_new_fb (width, height, 1);
+
+    if (!ret)
+    {
+      if ((backend==NULL) || (!strcmp (backend, "fb")))
+        ret = ctx_new_fb (width, height, 0);
+    }
+  }
+#endif
+
+#if CTX_RASTERIZER
+  // braille in terminal
+  if (!ret)
+  {
+    if ((backend==NULL) || (!strcmp (backend, "term")))
+    ret = ctx_new_term (width, height);
+  }
+  if (!ret)
+  {
+    if ((backend==NULL) || (!strcmp (backend, "termimg")))
+    ret = ctx_new_termimg (width, height);
+  }
+#endif
+  if (!ret)
+  {
+    fprintf (stderr, "no interactive ctx backend\n");
+    ctx_list_backends ();
+    exit (2);
+  }
+  ctx_get_event (ret); // enables events
+  return ret;
+}
+#endif
+#else
+void _ctx_texture_unlock (void)
+{
+}
+void _ctx_texture_lock (void)
+{
+}
+
+#endif
+void _ctx_resized (Ctx *ctx, int width, int height, long time);
+
+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);
+  }
+#endif
+}
+
+#if CTX_EVENTS
+
+
+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);
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
+#endif
+  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;
+
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    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;
+}
+
+typedef struct CtxIdleCb {
+  int (*cb) (Ctx *ctx, void *idle_data);
+  void *idle_data;
+
+  void (*destroy_notify)(void *destroy_data);
+  void *destroy_data;
+
+  int   ticks_full;
+  int   ticks_remaining;
+  int   is_idle;
+  int   id;
+} CtxIdleCb;
+
+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  */
+
+  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)
+  {
+    return;
+  }
+  for (l = ctx->events.idles; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
+
+    if (item->ticks_remaining >= 0)
+      item->ticks_remaining -= tick_delta;
+
+    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)
+  {
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
+  }
+}
+
+
+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)
+  {
+    fprintf (stderr, "warning: binding overflow\n");
+    return;
+  }
+  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 ++)
+  {
+    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;
+}
+
+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 = 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;
+      }
+    }
+  ctx_collect_events (event, data1, data2);
+}
+
+CtxBinding *ctx_get_bindings (Ctx *ctx)
+{
+  return &ctx->events.bindings[0];
+}
+
+void ctx_remove_idle (Ctx *ctx, int handle)
+{
+  CtxList *l;
+  CtxList *to_remove = NULL;
+
+  if (!ctx->events.idles)
+  {
+    return;
+  }
+  for (l = ctx->events.idles; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
+    if (item->id == handle)
+      ctx_list_prepend (&to_remove, item);
+  }
+  for (l = to_remove; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
+  }
+}
+
+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)
+{
+  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;
+}
+
+int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+{
+  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
+}
+
+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);
+}
+
+#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++;
+}
+
+
+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);
+  }
+}
+
+
+static int
+path_equal (void *path,
+            void *path2)
+{
+  //  XXX
+  return 0;
+}
+
+void ctx_listen_set_cursor (Ctx      *ctx,
+                            CtxCursor cursor)
+{
+  if (ctx->events.last_item)
+  {
+    ctx->events.last_item->cursor = cursor;
+  }
+}
+
+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;
+
+    /* 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;
+      }
+    }
+
+    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);
+
+    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)
+{
+  if (event)
+    event->stop_propagate = 1;
+}
+
+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;
+  }
+
+  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);
+}
+
+
+static void ctx_report_hit_region (CtxEvent *event,
+                       void     *data,
+                       void     *data2)
+{
+  const char *id = data;
+
+  fprintf (stderr, "hit region %s\n", id);
+  // XXX: NYI
+}
+
+void ctx_add_hit_region (Ctx *ctx, const char *id)
+{
+  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);
+}
+
+typedef struct _CtxGrab CtxGrab;
+
+struct _CtxGrab
+{
+  CtxItem *item;
+  int      device_no;
+  int      timeout_id;
+  int      start_time;
+  float    x; // for tap and hold
+  float    y;
+  CtxEventType  type;
+};
+
+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);
+}
+
+static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
+{
+  ctx_list_remove (&ctx->events.grabs, grab);
+  grab_free (ctx, grab);
+}
+
+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;
+}
+
+static CtxList *_ctx_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_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))
+  {
+    for (a = ctx->events.items; a; a = a->next)
+    {
+      CtxItem *item = a->data;
+      if (item->types & type)
+      {
+        ctx_list_prepend (&ret, item);
+        return ret;
+      }
+    }
+    return NULL;
+  }
+
+  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 (u >= item->x0 && v >= item->y0 &&
+        u <  item->x1 && v <  item->y1 && 
+        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
+        item->cursor)))
+    {
+      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);
+      }
+    }
+  }
+  return ret;
+}
+
+CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
+{
+  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 int
+_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
+{
+  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
+
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <stdatomic.h>
+#endif
+
+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_nct_has_event (Ctx  *n, int delay_ms);
+int ctx_ctx_consume_events (Ctx *ctx);
+
+
+
+void ctx_consume_events (Ctx *ctx)
+{
+#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);
+}
+
+int ctx_has_event (Ctx *ctx, int timeout)
+{
+#if CTX_SDL
+  if (ctx_sdl_events)
+  {
+    return SDL_WaitEventTimeout (NULL, timeout);
+  }
+  else
+#endif
+#if CTX_FB
+  if (ctx_fb_events)
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+  else
+#endif
+  if (ctx_native_events)
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+  else
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+
+  ctx_consume_events (ctx);
+  if (ctx->events.events)
+    return 1;
+  return 0;
+}
+
+#if CTX_FB
+static int ctx_fb_get_mice_fd (Ctx *ctx);
+#endif
+
+void ctx_get_event_fds (Ctx *ctx, int *fd, int *count)
+{
+#if CTX_SDL
+  if (ctx_sdl_events)
+  {
+    *count = 0;
+  }
+  else
+#endif
+#if CTX_FB
+  if (ctx_fb_events)
+  {
+    int mice_fd = ctx_fb_get_mice_fd (ctx);
+    fd[0] = STDIN_FILENO;
+    if (mice_fd)
+    {
+      fd[1] = mice_fd;
+      *count = 2;
+    }
+    else
+    {
+      *count = 1;
+    }
+  }
+  else
+#endif
+  if (ctx_native_events)
+  {
+    fd[0] = STDIN_FILENO;
+    *count = 1;
+  }
+  else
+  {
+    fd[0] = STDIN_FILENO;
+    *count = 1;
+  }
+}
+
+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)
+    {
+      event_copy = *((CtxEvent*)(ctx->events.events->data));
+      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
+      return &event_copy;
+    }
+  return NULL;
+}
+
+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;
+  }
+  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])
+    {
+      {
+#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;
+    }
+    if (current)
+    {
+#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
+      _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;
+}
+
+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, };
+
+  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]);
+
+  ctx_list_free (&list);
+
+  grab->timeout_id = 0;
+
+  return 0;
+
+  return ret;
+}
+
+int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
+                      char *string)
+{
+  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)
+    {
+      ctx_list_free (&hitlist);
+      return 0;
+    }
+  }
+
+  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+
+  return 0;
+}
+
+int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+{
+  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;
+  }
+
+  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);
+
+  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)))
+    {
+      grab = device_add_grab (ctx, device_no, item, item->types);
+      grab->start_time = time;
+
+      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)
+    {
+      ctx_list_free (&hitlist);
+      return 0;
+    }
+  }
+
+  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+  return 0;
+}
+
+void _ctx_resized (Ctx *ctx, int width, int height, long time)
+{
+  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);
+  }
+
+}
+
+int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+{
+  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 = _ctx_device_get_grabs (ctx, device_no);
+
+  for (g = grablist; g; g = g->next)
+  {
+    grab = g->data;
+
+    if (!event->stop_propagate)
+    {
+      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 = _ctx_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 128 // becomes max clients..
+
+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_input_pending (Ctx *ctx, 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);
+  }
+  int input_fds[5];
+  int n_fds;
+  ctx_get_event_fds (ctx, input_fds, &n_fds);
+  for (int i = 0; i < n_fds; i++)
+  {
+    FD_SET (input_fds[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 + 1, &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
+
+
+#define CTX_REPORT_COL_ROW 0
+
+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;
+
+#if CTX_REPORT_COL_ROW
+  int        line; /*  for error reporting */
+  int        col;  /*  for error reporting */
+#endif
+  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
+  CtxColorModel   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        translate_origin;
+
+  CtxColorSpace   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;
+  int   prev_byte;
+};
+
+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) );
+#if CTX_REPORT_COL_ROW
+  parser->line             = 1;
+#endif
+  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_SOURCE_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:
+        case CTX_STROKE_SOURCE:
+#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);
+      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_sourceTransform: ret = CTX_SOURCE_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);
+
+          case CTX_strokeSource:
+            return ctx_parser_set_command (parser, CTX_STROKE_SOURCE);
+
+          /* 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_nonzero:     return CTX_FILL_RULE_WINDING;
+          case CTX_non_zero:    return CTX_FILL_RULE_WINDING;
+          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;
+   }
+
+  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_PARSER_STRING_YENC,
+} 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 (parser->expected_args != CTX_ARG_STRING_OR_NUMBER &&
+      parser->expected_args != CTX_ARG_COLLECT_NUMBERS &&
+      parser->expected_args != parser->n_numbers)
+    {
+#if CTX_REPORT_COL_ROW
+         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
+      //return;
+    }
+
+#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_STROKE_SOURCE:
+        ctx_stroke_source (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 = (CtxColorSpace) arg(0);
+          parser->command = CTX_COLOR_SPACE; // did this work without?
+        }
+        else
+        {
+          ctx_colorspace (ctx, (CtxColorSpace)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);
+             CtxPixelFormat format = (CtxPixelFormat)arg(2);
+             int stride = ctx_pixel_format_get_stride (format, width);
+             int data_len = stride * height;
+             if (format == CTX_FORMAT_YUV420)
+                 data_len = height * width + 2*(height/2) * (width/2);
+
+
+             if (parser->pos != data_len)
+             {
+             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 = (CtxColorSpace)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:
+              case CTX_DCMYKA:
+                {
+                  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
+              case CTX_LAB:
+              case CTX_LCH:
+              default:
+                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_SOURCE_TRANSFORM:
+        ctx_source_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);
+        if (parser->translate_origin)
+        {
+          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 inline void ctx_parser_holding_append (CtxParser *parser, int byte)
+{
+#if !CTX_PARSER_FIXED_TEMP
+  if (CTX_UNLIKELY(parser->hold_len < parser->pos + 1 + 1))
+  {
+    int new_len = parser->hold_len * 2;
+    if (new_len < 512) new_len = 512;
+    parser->holding = (uint8_t*)realloc (parser->holding, new_len);
+    parser->hold_len = new_len;
+  }
+#endif
+
+  parser->holding[parser->pos++]=byte;
+#if CTX_PARSER_FIXED_TEMP
+  if (CTX_UNLIKELY(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;
+      char c = ',';
+      ctx_parser_feed_bytes (parser, &c, 1);
+    }
+  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);
+  }
+}
+
+static inline void ctx_parser_feed_byte (CtxParser *parser, char byte)
+{
+#if CTX_REPORT_COL_ROW
+    if (CTX_UNLIKELY(byte == '\n'))
+    {
+        parser->col=0;
+        parser->line++;
+    }
+    else
+    {
+        parser->col++;
+    }
+#endif
+
+    if (CTX_LIKELY(parser->state == CTX_PARSER_STRING_YENC))
+    {
+        if (CTX_UNLIKELY((parser->prev_byte == '=') && (byte == 'y')))
+        {
+          parser->state = CTX_PARSER_NEUTRAL;
+                 //   fprintf (stderr, "got %i\n", parser->pos);
+          parser->pos = ctx_ydec ((char*)parser->holding, (char*)parser->holding, parser->pos) - 1;
+#if 0
+          if (parser->pos > 5)
+                    fprintf (stderr, "dec got %i %c %c %c %c\n", parser->pos,
+                                    parser->holding[0],
+                                    parser->holding[1],
+                                    parser->holding[2],
+                                    parser->holding[3]
+                                    );
+#endif
+          ctx_parser_string_done (parser);
+        }
+        else
+        {
+          ctx_parser_holding_append (parser, byte);
+        }
+        parser->prev_byte = byte;
+        return;
+    }
+    else if (CTX_LIKELY(parser->state == CTX_PARSER_STRING_A85))
+    {
+        /* since these are our largest bulk transfers, minimize
+         * overhead for this case. */
+        if (CTX_LIKELY(byte!='~')) 
+        {
+          ctx_parser_holding_append (parser, byte);
+        }
+        else
+        {
+          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);
+        }
+        return;
+    }
+  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;
+              break;
+            case '\'':
+              parser->state = CTX_PARSER_STRING_APOS;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '=':
+              parser->state = CTX_PARSER_STRING_YENC;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '~':
+              parser->state = CTX_PARSER_STRING_A85;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '"':
+              parser->state = CTX_PARSER_STRING_QUOT;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '-':
+              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 0;
+              break;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->numbers[parser->n_numbers] += (byte - '0');
+              parser->decimal = 0;
+              break;
+            case '.':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 1;
+              break;
+            default:
+              parser->state = CTX_PARSER_WORD;
+              parser->pos = 0;
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        break;
+      case CTX_PARSER_NUMBER:
+      case CTX_PARSER_NEGATIVE_NUMBER:
+        {
+          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:
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case ' ':
+              case '\t':
+              case '\r':
+              case '\n':
+              case ';':
+              case ',':
+              case '(':
+              case ')':
+              case '{':
+              case '}':
+              case '=':
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '#':
+                parser->state = CTX_PARSER_COMMENT;
+                break;
+              case '-':
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+                parser->numbers[parser->n_numbers+1] = 0;
+                parser->n_numbers ++;
+                parser->decimal = 0;
+                break;
+              case '.':
+                //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':
+                if (parser->decimal)
+                  {
+                    parser->decimal *= 10;
+                    parser->numbers[parser->n_numbers] += (byte - '0') / (1.0 * parser->decimal);
+                  }
+                else
+                  {
+                    parser->numbers[parser->n_numbers] *= 10;
+                    parser->numbers[parser->n_numbers] += (byte - '0');
+                  }
+                break;
+              case '@': // cells
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_cell (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '%': // percent of width/height
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '^': // percent of height
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent_height (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '~': // percent of width
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent_width (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              default:
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_WORD;
+                parser->pos = 0;
+                ctx_parser_holding_append (parser, byte);
+                break;
+            }
+          if ( (parser->state != CTX_PARSER_NUMBER) &&
+               (parser->state != CTX_PARSER_NEGATIVE_NUMBER))
+            {
+              parser->n_numbers ++;
+              ctx_parser_number_done (parser);
+
+              if (parser->n_numbers == parser->expected_args ||
+                  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;
+                  CtxCode tmp3 = parser->command;
+                  int tmp4 = parser->expected_args;
+                  ctx_parser_dispatch_command (parser);
+                  parser->command = tmp3;
+                  switch (parser->command)
+                  {
+                    case CTX_DEFINE_TEXTURE:
+                    case CTX_TEXTURE:
+                      parser->n_numbers = tmp1;
+                      parser->n_args = tmp2;
+                      break;
+                          default:
+                      parser->n_numbers = 0;
+                      parser->n_args = 0;
+                      break;
+                  }
+                  parser->expected_args = tmp4;
+                }
+              if (parser->n_numbers > CTX_PARSER_MAX_ARGS)
+                { parser->n_numbers = CTX_PARSER_MAX_ARGS;
+                }
+            }
+        }
+        break;
+      case CTX_PARSER_WORD:
+        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 ' ': case '\t': case '\r': case '\n':
+            case ';': case ',':
+            case '(': case ')': case '=': case '{': case '}':
+              parser->state = CTX_PARSER_NEUTRAL;
+              break;
+            case '#':
+              parser->state = CTX_PARSER_COMMENT;
+              break;
+            case '-':
+              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 0;
+              break;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->numbers[parser->n_numbers] += (byte - '0');
+              parser->decimal = 0;
+              break;
+            case '.':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 1;
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        if (parser->state != CTX_PARSER_WORD)
+          {
+            ctx_parser_word_done (parser);
+          }
+        break;
+      case CTX_PARSER_STRING_A85:
+        if (CTX_LIKELY(byte!='~'))
+        {
+          ctx_parser_holding_append (parser, byte);
+        }
+        else
+        {
+          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;
+      case CTX_PARSER_STRING_APOS:
+        switch (byte)
+          {
+            case '\\': parser->state = CTX_PARSER_STRING_APOS_ESCAPED; break;
+            case '\'': parser->state = CTX_PARSER_NEUTRAL;
+              ctx_parser_string_done (parser);
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte); break;
+          }
+        break;
+      case CTX_PARSER_STRING_APOS_ESCAPED:
+        switch (byte)
+          {
+            case '0': byte = '\0'; break;
+            case 'b': byte = '\b'; break;
+            case 'f': byte = '\f'; break;
+            case 'n': byte = '\n'; break;
+            case 'r': byte = '\r'; break;
+            case 't': byte = '\t'; break;
+            case 'v': byte = '\v'; break;
+            default: break;
+          }
+        ctx_parser_holding_append (parser, byte);
+        parser->state = CTX_PARSER_STRING_APOS;
+        break;
+      case CTX_PARSER_STRING_QUOT_ESCAPED:
+        switch (byte)
+          {
+            case '0': byte = '\0'; break;
+            case 'b': byte = '\b'; break;
+            case 'f': byte = '\f'; break;
+            case 'n': byte = '\n'; break;
+            case 'r': byte = '\r'; break;
+            case 't': byte = '\t'; break;
+            case 'v': byte = '\v'; break;
+            default: break;
+          }
+        ctx_parser_holding_append (parser, byte);
+        parser->state = CTX_PARSER_STRING_QUOT;
+        break;
+      case CTX_PARSER_STRING_QUOT:
+        switch (byte)
+          {
+            case '\\':
+              parser->state = CTX_PARSER_STRING_QUOT_ESCAPED;
+              break;
+            case '"':
+              parser->state = CTX_PARSER_NEUTRAL;
+              ctx_parser_string_done (parser);
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        break;
+      case CTX_PARSER_COMMENT:
+        switch (byte)
+          {
+            case '\r':
+            case '\n':
+              parser->state = CTX_PARSER_NEUTRAL;
+            default:
+              break;
+          }
+        break;
+    }
+}
+
+void ctx_parser_feed_bytes (CtxParser *parser, const char *data, int count)
+{
+  for (int i = 0; i < count; i++)
+    ctx_parser_feed_byte (parser, data[i]);
+}
+
+void ctx_parse (Ctx *ctx, const char *string)
+{
+  if (!string)
+    return;
+  CtxParser *parser = ctx_parser_new (ctx, ctx_width(ctx),
+                                           ctx_height(ctx),
+                                           ctx_get_font_size(ctx),
+                                           ctx_get_font_size(ctx),
+                                           0, 0, NULL, NULL, NULL, NULL, NULL);
+  ctx_parser_feed_bytes (parser, string, strlen (string));
+  ctx_parser_free (parser);
+}
+
+#endif
+
+static CtxFont ctx_fonts[CTX_MAX_FONTS];
+static int     ctx_font_count = 0;
+
+#if CTX_FONT_ENGINE_STB
+static float
+ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar);
+static float
+ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB);
+static int
+ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke);
+
+CtxFontEngine ctx_font_engine_stb =
+{
+#if CTX_FONTS_FROM_FILE
+  ctx_load_font_ttf_file,
+#endif
+  ctx_load_font_ttf,
+  ctx_glyph_stb,
+  ctx_glyph_width_stb,
+  ctx_glyph_kern_stb,
+};
+
+int
+ctx_load_font_ttf (const char *name, const void *ttf_contents, int length)
+{
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
+  ctx_fonts[ctx_font_count].type = 1;
+  ctx_fonts[ctx_font_count].name = (char *) malloc (strlen (name) + 1);
+  ctx_strcpy ( (char *) ctx_fonts[ctx_font_count].name, name);
+  if (!stbtt_InitFont (&ctx_fonts[ctx_font_count].stb.ttf_info, ttf_contents, 0) )
+    {
+      ctx_log ( "Font init failed\n");
+      return -1;
+    }
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_stb;
+  ctx_font_count ++;
+  return ctx_font_count-1;
+}
+
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ttf_file (const char *name, const char *path)
+{
+  uint8_t *contents = NULL;
+  long length = 0;
+  ctx_get_contents (path, &contents, &length);
+  if (!contents)
+    {
+      ctx_log ( "File load failed\n");
+      return -1;
+    }
+  return ctx_load_font_ttf (name, contents, length);
+}
+#endif
+
+static int
+ctx_glyph_stb_find (CtxFont *font, uint32_t unichar)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  int index = font->stb.cache_index;
+  if (font->stb.cache_unichar == unichar)
+    {
+      return index;
+    }
+  font->stb.cache_unichar = 0;
+  index = font->stb.cache_index = stbtt_FindGlyphIndex (ttf_info, unichar);
+  font->stb.cache_unichar = unichar;
+  return index;
+}
+
+static float
+ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  float font_size          = ctx->state.gstate.font_size;
+  float scale              = stbtt_ScaleForPixelHeight (ttf_info, font_size);
+  int advance, lsb;
+  int glyph = ctx_glyph_stb_find (font, unichar);
+  if (glyph==0)
+    { return 0.0f; }
+  stbtt_GetGlyphHMetrics (ttf_info, glyph, &advance, &lsb);
+  return (advance * scale);
+}
+
+static float
+ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  float font_size = ctx->state.gstate.font_size;
+  float scale = stbtt_ScaleForPixelHeight (ttf_info, font_size);
+  int glyphA = ctx_glyph_stb_find (font, unicharA);
+  int glyphB = ctx_glyph_stb_find (font, unicharB);
+  return stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale;
+}
+
+static int
+ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  int glyph = ctx_glyph_stb_find (font, unichar);
+  if (glyph==0)
+    { return -1; }
+  float font_size = ctx->state.gstate.font_size;
+  int   baseline = ctx->state.y;
+  float origin_x = ctx->state.x;
+  float origin_y = baseline;
+  float scale    = stbtt_ScaleForPixelHeight (ttf_info, font_size);;
+  stbtt_vertex *vertices = NULL;
+  ctx_begin_path (ctx);
+  int num_verts = stbtt_GetGlyphShape (ttf_info, glyph, &vertices);
+  for (int i = 0; i < num_verts; i++)
+    {
+      stbtt_vertex *vertex = &vertices[i];
+      switch (vertex->type)
+        {
+          case STBTT_vmove:
+            ctx_move_to (ctx,
+                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
+            break;
+          case STBTT_vline:
+            ctx_line_to (ctx,
+                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
+            break;
+          case STBTT_vcubic:
+            ctx_curve_to (ctx,
+                          origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
+                          origin_x + vertex->cx1 * scale, origin_y - vertex->cy1 * scale,
+                          origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
+            break;
+          case STBTT_vcurve:
+            ctx_quad_to (ctx,
+                         origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
+                         origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
+            break;
+        }
+    }
+  stbtt_FreeShape (ttf_info, vertices);
+  if (stroke)
+    {
+      ctx_stroke (ctx);
+    }
+  else
+    { ctx_fill (ctx); }
+  return 0;
+}
+#endif
+
+#if CTX_FONT_ENGINE_CTX
+
+/* XXX: todo remove this, and rely on a binary search instead
+ */
+static int ctx_font_find_glyph_cached (CtxFont *font, uint32_t glyph)
+{
+  for (int i = 0; i < font->ctx.glyphs; i++)
+    {
+      if (font->ctx.index[i * 2] == glyph)
+        { return font->ctx.index[i * 2 + 1]; }
+    }
+  return -1;
+}
+
+static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  int ret = ctx_font_find_glyph_cached (font, unichar);
+  if (ret >= 0) return ret;
+
+  for (int i = 0; i < font->ctx.length; i++)
+  {
+    CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+    if (entry->code == CTX_DEFINE_GLYPH &&
+        entry->data.u32[0] == unichar)
+    {
+       return i;
+       // XXX this could be prone to insertion of valid header
+       // data in included bitmaps.. is that an issue?
+       //   
+    }
+  }
+  return -1;
+}
+
+
+static float
+ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+{
+  float font_size = ctx->state.gstate.font_size;
+  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
+  if (first_kern < 0) return 0.0;
+  for (int i = first_kern + 1; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_KERNING_PAIR)
+        {
+          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
+            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
+        }
+      if (entry->code == CTX_DEFINE_GLYPH)
+        return 0.0;
+    }
+  return 0.0;
+}
+#if 0
+static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar)
+{
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
+        { return i; }
+    }
+  return 0;
+}
+#endif
+
+
+static float
+ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  CtxState *state = &ctx->state;
+  float font_size = state->gstate.font_size;
+  int   start     = ctx_glyph_find_ctx (font, ctx, unichar);
+  if (start < 0)
+    { return 0.0; }  // XXX : fallback
+  for (int i = start; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        if (entry->data.u32[0] == (unsigned) unichar)
+          { return (entry->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE); }
+    }
+  return 0.0;
+}
+
+static int
+ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t unichar, int stroke)
+{
+  CtxState *state = &ctx->state;
+  CtxIterator iterator;
+  float origin_x = state->x;
+  float origin_y = state->y;
+  ctx_current_point (ctx, &origin_x, &origin_y);
+  int in_glyph = 0;
+  float font_size = state->gstate.font_size;
+  int start = 0;
+  if (font->type == 0)
+  {
+  start = ctx_glyph_find_ctx (font, ctx, unichar);
+  if (start < 0)
+    { return -1; }  // XXX : fallback glyph
+  }
+  ctx_iterator_init (&iterator, drawlist, start, CTX_ITERATOR_EXPAND_BITPACK);
+  CtxCommand *command;
+
+  /* XXX :  do a binary search instead of a linear search */
+  while ( (command= ctx_iterator_next (&iterator) ) )
+    {
+      CtxEntry *entry = &command->entry;
+      if (in_glyph)
+        {
+          if (entry->code == CTX_DEFINE_GLYPH)
+            {
+              if (stroke)
+                { ctx_stroke (ctx); }
+              else
+                {
+#if CTX_RASTERIZER
+#if CTX_ENABLE_SHADOW_BLUR
+      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+      {
+        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
+        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+      }
+      else
+#endif
+#endif
+         ctx_fill (ctx); 
+               
+                }
+              ctx_restore (ctx);
+              return 0;
+            }
+          ctx_process (ctx, entry);
+        }
+      else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
+        {
+          in_glyph = 1;
+          ctx_save (ctx);
+          ctx_translate (ctx, origin_x, origin_y);
+          ctx_move_to (ctx, 0, 0);
+          ctx_begin_path (ctx);
+          ctx_scale (ctx, font_size / CTX_BAKE_FONT_SIZE,
+                     font_size / CTX_BAKE_FONT_SIZE);
+        }
+    }
+  if (stroke)
+    { ctx_stroke (ctx);
+    }
+  else
+    { 
+    
+#if CTX_RASTERIZER
+#if CTX_ENABLE_SHADOW_BLUR
+      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+      {
+        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
+        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+      }
+      else
+#endif
+#endif
+      {
+         ctx_fill (ctx); 
+      }
+    }
+  ctx_restore (ctx);
+  return -1;
+}
+
+static int
+ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
+{
+  CtxDrawlist drawlist = { (CtxEntry *) font->ctx.data,
+                           font->ctx.length,
+                           font->ctx.length, 0, 0
+                         };
+  return ctx_glyph_drawlist (font, ctx, &drawlist, unichar, stroke);
+}
+
+#if 0
+uint32_t ctx_glyph_no (Ctx *ctx, int no)
+{
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  if (no < 0 || no >= font->ctx.glyphs)
+    { return 0; }
+  return font->ctx.index[no*2];
+}
+#endif
+
+static void ctx_font_init_ctx (CtxFont *font)
+{
+  int glyph_count = 0;
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        { glyph_count ++; }
+    }
+  font->ctx.glyphs = glyph_count;
+#if CTX_DRAWLIST_STATIC
+  static uint32_t idx[512]; // one might have to adjust this for
+  // larger fonts XXX
+  // should probably be made a #define
+  font->ctx.index = &idx[0];
+#else
+  font->ctx.index = (uint32_t *) malloc (sizeof (uint32_t) * 2 * glyph_count);
+#endif
+  int no = 0;
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        {
+          font->ctx.index[no*2]   = entry->data.u32[0];
+          font->ctx.index[no*2+1] = i;
+          no++;
+        }
+    }
+}
+
+int
+ctx_load_font_ctx (const char *name, const void *data, int length);
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ctx_file (const char *name, const char *path);
+#endif
+
+static CtxFontEngine ctx_font_engine_ctx =
+{
+#if CTX_FONTS_FROM_FILE
+  ctx_load_font_ctx_file,
+#endif
+  ctx_load_font_ctx,
+  ctx_glyph_ctx,
+  ctx_glyph_width_ctx,
+  ctx_glyph_kern_ctx,
+};
+
+int
+ctx_load_font_ctx (const char *name, const void *data, int length)
+{
+  if (length % sizeof (CtxEntry) )
+    { return -1; }
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
+  ctx_fonts[ctx_font_count].type = 0;
+  ctx_fonts[ctx_font_count].name = name;
+  ctx_fonts[ctx_font_count].ctx.data = (CtxEntry *) data;
+  ctx_fonts[ctx_font_count].ctx.length = length / sizeof (CtxEntry);
+  ctx_font_init_ctx (&ctx_fonts[ctx_font_count]);
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx;
+  ctx_font_count++;
+  return ctx_font_count-1;
+}
+
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ctx_file (const char *name, const char *path)
+{
+  uint8_t *contents = NULL;
+  long length = 0;
+  ctx_get_contents (path, &contents, &length);
+  if (!contents)
+    {
+      ctx_log ( "File load failed\n");
+      return -1;
+    }
+  return ctx_load_font_ctx (name, contents, length);
+}
+#endif
+#endif
+
+#if CTX_FONT_ENGINE_CTX_FS
+
+static float
+ctx_glyph_kern_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+{
+#if 0
+  float font_size = ctx->state.gstate.font_size;
+  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
+  if (first_kern < 0) return 0.0;
+  for (int i = first_kern + 1; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_KERNING_PAIR)
+        {
+          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
+            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
+        }
+      if (entry->code == CTX_DEFINE_GLYPH)
+        return 0.0;
+    }
+#endif
+  return 0.0;
+}
+
+static float
+ctx_glyph_width_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  CtxState *state = &ctx->state;
+  char path[1024];
+  sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
+  uint8_t *data = NULL;
+  long int len_bytes = 0;
+  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);
+    for (int i = 0; i < glyph_ctx->drawlist.count; i++)
+    {
+      CtxEntry *e = &glyph_ctx->drawlist.entries[i];
+      if (e->code == CTX_DEFINE_GLYPH)
+        ret = e->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE;
+    }
+    free (data);
+    ctx_free (glyph_ctx);
+  }
+  return ret;
+}
+
+static int
+ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
+{
+  char path[1024];
+  sprintf (path, "file://%s/%010p", font->ctx_fs.path, unichar);
+  uint8_t *data = NULL;
+  long int len_bytes = 0;
+  ctx_get_contents (path, &data, &len_bytes);
+
+  if (data){
+    Ctx *glyph_ctx = ctx_new ();
+    ctx_parse (glyph_ctx, data);
+    int ret = ctx_glyph_drawlist (font, ctx, &(glyph_ctx->drawlist),
+                                  unichar, stroke);
+    free (data);
+    ctx_free (glyph_ctx);
+    return ret;
+  }
+  return -1;
+}
+
+int
+ctx_load_font_ctx_fs (const char *name, const void *data, int length);
+
+static CtxFontEngine ctx_font_engine_ctx_fs =
+{
+#if CTX_FONTS_FROM_FILE
+  NULL,
+#endif
+  ctx_load_font_ctx_fs,
+  ctx_glyph_ctx_fs,
+  ctx_glyph_width_ctx_fs,
+  ctx_glyph_kern_ctx_fs,
+};
+
+int
+ctx_load_font_ctx_fs (const char *name, const void *path, int length) // length is ignored
+{
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
+
+  ctx_fonts[ctx_font_count].type = 42;
+  ctx_fonts[ctx_font_count].name = name;
+  ctx_fonts[ctx_font_count].ctx_fs.path = strdup (path);
+  int path_len = strlen (path);
+  if (ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] == '/')
+   ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] = 0;
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx_fs;
+  ctx_font_count++;
+  return ctx_font_count-1;
+}
+
+#endif
+
+int
+_ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
+{
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  // a begin-path here did not remove stray spikes in terminal
+  return font->engine->glyph (font, ctx, unichar, stroke);
+}
+
+int
+ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
+{
+#if CTX_BACKEND_TEXT
+  CtxEntry commands[3]; // 3 to silence incorrect warning from static analysis
+  ctx_memset (commands, 0, sizeof (commands) );
+  commands[0] = ctx_u32 (CTX_GLYPH, unichar, 0);
+  commands[0].data.u8[4] = stroke;
+  ctx_process (ctx, commands);
+  return 0; // XXX is return value used?
+#else
+  return _ctx_glyph (ctx, unichar, stroke);
+#endif
+}
+
+float
+ctx_glyph_width (Ctx *ctx, int unichar)
+{
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+
+  return font->engine->glyph_width (font, ctx, unichar);
+}
+
+static float
+ctx_glyph_kern (Ctx *ctx, int unicharA, int unicharB)
+{
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  return font->engine->glyph_kern (font, ctx, unicharA, unicharB);
+}
+
+float
+ctx_text_width (Ctx        *ctx,
+                const char *string)
+{
+  float sum = 0.0;
+  if (!string)
+    return 0.0f;
+  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
+    {
+      sum += ctx_glyph_width (ctx, ctx_utf8_to_unichar (utf8) );
+    }
+  return sum;
+}
+
+static void
+_ctx_glyphs (Ctx     *ctx,
+             CtxGlyph *glyphs,
+             int       n_glyphs,
+             int       stroke)
+{
+  for (int i = 0; i < n_glyphs; i++)
+    {
+      {
+        uint32_t unichar = glyphs[i].index;
+        ctx_move_to (ctx, glyphs[i].x, glyphs[i].y);
+        ctx_glyph (ctx, unichar, stroke);
+      }
+    }
+}
+
+static void
+_ctx_text (Ctx        *ctx,
+           const char *string,
+           int         stroke,
+           int         visible)
+{
+  CtxState *state = &ctx->state;
+  float x = ctx->state.x;
+  switch ( (int) ctx_state_get (state, CTX_text_align) )
+    //switch (state->gstate.text_align)
+    {
+      case CTX_TEXT_ALIGN_START:
+      case CTX_TEXT_ALIGN_LEFT:
         break;
-      case CTX_RECTANGLE:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+      case CTX_TEXT_ALIGN_CENTER:
+        x -= ctx_text_width (ctx, string) /2;
         break;
-      case CTX_FILL_RECT:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        ctx_fill (ctx);
+      case CTX_TEXT_ALIGN_END:
+      case CTX_TEXT_ALIGN_RIGHT:
+        x -= ctx_text_width (ctx, string);
         break;
-      case CTX_STROKE_RECT:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        ctx_stroke (ctx);
+    }
+  float y = ctx->state.y;
+  float baseline_offset = 0.0f;
+  switch ( (int) ctx_state_get (state, CTX_text_baseline) )
+    {
+      case CTX_TEXT_BASELINE_HANGING:
+        /* XXX : crude */
+        baseline_offset = ctx->state.gstate.font_size  * 0.55;
         break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_round_rectangle (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+      case CTX_TEXT_BASELINE_TOP:
+        /* XXX : crude */
+        baseline_offset = ctx->state.gstate.font_size  * 0.7;
         break;
-      case CTX_VIEW_BOX:
-        ctx_view_box (ctx, arg(0), arg(1), arg(2), arg(3) );
+      case CTX_TEXT_BASELINE_BOTTOM:
+        baseline_offset = -ctx->state.gstate.font_size * 0.1;
         break;
-      case CTX_LINEAR_GRADIENT:
-        ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) );
+      case CTX_TEXT_BASELINE_ALPHABETIC:
+      case CTX_TEXT_BASELINE_IDEOGRAPHIC:
+        baseline_offset = 0.0f;
         break;
-      case CTX_RADIAL_GRADIENT:
-        ctx_radial_gradient (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+      case CTX_TEXT_BASELINE_MIDDLE:
+        baseline_offset = ctx->state.gstate.font_size * 0.25;
         break;
-      case CTX_GRADIENT_STOP:
+    }
+  float x0 = x;
+  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
+    {
+      if (*utf8 == '\n')
         {
-          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);
+          y += ctx->state.gstate.font_size * ctx_state_get (state, CTX_line_spacing);
+          x = x0;
+          if (visible)
+            { ctx_move_to (ctx, x, y); }
         }
-        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);
-        if (parser->translate_origin)
+      else
         {
-          ctx_translate (ctx,
-                         (parser->cursor_x-1) * parser->cell_width * 1.0,
-                         (parser->cursor_y-1) * parser->cell_height * 1.0);
+          uint32_t unichar = ctx_utf8_to_unichar (utf8);
+          if (visible)
+            {
+              ctx_move_to (ctx, x, y + baseline_offset);
+              _ctx_glyph (ctx, unichar, stroke);
+            }
+          const char *next_utf8 = ctx_utf8_skip (utf8, 1);
+          if (next_utf8)
+            {
+              x += ctx_glyph_width (ctx, unichar);
+              x += ctx_glyph_kern (ctx, unichar, ctx_utf8_to_unichar (next_utf8) );
+            }
+          if (visible)
+            { ctx_move_to (ctx, x, y); }
         }
-        break;
     }
-#undef arg
-//  parser->n_numbers = 0;
+  if (!visible)
+    { ctx_move_to (ctx, x, y); }
 }
 
-static void ctx_parser_holding_append (CtxParser *parser, int byte)
+
+CtxGlyph *
+ctx_glyph_allocate (int n_glyphs)
 {
-#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 = (uint8_t*)realloc (parser->holding, new_len);
-    parser->hold_len = new_len;
-  }
+  return (CtxGlyph *) malloc (sizeof (CtxGlyph) * n_glyphs);
+}
+void
+gtx_glyph_free     (CtxGlyph *glyphs)
+{
+  free (glyphs);
+}
+
+void
+ctx_glyphs (Ctx        *ctx,
+            CtxGlyph   *glyphs,
+            int         n_glyphs)
+{
+  _ctx_glyphs (ctx, glyphs, n_glyphs, 0);
+}
+
+void
+ctx_glyphs_stroke (Ctx        *ctx,
+                   CtxGlyph   *glyphs,
+                   int         n_glyphs)
+{
+  _ctx_glyphs (ctx, glyphs, n_glyphs, 1);
+}
+
+void
+ctx_text (Ctx        *ctx,
+          const char *string)
+{
+  if (!string)
+    return;
+#if CTX_BACKEND_TEXT
+  ctx_process_cmd_str (ctx, CTX_TEXT, string, 0, 0);
+  _ctx_text (ctx, string, 0, 0);
+#else
+  _ctx_text (ctx, string, 0, 1);
 #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; }
+
+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)
+{
+  if (!string)
+    return;
+#if CTX_BACKEND_TEXT
+  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
-  parser->holding[parser->pos]=0;
 }
 
-static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int arg_no, float *value)
+void
+ctx_stroke_text (Ctx *ctx, const char *string,
+               float x, float y)
 {
-  int big   = parser->width;
-  int small = parser->height;
-  if (big < small)
+  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 ++)
     {
-      small = parser->width;
-      big   = parser->height;
+      if (!ctx_strcmp (ctx_fonts[i].name, name) )
+        { return i; }
     }
-  switch (code)
+  for (int i = 0; i < ctx_font_count; i ++)
     {
-      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;
+      if (ctx_strstr (ctx_fonts[i].name, name) )
+        { return i; }
+    }
+  return -1;
+}
+
+int ctx_resolve_font (const char *name)
+{
+  int ret = _ctx_resolve_font (name);
+  if (ret >= 0)
+    { return ret; }
+  if (!ctx_strcmp (name, "regular") )
+    {
+      int ret = _ctx_resolve_font ("sans");
+      if (ret >= 0) { return ret; }
+      ret = _ctx_resolve_font ("serif");
+      if (ret >= 0) { return ret; }
+    }
+  return 0;
+}
+
+static void ctx_font_setup (void)
+{
+  static int initialized = 0;
+  if (initialized) { return; }
+  initialized = 1;
+#if CTX_FONT_ENGINE_CTX
+  ctx_font_count = 0; // oddly - this is needed in arduino
+
+#if CTX_FONT_ENGINE_CTX_FS
+  ctx_load_font_ctx_fs ("sans-ctx", "/tmp/ctx-regular", 0);
+#else
+#if CTX_FONT_ascii
+  ctx_load_font_ctx ("sans-ctx", ctx_font_ascii, sizeof (ctx_font_ascii) );
+#endif
+#if CTX_FONT_regular
+  ctx_load_font_ctx ("sans-ctx", ctx_font_regular, sizeof (ctx_font_regular) );
+#endif
+#endif
+
+#if CTX_FONT_mono
+  ctx_load_font_ctx ("mono-ctx", ctx_font_mono, sizeof (ctx_font_mono) );
+#endif
+#if CTX_FONT_bold
+  ctx_load_font_ctx ("bold-ctx", ctx_font_bold, sizeof (ctx_font_bold) );
+#endif
+#if CTX_FONT_italic
+  ctx_load_font_ctx ("italic-ctx", ctx_font_italic, sizeof (ctx_font_italic) );
+#endif
+#if CTX_FONT_sans
+  ctx_load_font_ctx ("sans-ctx", ctx_font_sans, sizeof (ctx_font_sans) );
+#endif
+#if CTX_FONT_serif
+  ctx_load_font_ctx ("serif-ctx", ctx_font_serif, sizeof (ctx_font_serif) );
+#endif
+#if CTX_FONT_symbol
+  ctx_load_font_ctx ("symbol-ctx", ctx_font_symbol, sizeof (ctx_font_symbol) );
+#endif
+#if CTX_FONT_emoji
+  ctx_load_font_ctx ("emoji-ctx", ctx_font_emoji, sizeof (ctx_font_emoji) );
+#endif
+#endif
+
+#if NOTO_EMOJI_REGULAR
+  ctx_load_font_ttf ("sans-NotoEmoji_Regular", ttf_NotoEmoji_Regular_ttf, ttf_NotoEmoji_Regular_ttf_len);
+#endif
+#if ROBOTO_LIGHT
+  ctx_load_font_ttf ("sans-light-Roboto_Light", ttf_Roboto_Light_ttf, ttf_Roboto_Light_ttf_len);
+#endif
+#if ROBOTO_REGULAR
+  ctx_load_font_ttf ("sans-Roboto_Regular", ttf_Roboto_Regular_ttf, ttf_Roboto_Regular_ttf_len);
+#endif
+#if ROBOTO_BOLD
+  ctx_load_font_ttf ("sans-bold-Roboto_Bold", ttf_Roboto_Bold_ttf, ttf_Roboto_Bold_ttf_len);
+#endif
+#if DEJAVU_SANS
+  ctx_load_font_ttf ("sans-DejaVuSans", ttf_DejaVuSans_ttf, ttf_DejaVuSans_ttf_len);
+#endif
+#if VERA
+  ctx_load_font_ttf ("sans-Vera", ttf_Vera_ttf, ttf_Vera_ttf_len);
+#endif
+#if UNSCII_16
+  ctx_load_font_ttf ("mono-unscii16", ttf_unscii_16_ttf, ttf_unscii_16_ttf_len);
+#endif
+#if XA000_MONO
+  ctx_load_font_ttf ("mono-0xA000", ttf_0xA000_Mono_ttf, ttf_0xA000_Mono_ttf_len);
+#endif
+#if DEJAVU_SANS_MONO
+  ctx_load_font_ttf ("mono-DejaVuSansMono", ttf_DejaVuSansMono_ttf, ttf_DejaVuSansMono_ttf_len);
+#endif
+#if NOTO_MONO_REGULAR
+  ctx_load_font_ttf ("mono-NotoMono_Regular", ttf_NotoMono_Regular_ttf, ttf_NotoMono_Regular_ttf_len);
+#endif
+}
+
+
+
+#if !__COSMOPOLITAN__
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+//#include "ctx.h"
+/* instead of including ctx.h we declare the few utf8
+ * functions we use
+ */
+uint32_t ctx_utf8_to_unichar (const char *input);
+int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
+int ctx_utf8_strlen (const char *s);
+
+static void ctx_string_init (CtxString *string, int initial_size)
+{
+  string->allocated_length = initial_size;
+  string->length = 0;
+  string->utf8_length = 0;
+  string->str = (char*)malloc (string->allocated_length + 1);
+  string->str[0]='\0';
+}
+
+static void ctx_string_destroy (CtxString *string)
+{
+  if (string->str)
+    {
+      free (string->str);
+      string->str = NULL;
+    }
+}
+
+void ctx_string_clear (CtxString *string)
+{
+  string->length = 0;
+  string->utf8_length = 0;
+  string->str[string->length]=0;
+}
+
+static inline void _ctx_string_append_byte (CtxString *string, char  val)
+{
+  if (CTX_LIKELY((val & 0xC0) != 0x80))
+    { string->utf8_length++; }
+  if (CTX_UNLIKELY(string->length + 2 >= string->allocated_length))
+    {
+      char *old = string->str;
+      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
+      string->str = (char*)realloc (old, string->allocated_length);
+    }
+  string->str[string->length++] = val;
+  string->str[string->length] = '\0';
+}
+
+void ctx_string_append_byte (CtxString *string, char  val)
+{
+  _ctx_string_append_byte (string, val);
+}
+
+void ctx_string_append_unichar (CtxString *string, unsigned int unichar)
+{
+  char *str;
+  char utf8[5];
+  utf8[ctx_unichar_to_utf8 (unichar, (unsigned char *) utf8)]=0;
+  str = utf8;
+  while (str && *str)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
+
+static inline void _ctx_string_append_str (CtxString *string, const char *str)
+{
+  if (!str) { return; }
+  while (*str)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
+
+void ctx_string_append_utf8char (CtxString *string, const char *str)
+{
+  if (!str) { return; }
+  int len = ctx_utf8_len (*str);
+  for (int i = 0; i < len && *str; i++)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
+
+void ctx_string_append_str (CtxString *string, const char *str)
+{
+  _ctx_string_append_str (string, str);
+}
+
+CtxString *ctx_string_new_with_size (const char *initial, int initial_size)
+{
+  CtxString *string = (CtxString*)ctx_calloc (sizeof (CtxString), 1);
+  ctx_string_init (string, initial_size);
+  if (initial)
+    { _ctx_string_append_str (string, initial); }
+  return string;
+}
+
+CtxString *ctx_string_new (const char *initial)
+{
+  return ctx_string_new_with_size (initial, 8);
+}
+
+void ctx_string_append_data (CtxString *string, const char *str, int len)
+{
+  int i;
+  for (i = 0; i<len; i++)
+    { _ctx_string_append_byte (string, str[i]); }
+}
+
+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_utf8length (CtxString *string)
+{
+  return string->utf8_length;
+}
+
+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);
 }
 
-static void ctx_parser_transform_percent_height (CtxParser *parser, CtxCode code, int arg_no, float *value)
+char       *ctx_string_dissolve       (CtxString *string)
 {
-  *value *= (parser->height/100.0);
+  char *ret = string->str;
+  ctx_string_free (string, 0);
+  return ret;
 }
 
-static void ctx_parser_transform_percent_width (CtxParser *parser, CtxCode code, int arg_no, float *value)
+void
+ctx_string_set (CtxString *string, const char *new_string)
 {
-  *value *= (parser->height/100.0);
+  ctx_string_clear (string);
+  _ctx_string_append_str (string, new_string);
 }
 
-static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_no, float *value)
+static char *ctx_strdup (const char *str)
 {
-  float small = parser->cell_width;
-  if (small > parser->cell_height)
-    { small = parser->cell_height; }
-  switch (code)
+  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)
+{
+#if 1
+  int old_len = string->utf8_length;
+#else
+  int old_len = ctx_utf8_strlen (string->str);// string->utf8_length;
+#endif
+  if (CTX_LIKELY(pos == old_len))
     {
-      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;
+      _ctx_string_append_str (string, new_glyph);
+      return;
     }
-}
 
-// %h %v %m %M
+  char tmpg[3]=" ";
+  int new_len = ctx_utf8_len (*new_glyph);
+  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);
+}
 
-static void ctx_parser_number_done (CtxParser *parser)
+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);
 }
 
-static void ctx_parser_word_done (CtxParser *parser)
+uint32_t ctx_string_get_unichar (CtxString *string, int pos)
 {
-  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;
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  if (!p)
+    { return 0; }
+  return ctx_utf8_to_unichar (p);
+}
 
-      // trigger transition from number
-      parser->state = CTX_PARSER_NUMBER;
-      ctx_parser_feed_byte (parser, ',');
+
+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;
     }
-  else if (command > 0)
+  if (new_len <= 1 && new_glyph[0] < 32)
     {
-#if 0
-      if (old_args == CTX_ARG_COLLECT_NUMBERS ||
-          old_args == CTX_ARG_STRING_OR_NUMBER)
+      tmpg[0]=new_glyph[0]+64;
+      new_glyph = tmpg;
+    }
+  {
+    for (int i = old_len; i <= pos; i++)
       {
-        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;
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
       }
-#endif
-
-      parser->command = (CtxCode) command;
-      parser->n_numbers = 0;
-      parser->n_args = 0;
-      if (parser->expected_args == 0)
-        {
-          ctx_parser_dispatch_command (parser);
-        }
+  }
+  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
     {
-      /* 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]);
-            }
-        }
+      rest = ctx_strdup (p);
     }
+  memcpy (p, new_glyph, new_len);
+  memcpy (p + new_len, rest, strlen (rest) + 1);
+  free (rest);
+  string->length = strlen (string->str);
+  string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-static void ctx_parser_string_done (CtxParser *parser)
+void ctx_string_insert_unichar (CtxString *string, int pos, uint32_t unichar)
 {
-  if (parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
+  uint8_t utf8[5]="";
+  utf8[ctx_unichar_to_utf8(unichar, utf8)]=0;
+  ctx_string_insert_utf8 (string, pos, (char*)utf8);
+}
+
+void ctx_string_remove (CtxString *string, int pos)
+{
+  int old_len = string->utf8_length;
   {
-          /*
-    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
-        )
-        */
+    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 || *p == 0)
     {
-    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;
+      return;
+      rest = ctx_strdup ("");
+      prev_len = 0;
     }
-  }
-  else
+  else if (* (p+prev_len) == 0)
   {
-    ctx_parser_dispatch_command (parser);
+      rest = ctx_strdup ("");
   }
+  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);
 }
 
-void ctx_parser_feed_byte (CtxParser *parser, int byte)
+char *ctx_strdup_printf (const char *format, ...)
 {
-  switch (byte)
+  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;
+}
+
+void ctx_string_append_printf (CtxString *string, const char *format, ...)
+{
+  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);
+}
+
+#if CTX_CAIRO
+
+typedef struct _CtxCairo CtxCairo;
+struct
+  _CtxCairo
+{
+  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)
     {
-      case '\n':
-        parser->col=0;
-        parser->line++;
+      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;
-      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;
-              break;
-            case '\'':
-              parser->state = CTX_PARSER_STRING_APOS;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '~':
-              parser->state = CTX_PARSER_STRING_A85;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '"':
-              parser->state = CTX_PARSER_STRING_QUOT;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '-':
-              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 0;
-              break;
-            case '0': case '1': case '2': case '3': case '4':
-            case '5': case '6': case '7': case '8': case '9':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->numbers[parser->n_numbers] += (byte - '0');
-              parser->decimal = 0;
-              break;
-            case '.':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 1;
-              break;
-            default:
-              parser->state = CTX_PARSER_WORD;
-              parser->pos = 0;
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
+      case CTX_CLIP:
+        if (ctx_cairo->preserve)
+        {
+          cairo_clip_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_clip (cr);
+        }
         break;
-      case CTX_PARSER_NUMBER:
-      case CTX_PARSER_NEGATIVE_NUMBER:
+        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:
         {
-          switch (byte)
+          int cairo_val = CAIRO_LINE_CAP_SQUARE;
+          switch (ctx_arg_u8 (0) )
             {
-              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 ' ':
-              case '\t':
-              case '\r':
-              case '\n':
-              case ';':
-              case ',':
-              case '(':
-              case ')':
-              case '{':
-              case '}':
-              case '=':
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case '#':
-                parser->state = CTX_PARSER_COMMENT;
-                break;
-              case '-':
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-                parser->numbers[parser->n_numbers+1] = 0;
-                parser->n_numbers ++;
-                parser->decimal = 0;
+              case CTX_CAP_ROUND:
+                cairo_val = CAIRO_LINE_CAP_ROUND;
                 break;
-              case '.':
-                //if (parser->decimal) // TODO permit .13.32.43 to equivalent to .12 .32 .43
-                parser->decimal = 1;
+              case CTX_CAP_SQUARE:
+                cairo_val = CAIRO_LINE_CAP_SQUARE;
                 break;
-              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;
-                    parser->numbers[parser->n_numbers] += (byte - '0') / (1.0 * parser->decimal);
-                  }
-                else
-                  {
-                    parser->numbers[parser->n_numbers] *= 10;
-                    parser->numbers[parser->n_numbers] += (byte - '0');
-                  }
+              case CTX_CAP_NONE:
+                cairo_val = CAIRO_LINE_CAP_BUTT;
                 break;
-              case '@': // cells
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_cell (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
+            }
+          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 '%': // percent of width/height
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
+              case CTX_COMPOSITE_COPY:
+                cairo_val = CAIRO_OPERATOR_SOURCE;
                 break;
-              case '^': // percent of height
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent_height (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
+            }
+          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 '~': // percent of width
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent_width (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
+              case CTX_JOIN_BEVEL:
+                cairo_val = CAIRO_LINE_JOIN_BEVEL;
                 break;
-              default:
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_WORD;
-                parser->pos = 0;
-                ctx_parser_holding_append (parser, byte);
+              case CTX_JOIN_MITER:
+                cairo_val = CAIRO_LINE_JOIN_MITER;
                 break;
             }
-          if ( (parser->state != CTX_PARSER_NUMBER) &&
-               (parser->state != CTX_PARSER_NEGATIVE_NUMBER))
+          cairo_set_line_join (cr, cairo_val);
+        }
+        break;
+      case CTX_LINEAR_GRADIENT:
+        {
+          if (ctx_cairo->pat)
             {
-              parser->n_numbers ++;
-              ctx_parser_number_done (parser);
-
-              if (parser->n_numbers == parser->expected_args ||
-                  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;
-                  CtxCode tmp3 = parser->command;
-                  int tmp4 = parser->expected_args;
-                  ctx_parser_dispatch_command (parser);
-                  parser->command = tmp3;
-                  switch (parser->command)
-                  {
-                    case CTX_DEFINE_TEXTURE:
-                    case CTX_TEXTURE:
-                      parser->n_numbers = tmp1;
-                      parser->n_args = tmp2;
-                      break;
-                          default:
-                      parser->n_numbers = 0;
-                      parser->n_args = 0;
-                      break;
-                  }
-                  parser->expected_args = tmp4;
-                }
-              if (parser->n_numbers > CTX_PARSER_MAX_ARGS)
-                { parser->n_numbers = CTX_PARSER_MAX_ARGS;
-                }
+              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_PARSER_WORD:
-        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 ' ': case '\t': case '\r': case '\n':
-            case ';': case ',':
-            case '(': case ')': case '=': case '{': case '}':
-              parser->state = CTX_PARSER_NEUTRAL;
-              break;
-            case '#':
-              parser->state = CTX_PARSER_COMMENT;
-              break;
-            case '-':
-              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 0;
-              break;
-            case '0': case '1': case '2': case '3': case '4':
-            case '5': case '6': case '7': case '8': case '9':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->numbers[parser->n_numbers] += (byte - '0');
-              parser->decimal = 0;
-              break;
-            case '.':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 1;
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
-        if (parser->state != CTX_PARSER_WORD)
-          {
-            ctx_parser_word_done (parser);
-          }
-        break;
-      case CTX_PARSER_STRING_A85:
-        switch (byte)
-          {
-            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:
-              ctx_parser_holding_append (parser, byte);
-              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_PARSER_STRING_APOS:
-        switch (byte)
-          {
-            case '\\': parser->state = CTX_PARSER_STRING_APOS_ESCAPED; break;
-            case '\'': parser->state = CTX_PARSER_NEUTRAL;
-              ctx_parser_string_done (parser);
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte); 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;
-      case CTX_PARSER_STRING_APOS_ESCAPED:
-        switch (byte)
-          {
-            case '0': byte = '\0'; break;
-            case 'b': byte = '\b'; break;
-            case 'f': byte = '\f'; break;
-            case 'n': byte = '\n'; break;
-            case 'r': byte = '\r'; break;
-            case 't': byte = '\t'; break;
-            case 'v': byte = '\v'; break;
-            default: break;
-          }
-        ctx_parser_holding_append (parser, byte);
-        parser->state = CTX_PARSER_STRING_APOS;
+        // XXX  implement TEXTURE
+#if 0
+      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;
-      case CTX_PARSER_STRING_QUOT_ESCAPED:
-        switch (byte)
-          {
-            case '0': byte = '\0'; break;
-            case 'b': byte = '\b'; break;
-            case 'f': byte = '\f'; break;
-            case 'n': byte = '\n'; break;
-            case 'r': byte = '\r'; break;
-            case 't': byte = '\t'; break;
-            case 'v': byte = '\v'; break;
-            default: break;
-          }
-        ctx_parser_holding_append (parser, byte);
-        parser->state = CTX_PARSER_STRING_QUOT;
+#endif
+      case CTX_TEXT:
+        /* XXX: implement some linebreaking/wrap, positioning
+         *      behavior here
+         */
+        cairo_show_text (cr, ctx_arg_string () );
         break;
-      case CTX_PARSER_STRING_QUOT:
-        switch (byte)
-          {
-            case '\\':
-              parser->state = CTX_PARSER_STRING_QUOT_ESCAPED;
-              break;
-            case '"':
-              parser->state = CTX_PARSER_NEUTRAL;
-              ctx_parser_string_done (parser);
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
+      case CTX_CONT:
+      case CTX_EDGE:
+      case CTX_DATA:
+      case CTX_DATA_REV:
+      case CTX_FLUSH:
         break;
-      case CTX_PARSER_COMMENT:
-        switch (byte)
+    }
+  ctx_process (ctx_cairo->ctx, entry);
+}
+
+void ctx_cairo_free (CtxCairo *ctx_cairo)
+{
+  if (ctx_cairo->pat)
+    { cairo_pattern_destroy (ctx_cairo->pat); }
+  if (ctx_cairo->image)
+    { cairo_surface_destroy (ctx_cairo->image); }
+  free (ctx_cairo);
+}
+
+void
+ctx_render_cairo (Ctx *ctx, cairo_t *cr)
+{
+  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); }
+}
+
+Ctx *
+ctx_new_for_cairo (cairo_t *cr)
+{
+  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;
+}
+
+#endif
+
+#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])
           {
-            case '\r':
-            case '\n':
-              parser->state = CTX_PARSER_NEUTRAL;
-            default:
-              break;
+            best_length = longest_common_suffix[i%2][j];
+            if (offsetY) *offsetY = j - best_length;
+            if (offsetX) *offsetX = i - best_length;
           }
-        break;
+      }
     }
+  }
+  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)
+{
+  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)
 
-void ctx_parse (Ctx *ctx, const char *string)
+/* 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)
 {
-  if (!string)
-    return;
-  CtxParser *parser = ctx_parser_new (ctx, ctx_width(ctx),
-                                           ctx_height(ctx),
-                                           ctx_get_font_size(ctx),
-                                           ctx_get_font_size(ctx),
-                                           0, 0, NULL, NULL, NULL, NULL, NULL);
-  for (int i = 0; string[i]; i++)
-     ctx_parser_feed_byte (parser, string[i]);
-  ctx_parser_free (parser);
-}
+  CtxString *string = ctx_string_new ("");
+  CtxList *encoded_list = NULL;
 
-#endif
+  /* TODO : make expected position offset in prev slide based on
+   * matches and not be constant */
 
-static CtxFont ctx_fonts[CTX_MAX_FONTS];
-static int     ctx_font_count = 0;
+  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);
+  }
 
-#if CTX_FONT_ENGINE_STB
-static float
-ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar);
-static float
-ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB);
-static int
-ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke);
+  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;
 
-CtxFontEngine ctx_font_engine_stb =
-{
-#if CTX_FONTS_FROM_FILE
-  ctx_load_font_ttf_file,
+         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
+         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
-  ctx_load_font_ttf,
-  ctx_glyph_stb,
-  ctx_glyph_width_stb,
-  ctx_glyph_kern_stb,
-};
 
-int
-ctx_load_font_ttf (const char *name, const void *ttf_contents, int length)
-{
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
-  ctx_fonts[ctx_font_count].type = 1;
-  ctx_fonts[ctx_font_count].name = (char *) malloc (strlen (name) + 1);
-  ctx_strcpy ( (char *) ctx_fonts[ctx_font_count].name, name);
-  if (!stbtt_InitFont (&ctx_fonts[ctx_font_count].stb.ttf_info, ttf_contents, 0) )
-    {
-      ctx_log ( "Font init failed\n");
-      return -1;
+         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 (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);
+            }
+
+            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;
+              }
+            }
+         }
+      }
     }
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_stb;
-  ctx_font_count ++;
-  return ctx_font_count-1;
-}
 
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ttf_file (const char *name, const char *path)
-{
-  uint8_t *contents = NULL;
-  long length = 0;
-  ctx_get_contents (path, &contents, &length);
-  if (!contents)
+    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
+      break;
+  }
+
+  /* merge adjecant prev span references  */
+  {
+    for (CtxList *l = encoded_list; l; l = l->next)
     {
-      ctx_log ( "File load failed\n");
-      return -1;
+      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;
+        }
+      }
     }
-  return ctx_load_font_ttf (name, contents, length);
-}
-#endif
+  }
 
-static int
-ctx_glyph_stb_find (CtxFont *font, uint32_t unichar)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  int index = font->stb.cache_index;
-  if (font->stb.cache_unichar == unichar)
+  while (encoded_list)
+  {
+    CtxSpan *span = encoded_list->data;
+    if (span->from_prev)
     {
-      return index;
+      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));
     }
-  font->stb.cache_unichar = 0;
-  index = font->stb.cache_index = stbtt_FindGlyphIndex (ttf_info, unichar);
-  font->stb.cache_unichar = unichar;
-  return index;
-}
+    else
+    {
+      for (int i = span->start; i< span->start+span->length; i++)
+      {
+        if (src[i] == CTX_CODEC_CHAR)
+        {
+          char bytes[2]={CTX_CODEC_CHAR, CTX_CODEC_CHAR};
+          ctx_string_append_data (string, bytes, 2);
+        }
+        else
+        {
+          ctx_string_append_data (string, &src[i], 1);
+        }
+      }
+    }
+    free (span);
+    ctx_list_remove (&encoded_list, span);
+  }
 
-static float
-ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  float font_size          = ctx->state.gstate.font_size;
-  float scale              = stbtt_ScaleForPixelHeight (ttf_info, font_size);
-  int advance, lsb;
-  int glyph = ctx_glyph_stb_find (font, unichar);
-  if (glyph==0)
-    { return 0.0f; }
-  stbtt_GetGlyphHMetrics (ttf_info, glyph, &advance, &lsb);
-  return (advance * scale);
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
 }
 
-static float
-ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+#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)
 {
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  float font_size = ctx->state.gstate.font_size;
-  float scale = stbtt_ScaleForPixelHeight (ttf_info, font_size);
-  int glyphA = ctx_glyph_stb_find (font, unicharA);
-  int glyphB = ctx_glyph_stb_find (font, unicharB);
-  return stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale;
-}
+  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);
 
-static int
-ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  int glyph = ctx_glyph_stb_find (font, unichar);
-  if (glyph==0)
-    { return -1; }
-  float font_size = ctx->state.gstate.font_size;
-  int   baseline = ctx->state.y;
-  float origin_x = ctx->state.x;
-  float origin_y = baseline;
-  float scale    = stbtt_ScaleForPixelHeight (ttf_info, font_size);;
-  stbtt_vertex *vertices = NULL;
-  ctx_begin_path (ctx);
-  int num_verts = stbtt_GetGlyphShape (ttf_info, glyph, &vertices);
-  for (int i = 0; i < num_verts; i++)
+        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
     {
-      stbtt_vertex *vertex = &vertices[i];
-      switch (vertex->type)
+      if (in_ref)
+      {
+        if (ref_len < 16)
         {
-          case STBTT_vmove:
-            ctx_move_to (ctx,
-                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
-            break;
-          case STBTT_vline:
-            ctx_line_to (ctx,
-                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
-            break;
-          case STBTT_vcubic:
-            ctx_curve_to (ctx,
-                          origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
-                          origin_x + vertex->cx1 * scale, origin_y - vertex->cy1 * scale,
-                          origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
-            break;
-          case STBTT_vcurve:
-            ctx_quad_to (ctx,
-                         origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
-                         origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
-            break;
+          reference[ref_len++] = encoded[i];
+          reference[ref_len] = 0;
         }
+      }
+      else
+      ctx_string_append_data (string, &encoded[i], 1);
     }
-  stbtt_FreeShape (ttf_info, vertices);
-  if (stroke)
-    {
-      ctx_stroke (ctx);
-    }
-  else
-    { ctx_fill (ctx); }
-  return 0;
+  }
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
 }
 #endif
 
-#if CTX_FONT_ENGINE_CTX
+#define CTX_START_STRING "U\n"  // or " reset "
+#define CTX_END_STRING   "\nX"  // or "\ndone"
+#define CTX_END_STRING2  "\n\e"
 
-/* XXX: todo remove this, and rely on a binary search instead
- */
-static int ctx_font_find_glyph_cached (CtxFont *font, uint32_t glyph)
-{
-  for (int i = 0; i < font->ctx.glyphs; i++)
-    {
-      if (font->ctx.index[i * 2] == glyph)
-        { return font->ctx.index[i * 2 + 1]; }
-    }
-  return -1;
-}
+int ctx_frame_ack = -1;
+static char *prev_frame_contents = NULL;
+static int   prev_frame_len = 0;
 
-static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+static void ctx_ctx_flush (CtxCtx *ctxctx)
 {
-  int ret = ctx_font_find_glyph_cached (font, unichar);
-  if (ret >= 0) return ret;
+#if 0
+  FILE *debug = fopen ("/tmp/ctx-debug", "a");
+  fprintf (debug, "------\n");
+#endif
 
-  for (int i = 0; i < font->ctx.length; i++)
+  if (ctx_native_events)
+    fprintf (stdout, "\e[?201h");
+  fprintf (stdout, "\e[H\e[?25l\e[?200h");
+#if 1
+  fprintf (stdout, CTX_START_STRING);
+  ctx_render_stream (ctxctx->ctx, stdout, 0);
+  fprintf (stdout, CTX_END_STRING);
+#else
   {
-    CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-    if (entry->code == CTX_DEFINE_GLYPH &&
-        entry->data.u32[0] == unichar)
-    {
-       return i;
-       // XXX this could be prone to insertion of valid header
-       // data in included bitmaps.. is that an issue?
-       //   
-    }
-  }
-  return -1;
-}
+    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);
 
-static float
-ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
-{
-  float font_size = ctx->state.gstate.font_size;
-  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
-  if (first_kern < 0) return 0.0;
-  for (int i = first_kern + 1; i < font->ctx.length; i++)
+    if (prev_frame_contents && 0)  // XXX : 
     {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_KERNING_PAIR)
-        {
-          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
-            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
-        }
-      if (entry->code == CTX_DEFINE_GLYPH)
-        return 0.0;
-    }
-  return 0.0;
-}
+      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
-static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar)
-{
-  for (int i = 0; i < font->ctx.length; i++)
+      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
     {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
-        { return i; }
+      fwrite (cur_frame_contents, cur_frame_len, 1, stdout);
     }
-  return 0;
-}
+
+    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);
+}
 
-static float
-ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+void ctx_ctx_free (CtxCtx *ctx)
 {
-  CtxState *state = &ctx->state;
-  float font_size = state->gstate.font_size;
-  int   start     = ctx_glyph_find_ctx (font, ctx, unichar);
-  if (start < 0)
-    { return 0.0; }  // XXX : fallback
-  for (int i = start; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
-        if (entry->data.u32[0] == (unsigned) unichar)
-          { return (entry->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE); }
-    }
-  return 0.0;
+  nc_at_exit ();
+  free (ctx);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-static int
-ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t unichar, int stroke)
+Ctx *ctx_new_ctx (int width, int height)
 {
-  CtxState *state = &ctx->state;
-  CtxIterator iterator;
-  float origin_x = state->x;
-  float origin_y = state->y;
-  ctx_current_point (ctx, &origin_x, &origin_y);
-  int in_glyph = 0;
-  float font_size = state->gstate.font_size;
-  int start = 0;
-  if (font->type == 0)
+  float font_size = 12.0;
+  Ctx *ctx = ctx_new ();
+  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
+  fprintf (stdout, "\e[?1049h");
+  fflush (stdout);
+  //fprintf (stderr, "\e[H");
+  //fprintf (stderr, "\e[2J");
+  ctx_native_events = 1;
+  if (width <= 0 || height <= 0)
   {
-  start = ctx_glyph_find_ctx (font, ctx, unichar);
-  if (start < 0)
-    { return -1; }  // XXX : fallback glyph
+    ctxctx->cols = ctx_terminal_cols ();
+    ctxctx->rows = ctx_terminal_rows ();
+    width  = ctxctx->width  = ctx_terminal_width ();
+    height = ctxctx->height = ctx_terminal_height ();
+    font_size = height / ctxctx->rows;
+    ctx_font_size (ctx, font_size);
   }
-  ctx_iterator_init (&iterator, drawlist, start, CTX_ITERATOR_EXPAND_BITPACK);
-  CtxCommand *command;
+  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;
+}
 
-  /* XXX :  do a binary search instead of a linear search */
-  while ( (command= ctx_iterator_next (&iterator) ) )
+void ctx_ctx_pcm (Ctx *ctx);
+
+int ctx_ctx_consume_events (Ctx *ctx)
+{
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
+#if CTX_AUDIO
+  ctx_ctx_pcm (ctx);
+#endif
+  if (ctx_native_events)
     {
-      CtxEntry *entry = &command->entry;
-      if (in_glyph)
-        {
-          if (entry->code == CTX_DEFINE_GLYPH)
-            {
-              if (stroke)
-                { ctx_stroke (ctx); }
-              else
-                {
-#if CTX_RASTERIZER
-#if CTX_ENABLE_SHADOW_BLUR
-      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+      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)
       {
-        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
-        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+      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 (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"))
+      {
+        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
-#endif
-#endif
-         ctx_fill (ctx); 
-               
-                }
-              ctx_restore (ctx);
-              return 0;
-            }
-          ctx_process (ctx, entry);
-        }
-      else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
-        {
-          in_glyph = 1;
-          ctx_save (ctx);
-          ctx_translate (ctx, origin_x, origin_y);
-          ctx_move_to (ctx, 0, 0);
-          ctx_begin_path (ctx);
-          ctx_scale (ctx, font_size / CTX_BAKE_FONT_SIZE,
-                     font_size / CTX_BAKE_FONT_SIZE);
-        }
-    }
-  if (stroke)
-    { ctx_stroke (ctx);
+      {
+        ctx_key_press (ctx, 0, event, 0);
+      }
+      }
     }
   else
-    { 
-    
-#if CTX_RASTERIZER
-#if CTX_ENABLE_SHADOW_BLUR
-      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+    {
+      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;
+
+      if (!strcmp (event, "mouse-press"))
       {
-        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
-        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+        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);
+
+        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
-#endif
-#endif
       {
-         ctx_fill (ctx); 
+        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);
       }
     }
-  ctx_restore (ctx);
-  return -1;
-}
 
-static int
-ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  CtxDrawlist drawlist = { (CtxEntry *) font->ctx.data,
-                           font->ctx.length,
-                           font->ctx.length, 0, 0
-                         };
-  return ctx_glyph_drawlist (font, ctx, &drawlist, unichar, stroke);
+  return 1;
 }
 
-uint32_t ctx_glyph_no (Ctx *ctx, int no)
+int ctx_renderer_is_ctx (Ctx *ctx)
 {
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  if (no < 0 || no >= font->ctx.glyphs)
-    { return 0; }
-  return font->ctx.index[no*2];
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_ctx_free)
+          return 1;
+  return 0;
 }
 
-static void ctx_font_init_ctx (CtxFont *font)
-{
-  int glyph_count = 0;
-  for (int i = 0; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
-        { glyph_count ++; }
-    }
-  font->ctx.glyphs = glyph_count;
-#if CTX_DRAWLIST_STATIC
-  static uint32_t idx[512]; // one might have to adjust this for
-  // larger fonts XXX
-  // should probably be made a #define
-  font->ctx.index = &idx[0];
-#else
-  font->ctx.index = (uint32_t *) malloc (sizeof (uint32_t) * 2 * glyph_count);
 #endif
-  int no = 0;
-  for (int i = 0; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
-        {
-          font->ctx.index[no*2]   = entry->data.u32[0];
-          font->ctx.index[no*2+1] = i;
-          no++;
-        }
-    }
+
+#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;
 }
 
-int
-ctx_load_font_ctx (const char *name, const void *data, int length);
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ctx_file (const char *name, const char *path);
-#endif
+int _ctx_damage_control = 0;
 
-static CtxFontEngine ctx_font_engine_ctx =
+void ctx_tiled_free (CtxTiled *tiled)
 {
-#if CTX_FONTS_FROM_FILE
-  ctx_load_font_ctx_file,
-#endif
-  ctx_load_font_ctx,
-  ctx_glyph_ctx,
-  ctx_glyph_width_ctx,
-  ctx_glyph_kern_ctx,
-};
+  tiled->quit = 1;
+  mtx_lock (&tiled->mtx);
+  cnd_broadcast (&tiled->cond);
+  mtx_unlock (&tiled->mtx);
 
-int
-ctx_load_font_ctx (const char *name, const void *data, int length)
-{
-  if (length % sizeof (CtxEntry) )
-    { return -1; }
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
-  ctx_fonts[ctx_font_count].type = 0;
-  ctx_fonts[ctx_font_count].name = name;
-  ctx_fonts[ctx_font_count].ctx.data = (CtxEntry *) data;
-  ctx_fonts[ctx_font_count].ctx.length = length / sizeof (CtxEntry);
-  ctx_font_init_ctx (&ctx_fonts[ctx_font_count]);
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx;
-  ctx_font_count++;
-  return ctx_font_count-1;
+  while (tiled->thread_quit < _ctx_max_threads)
+    usleep (1000);
+
+  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;
+  }
+
+  ctx_free (tiled->ctx_copy);
+  }
+  // leak?
 }
+static unsigned char *sdl_icc = NULL;
+static long sdl_icc_length = 0;
 
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ctx_file (const char *name, const char *path)
+inline static void ctx_tiled_flush (CtxTiled *tiled)
 {
-  uint8_t *contents = NULL;
-  long length = 0;
-  ctx_get_contents (path, &contents, &length);
-  if (!contents)
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    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)
     {
-      ctx_log ( "File load failed\n");
-      return -1;
-    }
-  return ctx_load_font_ctx (name, contents, length);
-}
-#endif
-#endif
-
-#if CTX_FONT_ENGINE_CTX_FS
+      Ctx *hasher = ctx_hasher_new (tiled->width, tiled->height,
+                        CTX_HASH_COLS, CTX_HASH_ROWS);
+      ctx_render_ctx (tiled->ctx_copy, hasher);
 
-static float
-ctx_glyph_kern_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
-{
-#if 0
-  float font_size = ctx->state.gstate.font_size;
-  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
-  if (first_kern < 0) return 0.0;
-  for (int i = first_kern + 1; i < font->ctx.length; i++)
+      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
     {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_KERNING_PAIR)
+    for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
         {
-          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
-            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
+          tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
+          dirty_tiles++;
         }
-      if (entry->code == CTX_DEFINE_GLYPH)
-        return 0.0;
     }
-#endif
-  return 0.0;
-}
+    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 (tiled->tile_affinity[row * CTX_HASH_COLS + col] != -1)
+        {
+          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;
+        }
+      }
 
-static float
-ctx_glyph_width_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar)
-{
-  CtxState *state = &ctx->state;
-  char path[1024];
-  sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
-  uint8_t *data = NULL;
-  long int len_bytes = 0;
-  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);
-    for (int i = 0; i < glyph_ctx->drawlist.count; i++)
+    if (_ctx_damage_control)
     {
-      CtxEntry *e = &glyph_ctx->drawlist.entries[i];
-      if (e->code == CTX_DEFINE_GLYPH)
-        ret = e->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE;
+      for (int i = 0; i < tiled->width * tiled->height; i++)
+      {
+        tiled->pixels[i*4+2]  = (tiled->pixels[i*4+2] + 255)/2;
+      }
     }
-    free (data);
-    ctx_free (glyph_ctx);
-  }
-  return ret;
-}
-
-static int
-ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  char path[1024];
-  sprintf (path, "file://%s/%010p", font->ctx_fs.path, unichar);
-  uint8_t *data = NULL;
-  long int len_bytes = 0;
-  ctx_get_contents (path, &data, &len_bytes);
-
-  if (data){
-    Ctx *glyph_ctx = ctx_new ();
-    ctx_parse (glyph_ctx, data);
-    int ret = ctx_glyph_drawlist (font, ctx, &(glyph_ctx->drawlist),
-                                  unichar, stroke);
-    free (data);
-    ctx_free (glyph_ctx);
-    return ret;
-  }
-  return -1;
-}
-
-int
-ctx_load_font_ctx_fs (const char *name, const void *data, int length);
-
-static CtxFontEngine ctx_font_engine_ctx_fs =
-{
-#if CTX_FONTS_FROM_FILE
-  NULL,
-#endif
-  ctx_load_font_ctx_fs,
-  ctx_glyph_ctx_fs,
-  ctx_glyph_width_ctx_fs,
-  ctx_glyph_kern_ctx_fs,
-};
-
-int
-ctx_load_font_ctx_fs (const char *name, const void *path, int length) // length is ignored
-{
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
 
-  ctx_fonts[ctx_font_count].type = 42;
-  ctx_fonts[ctx_font_count].name = name;
-  ctx_fonts[ctx_font_count].ctx_fs.path = strdup (path);
-  int path_len = strlen (path);
-  if (ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] == '/')
-   ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] = 0;
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx_fs;
-  ctx_font_count++;
-  return ctx_font_count-1;
-}
+    tiled->render_frame = ++tiled->frame;
 
-#endif
+#if 0
 
-int
-_ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
-{
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  // a begin-path here did not remove stray spikes in terminal
-  return font->engine->glyph (font, ctx, unichar, stroke);
-}
+          //if (tiled->tile_affinity[hno]==no)
+          {
+            int x0 = ((tiled->width)/CTX_HASH_COLS) * 0;
+            int y0 = ((tiled->height)/CTX_HASH_ROWS) * 0;
+            int width = tiled->width / CTX_HASH_COLS;
+            int height = tiled->height / CTX_HASH_ROWS;
+            Ctx *host = tiled->host[0];
 
-int
-ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
-{
-#if CTX_BACKEND_TEXT
-  CtxEntry commands[3]; // 3 to silence incorrect warning from static analysis
-  ctx_memset (commands, 0, sizeof (commands) );
-  commands[0] = ctx_u32 (CTX_GLYPH, unichar, 0);
-  commands[0].data.u8[4] = stroke;
-  ctx_process (ctx, commands);
-  return 0; // XXX is return value used?
-#else
-  return _ctx_glyph (ctx, unichar, stroke);
+            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
+            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, 1, 1,
+                                 tiled->width*4, CTX_FORMAT_BGRA8,
+                                 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);
+          }
 #endif
-}
 
-float
-ctx_glyph_width (Ctx *ctx, int unichar)
-{
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
 
-  return font->engine->glyph_width (font, ctx, unichar);
+    mtx_lock (&tiled->mtx);
+    cnd_broadcast (&tiled->cond);
+    mtx_unlock (&tiled->mtx);
+  }
 }
 
-static float
-ctx_glyph_kern (Ctx *ctx, int unicharA, int unicharB)
+static
+void ctx_tiled_render_fun (void **data)
 {
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  return font->engine->glyph_kern (font, ctx, unicharA, unicharB);
-}
+  int      no = (size_t)data[0];
+  CtxTiled *tiled = data[1];
 
-float
-ctx_text_width (Ctx        *ctx,
-                const char *string)
-{
-  float sum = 0.0;
-  if (!string)
-    return 0.0f;
-  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
-    {
-      sum += ctx_glyph_width (ctx, ctx_utf8_to_unichar (utf8) );
-    }
-  return sum;
-}
+  while (!tiled->quit)
+  {
+    Ctx *host = tiled->host[no];
 
-static void
-_ctx_glyphs (Ctx     *ctx,
-             CtxGlyph *glyphs,
-             int       n_glyphs,
-             int       stroke)
-{
-  for (int i = 0; i < n_glyphs; i++)
-    {
-      {
-        uint32_t unichar = glyphs[i].index;
-        ctx_move_to (ctx, glyphs[i].x, glyphs[i].y);
-        ctx_glyph (ctx, unichar, stroke);
-      }
-    }
-}
+    mtx_lock (&tiled->mtx);
+    cnd_wait(&tiled->cond, &tiled->mtx);
+    mtx_unlock (&tiled->mtx);
 
-static void
-_ctx_text (Ctx        *ctx,
-           const char *string,
-           int         stroke,
-           int         visible)
-{
-  CtxState *state = &ctx->state;
-  float x = ctx->state.x;
-  switch ( (int) ctx_state_get (state, CTX_text_align) )
-    //switch (state->gstate.text_align)
-    {
-      case CTX_TEXT_ALIGN_START:
-      case CTX_TEXT_ALIGN_LEFT:
-        break;
-      case CTX_TEXT_ALIGN_CENTER:
-        x -= ctx_text_width (ctx, string) /2;
-        break;
-      case CTX_TEXT_ALIGN_END:
-      case CTX_TEXT_ALIGN_RIGHT:
-        x -= ctx_text_width (ctx, string);
-        break;
-    }
-  float y = ctx->state.y;
-  float baseline_offset = 0.0f;
-  switch ( (int) ctx_state_get (state, CTX_text_baseline) )
-    {
-      case CTX_TEXT_BASELINE_HANGING:
-        /* XXX : crude */
-        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;
-        break;
-      case CTX_TEXT_BASELINE_BOTTOM:
-        baseline_offset = -ctx->state.gstate.font_size * 0.1;
-        break;
-      case CTX_TEXT_BASELINE_ALPHABETIC:
-      case CTX_TEXT_BASELINE_IDEOGRAPHIC:
-        baseline_offset = 0.0f;
-        break;
-      case CTX_TEXT_BASELINE_MIDDLE:
-        baseline_offset = ctx->state.gstate.font_size * 0.25;
-        break;
-    }
-  float x0 = x;
-  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
+    if (tiled->render_frame != tiled->rendered_frame[no])
     {
-      if (*utf8 == '\n')
-        {
-          y += ctx->state.gstate.font_size * ctx_state_get (state, CTX_line_spacing);
-          x = x0;
-          if (visible)
-            { ctx_move_to (ctx, x, y); }
-        }
-      else
+      int hno = 0;
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
         {
-          uint32_t unichar = ctx_utf8_to_unichar (utf8);
-          if (visible)
-            {
-              ctx_move_to (ctx, x, y + baseline_offset);
-              _ctx_glyph (ctx, unichar, stroke);
-            }
-          const char *next_utf8 = ctx_utf8_skip (utf8, 1);
-          if (next_utf8)
+          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)
             {
-              x += ctx_glyph_width (ctx, unichar);
-              x += ctx_glyph_kern (ctx, unichar, ctx_utf8_to_unichar (next_utf8) );
+              width += tiled->width / CTX_HASH_COLS;
+              col++;
+              hno++;
             }
-          if (visible)
-            { ctx_move_to (ctx, x, y); }
+#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_BGRA8,
+                                 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;
     }
-  if (!visible)
-    { ctx_move_to (ctx, x, y); }
+  }
+  tiled->thread_quit++; // need atomic?
 }
 
+#endif
 
-CtxGlyph *
-ctx_glyph_allocate (int n_glyphs)
-{
-  return (CtxGlyph *) malloc (sizeof (CtxGlyph) * n_glyphs);
-}
-void
-gtx_glyph_free     (CtxGlyph *glyphs)
-{
-  free (glyphs);
-}
-
-void
-ctx_glyphs (Ctx        *ctx,
-            CtxGlyph   *glyphs,
-            int         n_glyphs)
-{
-  _ctx_glyphs (ctx, glyphs, n_glyphs, 0);
-}
 
-void
-ctx_glyphs_stroke (Ctx        *ctx,
-                   CtxGlyph   *glyphs,
-                   int         n_glyphs)
-{
-  _ctx_glyphs (ctx, glyphs, n_glyphs, 1);
-}
+#if CTX_EVENTS
 
-void
-ctx_text (Ctx        *ctx,
-          const char *string)
-{
-  if (!string)
-    return;
-#if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_TEXT, string, 0, 0);
-  _ctx_text (ctx, string, 0, 0);
-#else
-  _ctx_text (ctx, string, 0, 1);
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
 #endif
-}
 
 
-void
-ctx_fill_text (Ctx *ctx, const char *string,
-               float x, float y)
+#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
 {
-  ctx_move_to (ctx, x, y);
-  ctx_text (ctx, string);
-}
+  void   *priv; /* private storage  */
 
-void
-ctx_text_stroke (Ctx        *ctx,
-                 const char *string)
+  /* 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
 {
-  if (!string)
-    return;
-#if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_STROKE_TEXT, string, 0, 0);
-  _ctx_text (ctx, string, 1, 0);
-#else
-  _ctx_text (ctx, string, 1, 1);
+   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;
 
-void
-ctx_stroke_text (Ctx *ctx, const char *string,
-               float x, float y)
-{
-  ctx_move_to (ctx, x, y);
-  ctx_text_stroke (ctx, string);
-}
+   uint8_t      *fb;
 
-static int _ctx_resolve_font (const char *name)
+   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)
 {
-  for (int i = 0; i < ctx_font_count; i ++)
-    {
-      if (!ctx_strcmp (ctx_fonts[i].name, name) )
-        { return i; }
-    }
-  for (int i = 0; i < ctx_font_count; i ++)
-    {
-      if (ctx_strstr (ctx_fonts[i].name, name) )
-        { return i; }
-    }
-  return -1;
+  if (ctx_fb_clipboard)
+    free (ctx_fb_clipboard);
+  ctx_fb_clipboard = NULL;
+  if (text)
+  {
+    ctx_fb_clipboard = strdup (text);
+  }
 }
 
-int ctx_resolve_font (const char *name)
+static char *ctx_fb_get_clipboard (CtxFb *sdl)
 {
-  int ret = _ctx_resolve_font (name);
-  if (ret >= 0)
-    { return ret; }
-  if (!ctx_strcmp (name, "regular") )
-    {
-      int ret = _ctx_resolve_font ("sans");
-      if (ret >= 0) { return ret; }
-      ret = _ctx_resolve_font ("serif");
-      if (ret >= 0) { return ret; }
-    }
-  return 0;
+  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
+  return strdup ("");
 }
 
-static void ctx_font_setup ()
+#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)
 {
-  static int initialized = 0;
-  if (initialized) { return; }
-  initialized = 1;
-#if CTX_FONT_ENGINE_CTX
-  ctx_font_count = 0; // oddly - this is needed in arduino
+   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 CTX_FONT_ENGINE_CTX_FS
-  ctx_load_font_ctx_fs ("sans-ctx", "/tmp/ctx-regular", 0);
-#else
-#if CTX_FONT_ascii
-  ctx_load_font_ctx ("sans-ctx", ctx_font_ascii, sizeof (ctx_font_ascii) );
-#endif
-#if CTX_FONT_regular
-  ctx_load_font_ctx ("sans-ctx", ctx_font_regular, sizeof (ctx_font_regular) );
-#endif
-#endif
+   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
+     goto cleanup;
+   got_master = 1;
 
-#if CTX_FONT_mono
-  ctx_load_font_ctx ("mono-ctx", ctx_font_mono, sizeof (ctx_font_mono) );
-#endif
-#if CTX_FONT_bold
-  ctx_load_font_ctx ("bold-ctx", ctx_font_bold, sizeof (ctx_font_bold) );
-#endif
-#if CTX_FONT_italic
-  ctx_load_font_ctx ("italic-ctx", ctx_font_italic, sizeof (ctx_font_italic) );
-#endif
-#if CTX_FONT_sans
-  ctx_load_font_ctx ("sans-ctx", ctx_font_sans, sizeof (ctx_font_sans) );
-#endif
-#if CTX_FONT_serif
-  ctx_load_font_ctx ("serif-ctx", ctx_font_serif, sizeof (ctx_font_serif) );
-#endif
-#if CTX_FONT_symbol
-  ctx_load_font_ctx ("symbol-ctx", ctx_font_symbol, sizeof (ctx_font_symbol) );
-#endif
-#if CTX_FONT_emoji
-  ctx_load_font_ctx ("emoji-ctx", ctx_font_emoji, sizeof (ctx_font_emoji) );
-#endif
-#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;
 
-#if NOTO_EMOJI_REGULAR
-  ctx_load_font_ttf ("sans-NotoEmoji_Regular", ttf_NotoEmoji_Regular_ttf, ttf_NotoEmoji_Regular_ttf_len);
-#endif
-#if ROBOTO_LIGHT
-  ctx_load_font_ttf ("sans-light-Roboto_Light", ttf_Roboto_Light_ttf, ttf_Roboto_Light_ttf_len);
-#endif
-#if ROBOTO_REGULAR
-  ctx_load_font_ttf ("sans-Roboto_Regular", ttf_Roboto_Regular_ttf, ttf_Roboto_Regular_ttf_len);
-#endif
-#if ROBOTO_BOLD
-  ctx_load_font_ttf ("sans-bold-Roboto_Bold", ttf_Roboto_Bold_ttf, ttf_Roboto_Bold_ttf_len);
-#endif
-#if DEJAVU_SANS
-  ctx_load_font_ttf ("sans-DejaVuSans", ttf_DejaVuSans_ttf, ttf_DejaVuSans_ttf_len);
-#endif
-#if VERA
-  ctx_load_font_ttf ("sans-Vera", ttf_Vera_ttf, ttf_Vera_ttf_len);
-#endif
-#if UNSCII_16
-  ctx_load_font_ttf ("mono-unscii16", ttf_unscii_16_ttf, ttf_unscii_16_ttf_len);
-#endif
-#if XA000_MONO
-  ctx_load_font_ttf ("mono-0xA000", ttf_0xA000_Mono_ttf, ttf_0xA000_Mono_ttf_len);
-#endif
-#if DEJAVU_SANS_MONO
-  ctx_load_font_ttf ("mono-DejaVuSansMono", ttf_DejaVuSansMono_ttf, ttf_DejaVuSansMono_ttf_len);
-#endif
-#if NOTO_MONO_REGULAR
-  ctx_load_font_ttf ("mono-NotoMono_Regular", ttf_NotoMono_Regular_ttf, ttf_NotoMono_Regular_ttf_len);
-#endif
-}
 
+   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};
+
+     struct drm_mode_get_connector conn={0};
+
+     conn.connector_id=res_conn_buf[i];
+
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
+
+     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 (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
+
+     //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;
+
+//------------------------------------------------------------------------------
+//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;
+
+     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;
 
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
-#endif
+     map_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
+       goto cleanup;
 
-#if !__COSMOPOLITAN__
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
+     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;
 
-//#include "ctx.h"
-/* instead of including ctx.h we declare the few utf8
- * functions we use
- */
-uint32_t ctx_utf8_to_unichar (const char *input);
-int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
-int ctx_utf8_strlen (const char *s);
+     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;
 
-void ctx_string_init (CtxString *string, int initial_size)
-{
-  string->allocated_length = initial_size;
-  string->length = 0;
-  string->utf8_length = 0;
-  string->str = (char*)malloc (string->allocated_length + 1);
-  string->str[0]='\0';
+     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;
 }
 
-static void ctx_string_destroy (CtxString *string)
+void ctx_fbdrm_flip (CtxFb *fb)
 {
-  if (string->str)
-    {
-      free (string->str);
-      string->str = NULL;
-    }
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
 }
 
-void ctx_string_clear (CtxString *string)
+void ctx_fbdrm_close (CtxFb *fb)
 {
-  string->length = 0;
-  string->utf8_length = 0;
-  string->str[string->length]=0;
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+  close (fb->fb_fd);
+  fb->fb_fd = 0;
 }
 
-static inline void _ctx_string_append_byte (CtxString *string, char  val)
+static void ctx_fb_flip (CtxFb *fb)
 {
-  if ( (val & 0xC0) != 0x80)
-    { string->utf8_length++; }
-  if (string->length + 2 >= string->allocated_length)
-    {
-      char *old = string->str;
-      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
-      string->str = (char*)realloc (old, string->allocated_length);
-    }
-  string->str[string->length++] = val;
-  string->str[string->length] = '\0';
+  if (fb->is_drm)
+    ctx_fbdrm_flip (fb);
+  else
+    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
 }
 
-void ctx_string_append_byte (CtxString *string, char  val)
+inline static uint32_t
+ctx_swap_red_green2 (uint32_t orig)
 {
-  _ctx_string_append_byte (string, val);
+  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;
 }
 
-void ctx_string_append_unichar (CtxString *string, unsigned int unichar)
+static int       ctx_fb_cursor_drawn   = 0;
+static int       ctx_fb_cursor_drawn_x = 0;
+static int       ctx_fb_cursor_drawn_y = 0;
+static CtxCursor ctx_fb_cursor_drawn_shape = 0;
+
+
+#define CTX_FB_HIDE_CURSOR_FRAMES 200
+
+static int ctx_fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
+
+static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
 {
-  char *str;
-  char utf8[5];
-  utf8[ctx_unichar_to_utf8 (unichar, (unsigned char *) utf8)]=0;
-  str = utf8;
-  while (str && *str)
-    {
-      _ctx_string_append_byte (string, *str);
-      str++;
-    }
+  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;
+      }
+      /*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 inline void _ctx_string_append_str (CtxString *string, const char *str)
+static void ctx_fb_undraw_cursor (CtxFb *fb)
 {
-  if (!str) { return; }
-  while (*str)
+    CtxTiled *tiled = (void*)fb;
+    int cursor_size = ctx_height (tiled->ctx) / 28;
+
+    if (ctx_fb_cursor_drawn)
     {
-      _ctx_string_append_byte (string, *str);
-      str++;
+      int no = 0;
+      int startx = -cursor_size;
+      int starty = -cursor_size;
+      if (ctx_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)
+      {
+        if (x + ctx_fb_cursor_drawn_x < tiled->width && y + ctx_fb_cursor_drawn_y < tiled->height)
+        {
+          if (ctx_is_in_cursor (x, y, cursor_size, ctx_fb_cursor_drawn_shape))
+          {
+            int o = ((ctx_fb_cursor_drawn_y + y) * tiled->width + (ctx_fb_cursor_drawn_x + x)) * 4;
+            fb->fb[o+0]^=0x88;
+            fb->fb[o+1]^=0x88;
+            fb->fb[o+2]^=0x88;
+          }
+        }
+      }
+
+    ctx_fb_cursor_drawn = 0;
     }
 }
 
-void ctx_string_append_utf8char (CtxString *string, const char *str)
+static void ctx_fb_draw_cursor (CtxFb *fb)
 {
-  if (!str) { return; }
-  int len = ctx_utf8_len (*str);
-  for (int i = 0; i < len && *str; i++)
+    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 == ctx_fb_cursor_drawn_x &&
+        cursor_y == ctx_fb_cursor_drawn_y &&
+        cursor_shape == ctx_fb_cursor_drawn_shape)
+      ctx_fb_cursor_same_pos ++;
+    else
+      ctx_fb_cursor_same_pos = 0;
+
+    if (ctx_fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
     {
-      _ctx_string_append_byte (string, *str);
-      str++;
+      if (ctx_fb_cursor_drawn)
+        ctx_fb_undraw_cursor (fb);
+      return;
     }
-}
 
-void ctx_string_append_str (CtxString *string, const char *str)
-{
-  _ctx_string_append_str (string, str);
-}
+    /* 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 (ctx_fb_cursor_same_pos && ctx_fb_cursor_drawn)
+      return;
 
-CtxString *ctx_string_new_with_size (const char *initial, int initial_size)
-{
-  CtxString *string = (CtxString*)ctx_calloc (sizeof (CtxString), 1);
-  ctx_string_init (string, initial_size);
-  if (initial)
-    { _ctx_string_append_str (string, initial); }
-  return string;
-}
+    ctx_fb_undraw_cursor (fb);
 
-CtxString *ctx_string_new (const char *initial)
-{
-  return ctx_string_new_with_size (initial, 8);
-}
+    no = 0;
 
-void ctx_string_append_data (CtxString *string, const char *str, int len)
-{
-  int i;
-  for (i = 0; i<len; i++)
-    { _ctx_string_append_byte (string, str[i]); }
+    int startx = -cursor_size;
+    int starty = -cursor_size;
+
+    if (cursor_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)
+      {
+        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;
+          }
+        }
+      }
+    ctx_fb_cursor_drawn = 1;
+    ctx_fb_cursor_drawn_x = cursor_x;
+    ctx_fb_cursor_drawn_y = cursor_y;
+    ctx_fb_cursor_drawn_shape = cursor_shape;
 }
 
-void ctx_string_append_string (CtxString *string, CtxString *string2)
+static void ctx_fb_show_frame (CtxFb *fb, int block)
 {
-  const char *str = ctx_string_get (string2);
-  while (str && *str)
+  CtxTiled *tiled = (void*)fb;
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    if (block == 0) // consume event call
     {
-      _ctx_string_append_byte (string, *str);
-      str++;
+      ctx_fb_draw_cursor (fb);
+      ctx_fb_flip (fb);
     }
-}
+    return;
+  }
 
-const char *ctx_string_get (CtxString *string)
-{
-  return string->str;
-}
+  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;
+  }
 
-int ctx_string_get_utf8length (CtxString *string)
-{
-  return string->utf8_length;
-}
+    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;
 
-int ctx_string_get_length (CtxString *string)
-{
-  return string->length;
-}
+       int rows = ((tiled->width * tiled->height) - pre_skip - post_skip)/tiled->width;
 
-void
-ctx_string_free (CtxString *string, int freealloc)
-{
-  if (freealloc)
-    {
-      ctx_string_destroy (string);
+       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;
+       }
+
+       if (pre_skip < 0) pre_skip = 0;
+       if (post_skip < 0) post_skip = 0;
+
+     __u32 dummy = 0;
+
+       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;
+
+     // 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;
+     }
     }
-#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); }
+    ctx_fb_cursor_drawn = 0;
+    ctx_fb_draw_cursor (fb);
+    ctx_fb_flip (fb);
+    tiled->shown_frame = tiled->render_frame;
   }
-#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;
-}
+#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)
 
-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);
-}
+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 ctx_ev_src_mice = {
+  NULL,
+  (void*)mice_has_event,
+  (void*)mice_get_event,
+  (void*)mice_destroy,
+  mice_get_fd,
+  mice_set_coord
+};
 
-void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
+typedef struct Mice
 {
-  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);
-  free (rest);
-  string->length = strlen (string->str);
-  string->utf8_length = ctx_utf8_strlen (string->str);
-}
+  int     fd;
+  double  x;
+  double  y;
+  int     button;
+  int     prev_state;
+} Mice;
 
-void ctx_string_insert_unichar (CtxString *string, int pos, uint32_t unichar)
+Mice *_mrg_evsrc_coord = NULL;
+static int _ctx_mice_fd = 0;
+
+void _mmm_get_coords (Ctx *ctx, double *x, double *y)
 {
-  uint8_t utf8[5]="";
-  utf8[ctx_unichar_to_utf8(unichar, utf8)]=0;
-  ctx_string_insert_utf8 (string, pos, (char*)utf8);
+  if (!_mrg_evsrc_coord)
+    return;
+  if (x)
+    *x = _mrg_evsrc_coord->x;
+  if (y)
+    *y = _mrg_evsrc_coord->y;
 }
 
-void ctx_string_remove (CtxString *string, int pos)
+static Mice  mice;
+static Mice* mrg_mice_this = &mice;
+
+static int mmm_evsource_mice_init ()
 {
-  int old_len = string->utf8_length;
+  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)
   {
-    for (int i = old_len; i <= pos; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
+    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;
   }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if (!p || *p == 0)
-    {
-      return;
-      rest = ctx_strdup ("");
-      prev_len = 0;
-    }
-  else if (* (p+prev_len) == 0)
+  if (write (mrg_mice_this->fd, reset, 1) == -1)
   {
-      rest = ctx_strdup ("");
+    // might happen if we're a regular user with only read permission
   }
-  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);
+  _ctx_mice_fd = mrg_mice_this->fd;
+  _mrg_evsrc_coord = mrg_mice_this;
+  return 0;
 }
 
-char *ctx_strdup_printf (const char *format, ...)
+static void mice_destroy ()
 {
-  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;
+  if (mrg_mice_this->fd != -1)
+    close (mrg_mice_this->fd);
 }
 
-void ctx_string_append_printf (CtxString *string, const char *format, ...)
+static int mice_has_event ()
 {
-  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);
-}
+  struct timeval tv;
+  int retval;
 
-#if CTX_CAIRO
+  if (mrg_mice_this->fd == -1)
+    return 0;
 
-typedef struct _CtxCairo CtxCairo;
-struct
-  _CtxCairo
-{
-  CtxImplementation vfuncs;
-  Ctx              *ctx;
-  cairo_t          *cr;
-  cairo_pattern_t  *pat;
-  cairo_surface_t  *image;
-  int               preserve;
-};
+  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;
+}
 
-static void
-ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
+static char *mice_get_event ()
 {
-  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:
-#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:
+  const char *ret = "mouse-motion";
+  double relx, rely;
+  signed char buf[3];
+  int n_read = 0;
+  CtxFb *fb = ctx_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];
+
+  if (relx < 0)
+  {
+    if (relx > -6)
+    relx = - relx*relx;
+    else
+    relx = -36;
+  }
+  else
+  {
+    if (relx < 6)
+    relx = relx*relx;
+    else
+    relx = 36;
+  }
+
+  if (rely < 0)
+  {
+    if (rely > -6)
+    rely = - rely*rely;
+    else
+    rely = -36;
+  }
+  else
+  {
+    if (rely < 6)
+    rely = rely*rely;
+    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 >= 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)
         {
-          // does not map to cairo
+          ret = "mouse-press";
         }
-        break;
-      case CTX_COMPOSITING_MODE:
+      else
         {
-          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);
+          ret = "mouse-release";
         }
-      case CTX_LINE_JOIN:
+      button = 1;
+    }
+  else if (buf[0] & 1)
+  {
+    ret = "mouse-drag";
+    button = 1;
+  }
+
+  if (!button)
+  {
+    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
+    {
+      if (buf[0] & 2)
         {
-          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);
+          ret = "mouse-press";
         }
-        break;
-      case CTX_LINEAR_GRADIENT:
+      else
         {
-          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);
+          ret = "mouse-release";
         }
-        break;
-      case CTX_RADIAL_GRADIENT:
+      button = 3;
+    }
+    else if (buf[0] & 2)
+    {
+      ret = "mouse-drag";
+      button = 3;
+    }
+  }
+
+  if (!button)
+  {
+    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
+    {
+      if (buf[0] & 4)
         {
-          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);
+          ret = "mouse-press";
         }
-        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:
+      else
         {
-          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) );
+          ret = "mouse-release";
         }
-        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;
+      button = 2;
     }
-  ctx_process (ctx_cairo->ctx, entry);
+    else if (buf[0] & 4)
+    {
+      ret = "mouse-drag";
+      button = 2;
+    }
+  }
+
+  mrg_mice_this->prev_state = buf[0];
+
+  {
+    char *r = malloc (64);
+    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
+    return r;
+  }
+
+  return NULL;
 }
 
-void ctx_cairo_free (CtxCairo *ctx_cairo)
+static int mice_get_fd (EvSource *ev_source)
 {
-  if (ctx_cairo->pat)
-    { cairo_pattern_destroy (ctx_cairo->pat); }
-  if (ctx_cairo->image)
-    { cairo_surface_destroy (ctx_cairo->image); }
-  free (ctx_cairo);
+  return mrg_mice_this->fd;
 }
 
-void
-ctx_render_cairo (Ctx *ctx, cairo_t *cr)
+static void mice_set_coord (EvSource *ev_source, double x, double y)
 {
-  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); }
+  mrg_mice_this->x = x;
+  mrg_mice_this->y = y;
 }
 
-Ctx *
-ctx_new_for_cairo (cairo_t *cr)
+static EvSource *evsource_mice_new (void)
 {
-  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 (mmm_evsource_mice_init () == 0)
+    {
+      mrg_mice_this->x = 0;
+      mrg_mice_this->y = 0;
+      return &ctx_ev_src_mice;
+    }
+  return NULL;
 }
 
-#endif
+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);
 
-#if CTX_EVENTS
+/* kept out of struct to be reachable by atexit */
+static EvSource ctx_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 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;
-} 
+static struct termios orig_attr;
 
-typedef struct CtxSpan {
-  int from_prev;
-  int start;
-  int length;
-} CtxSpan;
+static void real_evsource_kb_destroy (int sign)
+{
+  static int done = 0;
 
-#define CHUNK_SIZE 32
-#define MIN_MATCH  7        // minimum match length to be encoded
-#define WINDOW_PADDING 16   // look-aside amount
+  if (sign == 0)
+    return;
 
-#if 0
-static void _dassert(int line, int condition, const char *str, int foo, int bar, int baz)
-{
-  if (!condition)
+  if (done)
+    return;
+  done = 1;
+
+  switch (sign)
   {
-    FILE *f = fopen ("/tmp/cdebug", "a");
-    fprintf (f, "%i: %s    %i %i %i\n", line, str, foo, bar, baz);
-    fclose (f);
+    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");
 }
-#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)
+static void evsource_kb_destroy (int sign)
 {
-  CtxString *string = ctx_string_new ("");
-  CtxList *encoded_list = NULL;
-
-  /* TODO : make expected position offset in prev slide based on
-   * matches and not be constant */
+  real_evsource_kb_destroy (-11);
+}
 
-  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);
-  }
+static int evsource_kb_init ()
+{
+//  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);
 
-  for (CtxList *l = encoded_list; l; l = l->next)
-  {
-    CtxSpan *span = l->data;
-    if (!span->from_prev)
+  struct termios raw;
+  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
     {
-      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;
+      fprintf (stderr, "error initializing keyboard\n");
+      return -1;
+    }
+  raw = orig_attr;
 
-         dassert(span->start>=0 , 0,0,0);
+  cfmakeraw (&raw);
 
-         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
+  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 (match_len >= MIN_MATCH)
-         {
-            int start  = span->start;
-            int length = span->length;
+  return 0;
+}
+static int evsource_kb_has_event (void)
+{
+  struct timeval tv;
+  int retval;
 
-            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);
+  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;
+}
 
-            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);
-            }
+/* 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"},
 
-            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);
-            }
+  {"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"},
 
-            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;
-              }
-            }
-         }
-      }
-    }
+  {"control-up",          "\e[1;5A"},
+  {"control-down",        "\e[1;5B"},
+  {"control-right",       "\e[1;5C"},
+  {"control-left",        "\e[1;5D"},
 
-    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
-      break;
-  }
+  /* putty */
+  {"control-up",          "\eOA"},
+  {"control-down",        "\eOB"},
+  {"control-right",       "\eOC"},
+  {"control-left",        "\eOD"},
 
-  /* 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;
-        }
-      }
-    }
-  }
+  {"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"},
 
-  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)
-        {
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-        }
-        else
-        {
-          ctx_string_append_byte (string, src[i]);
-        }
-      }
-    }
-    free (span);
-    ctx_list_remove (&encoded_list, span);
-  }
+  {"shift-up",            "\e[a"},
+  {"shift-down",          "\e[b"},
+  {"shift-right",         "\e[c"},
+  {"shift-left",          "\e[d"},
 
-  char *ret = string->str;
-  if (out_len) *out_len = string->length;
-  ctx_string_free (string, 0);
-  return ret;
-}
+  {"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 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);
+  {"F1",         "\e[25~"},
+  {"F2",         "\e[26~"},
+  {"F3",         "\e[27~"},
+  {"F4",         "\e[26~"},
 
-        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
+  {"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 (!strncmp (buf, "\e[M", MIN(length,3)))
     {
-      if (in_ref)
+      if (length >= 6)
+        return 9001;
+      return 2342;
+    }
+  for (i = 0; ufb_keycodes[i].nick; i++)
+    if (!strncmp (buf, ufb_keycodes[i].sequence, length))
       {
-        if (ref_len < 16)
-        {
-          reference[ref_len++] = encoded[i];
-          reference[ref_len] = 0;
-        }
+        matches ++;
+        if ((int)strlen (ufb_keycodes[i].sequence) == length && ret)
+          {
+            *ret = &ufb_keycodes[i];
+            return 1;
+          }
       }
-      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;
+  if (matches != 1 && ret)
+    *ret = NULL;
+  return matches==1?2:matches;
 }
-#endif
-
-#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;
+//int is_active (void *host)
+//{
+//        return 1;
+//}
 
-static void ctx_ctx_flush (CtxCtx *ctxctx)
+static char *evsource_kb_get_event (void)
 {
-#if 0
-  FILE *debug = fopen ("/tmp/ctx-debug", "a");
-  fprintf (debug, "------\n");
-#endif
+  unsigned char buf[20];
+  int length;
 
-  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);
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const MmmKeyCode *match = NULL;
 
-    if (prev_frame_contents && 0)  // XXX : 
-    {
-      char *encoded;
-      int encoded_len = 0;
-      //uint64_t ticks_start = ctx_ticks ();
+        //if (!is_active (ctx_ev_src_kb.priv))
+        //  return NULL;
 
-      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 ();
+        /* 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");
+          }
 
-      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);
-    }
+        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
-    {
-      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);
+      return strdup("key read eek");
+  return strdup("fail");
 }
 
-void ctx_ctx_free (CtxCtx *ctx)
+static int evsource_kb_get_fd (void)
 {
-  nc_at_exit ();
-  free (ctx);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
+  return STDIN_FILENO;
 }
 
-Ctx *ctx_new_ctx (int width, int height)
+
+static EvSource *evsource_kb_new (void)
 {
-  Ctx *ctx = ctx_new ();
-  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
-  fprintf (stdout, "\e[?1049h");
-  fflush (stdout);
-  //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
+  if (evsource_kb_init() == 0)
   {
-    ctxctx->width  = width;
-    ctxctx->height = height;
-    ctxctx->cols   = width / 80;
-    ctxctx->rows   = height / 24;
+    return &ctx_ev_src_kb;
   }
-  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;
+  return NULL;
 }
 
-void ctx_ctx_pcm (Ctx *ctx);
-
-int ctx_ctx_consume_events (Ctx *ctx)
+static int event_check_pending (CtxFb *fb)
 {
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
-#if CTX_AUDIO
-  ctx_ctx_pcm (ctx);
-#endif
-  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 (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"))
-      {
-        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
-      {
-        ctx_key_press (ctx, 0, event, 0);
-      }
-      }
-    }
-  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;
-
-      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"))
+  CtxTiled *tiled = (void*)fb;
+  int events = 0;
+  for (int i = 0; i < fb->evsource_count; i++)
+  {
+    while (evsource_has_event (fb->evsource[i]))
+    {
+      char *event = evsource_get_event (fb->evsource[i]);
+      if (event)
       {
-        //nct_set_cursor_pos (backend->term, ix, iy);
-        //nct_flush (backend->term);
-        if (ctxctx->was_down)
+        if (fb->vt_active)
         {
-          ctx_pointer_release (ctx, x, y, 0, 0);
-          ctxctx->was_down = 0;
+          ctx_key_press (tiled->ctx, 0, event, 0); // we deliver all events as key-press, the key_press 
handler disambiguates
+          events++;
         }
-        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")||
-                 !strcmp (event, "return"))
-          ctx_key_press (ctx, 0, "\n", 0);
-        else
-        ctx_key_press (ctx, 0, event, 0);
+        free (event);
       }
     }
-
-  return 1;
+  }
+  return events;
 }
 
-int ctx_renderer_is_ctx (Ctx *ctx)
+int ctx_fb_consume_events (Ctx *ctx)
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_ctx_free)
-          return 1;
+  CtxFb *fb = (void*)ctx->renderer;
+  ctx_fb_show_frame (fb, 0);
+  event_check_pending (fb);
   return 0;
 }
 
-#endif
+inline static void ctx_fb_reset (CtxFb *fb)
+{
+  ctx_fb_show_frame (fb, 1);
+}
 
-#if CTX_TILED
-static inline int
-ctx_tiled_threads_done (CtxTiled *tiled)
+inline static void ctx_fb_flush (CtxFb *fb)
 {
-  int sum = 0;
-  for (int i = 0; i < _ctx_max_threads; i++)
+  ctx_tiled_flush ((CtxTiled*)fb);
+}
+
+void ctx_fb_free (CtxFb *fb)
+{
+  if (fb->is_drm)
   {
-     if (tiled->rendered_frame[i] == tiled->render_frame)
-       sum ++;
+    ctx_fbdrm_close (fb);
   }
-  return sum;
+
+  ioctl (0, KDSETMODE, KD_TEXT);
+  if (system("stty sane")){};
+  ctx_tiled_free ((CtxTiled*)fb);
+  //free (fb);
+#if CTX_BABL
+  babl_exit ();
+#endif
 }
 
-int _ctx_damage_control = 0;
+//static unsigned char *fb_icc = NULL;
+//static long fb_icc_length = 0;
 
-void ctx_tiled_free (CtxTiled *tiled)
+int ctx_renderer_is_fb (Ctx *ctx)
 {
-  tiled->quit = 1;
-  mtx_lock (&tiled->mtx);
-  cnd_broadcast (&tiled->cond);
-  mtx_unlock (&tiled->mtx);
-
-  while (tiled->thread_quit < _ctx_max_threads)
-    usleep (1000);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_fb_free)
+          return 1;
+  return 0;
+}
 
-  if (tiled->pixels)
-  {
-    free (tiled->pixels);
-  tiled->pixels = NULL;
-  for (int i = 0 ; i < _ctx_max_threads; i++)
+static CtxFb *ctx_fb = NULL;
+static void vt_switch_cb (int sig)
+{
+  CtxTiled *tiled = (void*)ctx_fb;
+  if (sig == SIGUSR1)
   {
-    ctx_free (tiled->host[i]);
-    tiled->host[i]=NULL;
+    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
+    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
+    {
+      tiled->ctx->dirty=1;
 
-  ctx_free (tiled->ctx_copy);
+      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;
+      }
+    }
   }
-  // leak?
 }
-static unsigned char *sdl_icc = NULL;
-static long sdl_icc_length = 0;
 
-inline static void ctx_tiled_flush (CtxTiled *tiled)
+static int ctx_fb_get_mice_fd (Ctx *ctx)
 {
-  if (tiled->shown_frame == tiled->render_frame)
+  //CtxFb *fb = (void*)ctx->renderer;
+  return _ctx_mice_fd;
+}
+
+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)
   {
-    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)
+    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
+  {
+  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)
     {
-      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);
+      fb->fb_path = strdup ("/dev/graphics/fb0");
     }
     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++;
-        }
+      free (fb);
+      return NULL;
     }
-    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 (tiled->tile_affinity[row * CTX_HASH_COLS + col] != -1)
-        {
-          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)
+  if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
     {
-      for (int i = 0; i < tiled->width * tiled->height; i++)
-      {
-        tiled->pixels[i*4+2]  = (tiled->pixels[i*4+2] + 255)/2;
-      }
+      fprintf (stderr, "error getting fbinfo\n");
+      close (fb->fb_fd);
+      free (fb->fb_path);
+      free (fb);
+      return NULL;
     }
 
-    tiled->render_frame = ++tiled->frame;
-
-#if 0
-
-          //if (tiled->tile_affinity[hno]==no)
-          {
-            int x0 = ((tiled->width)/CTX_HASH_COLS) * 0;
-            int y0 = ((tiled->height)/CTX_HASH_ROWS) * 0;
-            int width = tiled->width / CTX_HASH_COLS;
-            int height = tiled->height / CTX_HASH_ROWS;
-            Ctx *host = tiled->host[0];
-
-            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
-            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, 1, 1,
-                                 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);
-          }
-#endif
+   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;
 
-    mtx_lock (&tiled->mtx);
-    cnd_broadcast (&tiled->cond);
-    mtx_unlock (&tiled->mtx);
-  }
-}
+  fb->fb_bits = fb->vinfo.bits_per_pixel;
+//fprintf (stderr, "fb bits: %i\n", fb->fb_bits);
 
-static
-void ctx_tiled_render_fun (void **data)
-{
-  int      no = (size_t)data[0];
-  CtxTiled *tiled = data[1];
+  if (fb->fb_bits == 16)
+    fb->fb_bits =
+      fb->vinfo.red.length +
+      fb->vinfo.green.length +
+      fb->vinfo.blue.length;
 
-  while (!tiled->quit)
+   else if (fb->fb_bits == 8)
   {
-    Ctx *host = tiled->host[no];
-
-    mtx_lock (&tiled->mtx);
-    cnd_wait(&tiled->cond, &tiled->mtx);
-    mtx_unlock (&tiled->mtx);
+    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;
 
-    if (tiled->render_frame != tiled->rendered_frame[no])
+    /* do we really need to restore it ? */
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
     {
-      int hno = 0;
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
-        {
-          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;
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
+    }
 
-            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);
+    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;
+    }
 
-            ctx_translate (host, -x0, -y0);
-            ctx_render_ctx (tiled->ctx_copy, host);
-          }
-        }
-      tiled->rendered_frame[no] = tiled->render_frame;
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &cmap) == -1)
+    {
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
     }
   }
-  tiled->thread_quit++; // need atomic?
-}
 
-#endif
-
-
-#if CTX_EVENTS
+  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;
 
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <signal.h>
+#if CTX_BABL
+  babl_init ();
 #endif
 
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
 
-#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  */
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
 
-  /* returns non 0 if there is events waiting */
-  int   (*has_event) (EvSource *ev_source);
+  ctx_set_renderer (tiled->ctx, fb);
+  ctx_set_renderer (tiled->ctx_copy, fb);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
 
-  /* get an event, the returned event should be freed by the caller  */
-  char *(*get_event) (EvSource *ev_source);
+  ctx_set_size (tiled->ctx, width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
 
-  /* destroy/unref this instance */
-  void  (*destroy)   (EvSource *ev_source);
+  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;
 
-  /* get the underlying fd, useful for using select on  */
-  int   (*get_fd)    (EvSource *ev_source);
+  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_BGRA8); // this format
+                                  // is overriden in  thread
+    ((CtxRasterizer*)(tiled->host[i]->renderer))->swap_red_green = 1;
+    ctx_set_texture_source (tiled->host[i], tiled->ctx);
+  }
 
+  mtx_init (&tiled->mtx, mtx_plain);
+  cnd_init (&tiled->cond);
 
-  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.
-   */
+#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
 
-  /* if this returns non-0 select can be used for non-blocking.. */
-};
+  ctx_flush (tiled->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;
+  }
 
-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
-                                                           //
+  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;
 
-   int           pointer_down[3];
+  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 tiled->ctx;
+#else
+  return NULL;
+#endif
+}
+#else
+
+int ctx_renderer_is_fb (Ctx *ctx)
+{
+  return 0;
+}
 #endif
+#endif
+
+#if CTX_SDL
+
+/**/
+
+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;
 
-   uint8_t      *fb;
+   SDL_Window   *window;
+   SDL_Renderer *renderer;
+   SDL_Texture  *texture;
 
-   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;
+// cnd_t  cond;
+// mtx_t  mtx;
+   int           fullscreen;
 };
 
-static char *ctx_fb_clipboard = NULL;
-static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
+#include "stb_image_write.h"
+
+void ctx_screenshot (Ctx *ctx, const char *output_path)
 {
-  if (ctx_fb_clipboard)
-    free (ctx_fb_clipboard);
-  ctx_fb_clipboard = NULL;
-  if (text)
+#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
+  // we rely on the same layout
+  for (int i = 0; i < sdl->width * sdl->height; i++)
   {
-    ctx_fb_clipboard = strdup (text);
+    int tmp = sdl->pixels[i*4];
+    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
+    sdl->pixels[i*4 + 2] = tmp;
   }
-}
+#endif
 
-static char *ctx_fb_get_clipboard (CtxFb *sdl)
-{
-  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
-  return strdup ("");
-}
+  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
 
-#if UINTPTR_MAX == 0xffFFffFF
-  #define fbdrmuint_t uint32_t
-#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
-  #define fbdrmuint_t uint64_t
+#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
+}
 
-void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
+int ctx_show_fps = 1;
+void ctx_sdl_set_title (void *self, const char *new_title)
 {
-   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};
+   CtxSDL *sdl = self;
+   if (!ctx_show_fps)
+   SDL_SetWindowTitle (sdl->window, new_title);
+}
 
-   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
-     goto cleanup;
-   got_master = 1;
+static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
+{
+  CtxTiled *tiled = &sdl->tiled;
+  if (tiled->shown_cursor != tiled->ctx->cursor)
+  {
+    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);
+    }
+  }
 
-   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;
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    return;
+  }
 
+  if (block)
+  {
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+    {
+      usleep (50);
+      count ++;
+      if (count > 2000)
+      {
+        tiled->shown_frame = tiled->render_frame;
+        return;
+      }
+    }
+  }
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
 
-   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};
 
-     struct drm_mode_get_connector conn={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;
 
-     conn.connector_id=res_conn_buf[i];
+    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);
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
 
-     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 (ctx_show_fps)
+  {
+    static uint64_t prev_time = 0;
+    static char tmp_title[1024];
+    uint64_t time = ctx_ticks ();
+    sprintf (tmp_title, "FPS: %.1f", 1000000.0/  (time - prev_time));
+    prev_time = time;
+    SDL_SetWindowTitle (sdl->window, tmp_title);
+  }
+  }
+  tiled->shown_frame = tiled->render_frame;
+}
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
+static const char *ctx_sdl_keysym_to_name (unsigned int sym, int *r_keycode)
+{
+  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;
 
-     //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;
+     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;
+}
 
-//------------------------------------------------------------------------------
-//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;
+int ctx_sdl_consume_events (Ctx *ctx)
+{
+  CtxTiled *tiled = (void*)ctx->renderer;
+  CtxSDL *sdl = (void*)ctx->renderer;
+  SDL_Event event;
+  int got_events = 0;
 
-     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;
+  ctx_sdl_show_frame (sdl, 0);
 
-     map_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
-       goto cleanup;
+  while (SDL_PollEvent (&event))
+  {
+    got_events ++;
+    switch (event.type)
+    {
+      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:
+        {
+        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) )
+           )
+          {
+            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);
 
-     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;
+          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
+          {
+#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;
+           }
 
-     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;
+          {
+            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;
+          }
 
-     fb->crtc.crtc_id=enc.crtc_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
-        goto cleanup;
+           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);
 
-     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;
+          tiled->width  = width;
+          tiled->height = height;
+          ctx_set_size (tiled->ctx, width, height);
+          ctx_set_size (tiled->ctx_copy, width, height);
+        }
+        break;
+    }
+  }
+  return 1;
 }
-
-void ctx_fbdrm_flip (CtxFb *fb)
+#else
+void ctx_screenshot (Ctx *ctx, const char *path)
 {
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
 }
+#endif
 
-void ctx_fbdrm_close (CtxFb *fb)
-{
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-  close (fb->fb_fd);
-  fb->fb_fd = 0;
-}
+#if CTX_SDL
 
-static void ctx_fb_flip (CtxFb *fb)
+static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
 {
-  if (fb->is_drm)
-    ctx_fbdrm_flip (fb);
-  else
-    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+  if (text)
+    SDL_SetClipboardText (text);
 }
 
-inline static uint32_t
-ctx_swap_red_green2 (uint32_t orig)
+static char *ctx_sdl_get_clipboard (CtxSDL *sdl)
 {
-  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 SDL_GetClipboardText ();
 }
 
-static int       ctx_fb_cursor_drawn   = 0;
-static int       ctx_fb_cursor_drawn_x = 0;
-static int       ctx_fb_cursor_drawn_y = 0;
-static CtxCursor ctx_fb_cursor_drawn_shape = 0;
-
-
-#define CTX_FB_HIDE_CURSOR_FRAMES 200
-
-static int ctx_fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
-
-static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
+inline static void ctx_sdl_reset (CtxSDL *sdl)
 {
-  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;
-      }
-      /*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;
+  ctx_sdl_show_frame (sdl, 1);
 }
 
-static void ctx_fb_undraw_cursor (CtxFb *fb)
+inline static void ctx_sdl_flush (CtxSDL *sdl)
 {
-    CtxTiled *tiled = (void*)fb;
-    int cursor_size = ctx_height (tiled->ctx) / 28;
-
-    if (ctx_fb_cursor_drawn)
-    {
-      int no = 0;
-      int startx = -cursor_size;
-      int starty = -cursor_size;
-      if (ctx_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)
-      {
-        if (x + ctx_fb_cursor_drawn_x < tiled->width && y + ctx_fb_cursor_drawn_y < tiled->height)
-        {
-          if (ctx_is_in_cursor (x, y, cursor_size, ctx_fb_cursor_drawn_shape))
-          {
-            int o = ((ctx_fb_cursor_drawn_y + y) * tiled->width + (ctx_fb_cursor_drawn_x + x)) * 4;
-            fb->fb[o+0]^=0x88;
-            fb->fb[o+1]^=0x88;
-            fb->fb[o+2]^=0x88;
-          }
-        }
-      }
-
-    ctx_fb_cursor_drawn = 0;
-    }
+  ctx_tiled_flush ((void*)sdl);
+  //CtxTiled *tiled = (void*)sdl;
 }
 
-static void ctx_fb_draw_cursor (CtxFb *fb)
+void ctx_sdl_free (CtxSDL *sdl)
 {
-    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 == ctx_fb_cursor_drawn_x &&
-        cursor_y == ctx_fb_cursor_drawn_y &&
-        cursor_shape == ctx_fb_cursor_drawn_shape)
-      ctx_fb_cursor_same_pos ++;
-    else
-      ctx_fb_cursor_same_pos = 0;
-
-    if (ctx_fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
-    {
-      if (ctx_fb_cursor_drawn)
-        ctx_fb_undraw_cursor (fb);
-      return;
-    }
-
-    /* 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 (ctx_fb_cursor_same_pos && ctx_fb_cursor_drawn)
-      return;
 
-    ctx_fb_undraw_cursor (fb);
-
-    no = 0;
+  if (sdl->texture)
+  SDL_DestroyTexture (sdl->texture);
+  if (sdl->renderer)
+  SDL_DestroyRenderer (sdl->renderer);
+  if (sdl->window)
+  SDL_DestroyWindow (sdl->window);
 
-    int startx = -cursor_size;
-    int starty = -cursor_size;
+  ctx_tiled_free ((CtxTiled*)sdl);
+#if CTX_BABL
+  babl_exit ();
+#endif
+}
 
-    if (cursor_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)
-      {
-        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;
-          }
-        }
-      }
-    ctx_fb_cursor_drawn = 1;
-    ctx_fb_cursor_drawn_x = cursor_x;
-    ctx_fb_cursor_drawn_y = cursor_y;
-    ctx_fb_cursor_drawn_shape = cursor_shape;
+int ctx_renderer_is_sdl (Ctx *ctx)
+{
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_sdl_free)
+          return 1;
+  return 0;
 }
 
-static void ctx_fb_show_frame (CtxFb *fb, int block)
+void ctx_sdl_set_fullscreen (Ctx *ctx, int val)
 {
-  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;
-  }
+  CtxSDL *sdl = (void*)ctx->renderer;
 
-  if (block)
+  if (val)
   {
-    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;
-      }
-    }
+    SDL_SetWindowFullscreen (sdl->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
   }
   else
   {
-    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-      return;
-  }
-
-    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;
-
-       int rows = ((tiled->width * tiled->height) - pre_skip - post_skip)/tiled->width;
-
-       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;
-       }
-
-       if (pre_skip < 0) pre_skip = 0;
-       if (post_skip < 0) post_skip = 0;
-
-     __u32 dummy = 0;
-
-       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;
-
-     // 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;
-     }
-    }
-    ctx_fb_cursor_drawn = 0;
-    ctx_fb_draw_cursor (fb);
-    ctx_fb_flip (fb);
-    tiled->shown_frame = tiled->render_frame;
+    SDL_SetWindowFullscreen (sdl->window, 0);
   }
+  // XXX we're presuming success
+  sdl->fullscreen = val;
+}
+int ctx_sdl_get_fullscreen (Ctx *ctx)
+{
+  CtxSDL *sdl = (void*)ctx->renderer;
+  return sdl->fullscreen;
 }
 
 
-#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)
+Ctx *ctx_new_sdl (int width, int height)
+{
+#if CTX_RASTERIZER
 
+  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
+  CtxTiled *tiled = (void*)sdl;
 
+  ctx_get_contents ("file:///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 (tiled->ctx);
+     free (sdl);
+     return NULL;
+  }
+#if CTX_BABL
+  babl_init ();
+#endif
+  sdl->fullscreen = 0;
 
-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 ctx_ev_src_mice = {
-  NULL,
-  (void*)mice_has_event,
-  (void*)mice_get_event,
-  (void*)mice_destroy,
-  mice_get_fd,
-  mice_set_coord
-};
+  ctx_show_fps = getenv ("CTX_SHOW_FPS")!=NULL;
 
-typedef struct Mice
-{
-  int     fd;
-  double  x;
-  double  y;
-  int     button;
-  int     prev_state;
-} Mice;
+  ctx_sdl_events = 1;
+  sdl->texture = SDL_CreateTexture (sdl->renderer,
+        SDL_PIXELFORMAT_ABGR8888,
+        SDL_TEXTUREACCESS_STREAMING,
+        width, height);
 
-Mice *_mrg_evsrc_coord = NULL;
-static int _ctx_mice_fd = 0;
+  SDL_StartTextInput ();
+  SDL_EnableScreenSaver ();
 
-void _mmm_get_coords (Ctx *ctx, double *x, double *y)
-{
-  if (!_mrg_evsrc_coord)
-    return;
-  if (x)
-    *x = _mrg_evsrc_coord->x;
-  if (y)
-    *y = _mrg_evsrc_coord->y;
-}
+  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);
 
-static Mice  mice;
-static Mice* mrg_mice_this = &mice;
+  tiled->pixels = (uint8_t*)malloc (width * height * 4);
 
-static int mmm_evsource_mice_init ()
-{
-  unsigned char reset[]={0xff};
-  /* need to detect which event */
+  ctx_set_size (tiled->ctx,      width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
 
-  mrg_mice_this->prev_state = 0;
-  mrg_mice_this->fd = open ("/dev/input/mice", O_RDONLY | O_NONBLOCK);
-  if (mrg_mice_this->fd == -1)
+  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++)
   {
-    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;
+    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);
   }
-  if (write (mrg_mice_this->fd, reset, 1) == -1)
-  {
-    // might happen if we're a regular user with only read permission
+
+  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]=sdl;\
+    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
   }
-  _ctx_mice_fd = mrg_mice_this->fd;
-  _mrg_evsrc_coord = mrg_mice_this;
-  return 0;
+  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 (tiled->ctx);
+  return tiled->ctx;
+#else
+  return NULL;
+#endif
 }
+#else
 
-static void mice_destroy ()
+int ctx_renderer_is_sdl (Ctx *ctx)
 {
-  if (mrg_mice_this->fd != -1)
-    close (mrg_mice_this->fd);
+  return 0;
 }
+#endif
 
-static int mice_has_event ()
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+typedef struct CtxTermCell
 {
-  struct timeval tv;
-  int retval;
+  char    utf8[5];
+  uint8_t fg[4];
+  uint8_t bg[4];
 
-  if (mrg_mice_this->fd == -1)
-    return 0;
+  char    prev_utf8[5];
+  uint8_t prev_fg[4];
+  uint8_t prev_bg[4];
+} CtxTermCell;
 
-  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;
+typedef struct CtxTermLine
+{
+  CtxTermCell *cells;
+  int maxcol;
+  int size;
+} CtxTermLine;
+
+typedef enum
+{
+  CTX_TERM_ASCII,
+  CTX_TERM_ASCII_MONO,
+  CTX_TERM_SEXTANT,
+  CTX_TERM_BRAILLE_MONO,
+  CTX_TERM_BRAILLE,
+  CTX_TERM_QUARTER,
+} CtxTermMode;
+
+typedef struct _CtxTerm CtxTerm;
+struct _CtxTerm
+{
+   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      *host;
+   CtxList  *lines;
+   CtxTermMode mode;
+};
+
+static int ctx_term_ch = 8;
+static int ctx_term_cw = 8;
+
+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)
+  {
+    ctx_list_append (&term->lines, calloc (sizeof (CtxTermLine), 1));
+  }
+  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);
 }
 
-static char *mice_get_event ()
+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)
 {
-  const char *ret = "mouse-motion";
-  double relx, rely;
-  signed char buf[3];
-  int n_read = 0;
-  CtxFb *fb = ctx_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];
+  return r * 256 * 256 + g * 256 + b;
+}
 
-  if (relx < 0)
+
+static void ctx_term_set_fg (int red, int green, int blue)
+{
+  long lc = ctx_rgb_to_long (red, green, blue);
+  if (lc == _ctx_curfg)
+    return;
+  _ctx_curfg=lc;
+  if (_ctx_term256 == 0)
   {
-    if (relx > -6)
-    relx = - relx*relx;
-    else
-    relx = -36;
+    printf("\e[38;2;%i;%i;%im", red,green,blue);
   }
   else
   {
-    if (relx < 6)
-    relx = relx*relx;
+    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
-    relx = 36;
+      printf("\e[38;5;%im", 16 + r * 6 * 6 + g * 6  + b);
   }
+}
 
-  if (rely < 0)
+static void ctx_term_set_bg(int red, int green, int blue)
+{
+  long lc = ctx_rgb_to_long (red, green, blue);
+//if (lc == _ctx_curbg)
+//  return;
+  _ctx_curbg=lc;
+  if (_ctx_term256 == 0)
   {
-    if (rely > -6)
-    rely = - rely*rely;
-    else
-    rely = -36;
+    printf("\e[48;2;%i;%i;%im", red,green,blue);
   }
   else
   {
-    if (rely < 6)
-    rely = rely*rely;
+    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[48;5;%im", 16 + 216 + gray);
+    }
     else
-    rely = 36;
+      printf("\e[48;5;%im", 16 + r * 6 * 6 + g * 6  + b);
   }
+}
 
-  mrg_mice_this->x += relx;
-  mrg_mice_this->y += rely;
+static int _ctx_term_force_full = 0;
 
-  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))
+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)
+  {
+    CtxTermLine *line = l->data;
+    for (int col = 1; col <= line->maxcol; col++)
     {
-      if (buf[0] & 1)
-        {
-          ret = "mouse-press";
-        }
+      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)
+      {
+        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
-        {
-          ret = "mouse-release";
-        }
-      button = 1;
+      {
+        // 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);
     }
-  else if (buf[0] & 1)
-  {
-    ret = "mouse-drag";
-    button = 1;
+    if (row != term->rows)
+      printf ("\n\r");
+    row ++;
   }
+  printf ("\e[0m");
+  //printf ("\e[?25h");
+  //
+}
 
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
+// xx
+// xx
+// xx
+//
+
+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 ctx_sqrtf(diff);
+  return diff;
+}
+
+static void ctx_term_output_buf_half (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
+{
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█",
+
+  };
+  for (int row = 0; row < height/2; row++)
     {
-      if (buf[0] & 2)
-        {
-          ret = "mouse-press";
-        }
-      else
+      for (int col = 0; col < width-3; col++)
         {
-          ret = "mouse-release";
+          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]);
         }
-      button = 3;
-    }
-    else if (buf[0] & 2)
-    {
-      ret = "mouse-drag";
-      button = 3;
     }
-  }
+}
 
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
-    {
-      if (buf[0] & 4)
+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)
         {
-          ret = "mouse-press";
+          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
+        else
         {
-          ret = "mouse-release";
+          int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], &rgba[0][0]);
+          if (diff > curdiff)
+          {
+            curdiff = diff;
+            for (int c = 0; c < 3; c++)
+              rgba[1][c] = pixels[noi + c];
+          }
         }
-      button = 2;
-    }
-    else if (buf[0] & 4)
-    {
-      ret = "mouse-drag";
-      button = 2;
-    }
-  }
+      }
+          int  rgbasum[2][4] = {0,};
+          int  sumcount[2];
 
-  mrg_mice_this->prev_state = buf[0];
+          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;
 
-  {
-    char *r = malloc (64);
-    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
-    return r;
-  }
+          for (int y = y0; y < y0 + h; y++)
+            for (int x = x0; x < x0 + w; x++)
+                {
+                  int noi = (y) * stride + (x) * 4;
 
-  return NULL;
-}
+                  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];
+                }
 
-static int mice_get_fd (EvSource *ev_source)
-{
-  return mrg_mice_this->fd;
-}
 
-static void mice_set_coord (EvSource *ev_source, double x, double y)
-{
-  mrg_mice_this->x = x;
-  mrg_mice_this->y = y;
-}
+          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];
+          }
+          }
 
-static EvSource *evsource_mice_new (void)
-{
-  if (mmm_evsource_mice_init () == 0)
-    {
-      mrg_mice_this->x = 0;
-      mrg_mice_this->y = 0;
-      return &ctx_ev_src_mice;
-    }
-  return NULL;
 }
 
-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);
-
-/* kept out of struct to be reachable by atexit */
-static EvSource ctx_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;
 
-static void real_evsource_kb_destroy (int sign)
+static void ctx_term_output_buf_quarter (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-  static int done = 0;
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█"
 
-  if (sign == 0)
-    return;
+  };
+  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 (done)
-    return;
-  done = 1;
+          int pixels_set = 0;
+          for (int y = 0; y < 2; 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]))
 
-  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");
+                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]);
+        }
+    }
 }
 
-static void evsource_kb_destroy (int sign)
-{
-  real_evsource_kb_destroy (-11);
-}
 
-static int evsource_kb_init ()
+static void ctx_term_output_buf_sextant (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-//  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);
+  int stride = width * 4;
 
-  struct termios raw;
-  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+  const char *sextants[]={
+   " 
","🬀","🬁","🬂","🬃","🬄","🬅","🬆","🬇","🬈","🬉","🬊","🬋","🬌","🬍","🬎","🬏","🬐","🬑","🬒","🬓","▌","🬔","🬕","🬖","🬗","🬘","🬙","🬚","🬛","🬜","🬝","🬞","🬟","🬠","🬡","🬢","🬣","🬤","🬥","🬦","🬧","▐","🬨","🬩","🬪","🬫","🬬","🬭","🬮","🬯","🬰","🬱","🬲","🬳","🬴","🬵","🬶","🬷","🬸","🬹","🬺","🬻","█"
+  };
+
+  for (int row = 0; row < height/ctx_term_ch; row++)
     {
-      fprintf (stderr, "error initializing keyboard\n");
-      return -1;
-    }
-  raw = orig_attr;
+      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}};
 
-  cfmakeraw (&raw);
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
 
-  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?
+          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]))
 
-  return 0;
-}
-static int evsource_kb_has_event (void)
-{
-  struct timeval tv;
-  int retval;
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
 
-  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;
+          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]);
+        }
+    }
 }
 
-/* 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"},
-
-  {"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~"},
+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);
 
-  {"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 (!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)
+          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)
           {
-            *ret = &ufb_keycodes[i];
-            return 1;
+            rgba[1][0] = 0;
+            rgba[1][1] = 0;
+            rgba[1][2] = 0;
           }
-      }
-  if (matches != 1 && ret)
-    *ret = NULL;
-  return matches==1?2:matches;
-}
 
-//int is_active (void *host)
-//{
-//        return 1;
-//}
 
-static char *evsource_kb_get_event (void)
-{
-  unsigned char buf[20];
-  int length;
+          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++)
+              {
+                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]))
 
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const MmmKeyCode *match = NULL;
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
 
-        //if (!is_active (ctx_ev_src_kb.priv))
-        //  return NULL;
 
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
+           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]))
           {
-            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");
+            for (int c = 0; c < 4; c ++)
+            {
+              int tmp = rgba[0][c];
+              rgba[0][c] = rgba[1][c];
+              rgba[1][c] = tmp;
+            }
           }
-
-        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
+          if (mono)
           {
-            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);
+            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++)
+              {
+                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 ++; 
                   }
-                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);
+                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;
+
+            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
+            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]);
             }
-              return NULL;
-            default: /* continue */
-              break;
           }
-      }
-    else
-      return strdup("key read eek");
-  return strdup("fail");
+        }
+    }
 }
 
-static int evsource_kb_get_fd (void)
+
+inline static void ctx_term_render (void *ctx,
+                                       CtxCommand *command)
 {
-  return STDIN_FILENO;
+  CtxTerm *term = (void*)ctx;
+  /* directly forward */
+  ctx_process (term->host, &command->entry);
 }
 
-
-static EvSource *evsource_kb_new (void)
+inline static void ctx_term_flush (CtxTerm *term)
 {
-  if (evsource_kb_init() == 0)
+  int width =  term->width;
+  int height = term->height;
+  switch (term->mode)
   {
-    return &ctx_ev_src_kb;
+    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;
   }
-  return NULL;
-}
+#if CTX_BRAILLE_TEXT
+  CtxRasterizer *rasterizer = (CtxRasterizer*)(term->host->renderer);
+  // XXX instead sort and inject along with braille
+  //
 
-static int event_check_pending (CtxFb *fb)
-{
-  CtxTiled *tiled = (void*)fb;
-  int events = 0;
-  for (int i = 0; i < fb->evsource_count; i++)
+  //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)
   {
-    while (evsource_has_event (fb->evsource[i]))
+    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 ++)
     {
-      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);
-      }
+      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);
   }
-  return events;
+
+  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
 }
 
-int ctx_fb_consume_events (Ctx *ctx)
+void ctx_term_free (CtxTerm *term)
 {
-  CtxFb *fb = (void*)ctx->renderer;
-  ctx_fb_show_frame (fb, 0);
-  event_check_pending (fb);
+  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 */
+}
+
+int ctx_renderer_is_term (Ctx *ctx)
+{
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_term_free)
+          return 1;
   return 0;
 }
 
-inline static void ctx_fb_reset (CtxFb *fb)
+float ctx_term_get_cell_width (Ctx *ctx)
 {
-  ctx_fb_show_frame (fb, 1);
+  return ctx_term_cw;
 }
 
-inline static void ctx_fb_flush (CtxFb *fb)
+float ctx_term_get_cell_height (Ctx *ctx)
 {
-  ctx_tiled_flush ((CtxTiled*)fb);
+  return ctx_term_ch;
 }
 
-void ctx_fb_free (CtxFb *fb)
+Ctx *ctx_new_term (int width, int height)
 {
-  if (fb->is_drm)
+  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_MONO;
+  //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")){
+    term->mode = CTX_TERM_BRAILLE_MONO;
+    ctx_term_ch = 4;
+  }
+  else {
+    fprintf (stderr, "recognized values for CTX_TERM_MODE:\n"
+                    " sextant ascii quarter braille\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)
   {
-    ctx_fbdrm_close (fb);
+    width = maxwidth;
+    height = maxheight;
   }
+  if (width > maxwidth) width = maxwidth;
+  if (height > maxheight) height = maxheight;
+  term->ctx = ctx;
+  term->width  = width;
+  term->height = height;
 
-  ioctl (0, KDSETMODE, KD_TEXT);
-  if (system("stty sane")){};
-  ctx_tiled_free ((CtxTiled*)fb);
-  //free (fb);
-#if CTX_BABL
-  babl_exit ();
+  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
+
+
+  return ctx;
 }
 
-//static unsigned char *fb_icc = NULL;
-//static long fb_icc_length = 0;
+#endif
 
-int ctx_renderer_is_fb (Ctx *ctx)
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+typedef struct _CtxTermImg CtxTermImg;
+struct _CtxTermImg
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_fb_free)
-          return 1;
-  return 0;
+   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);
 }
 
-static CtxFb *ctx_fb = NULL;
-static void vt_switch_cb (int sig)
+inline static void ctx_termimg_flush (CtxTermImg *termimg)
 {
-  CtxTiled *tiled = (void*)ctx_fb;
-  if (sig == SIGUSR1)
+  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);
+
+  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)
   {
-    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);
+     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\\");
   }
-  else
-  {
-    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
-    {
-      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;
-      }
-    }
+  free (encoded);
+  
+  fflush (NULL);
+}
+
+void ctx_termimg_free (CtxTermImg *termimg)
+{
+  while (termimg->lines)
+  {
+    free (termimg->lines->data);
+    ctx_list_remove (&termimg->lines, termimg->lines->data);
   }
+  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 */
 }
 
-static int ctx_fb_get_mice_fd (Ctx *ctx)
+int ctx_renderer_is_termimg (Ctx *ctx)
 {
-  //CtxFb *fb = (void*)ctx->renderer;
-  return _ctx_mice_fd;
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_termimg_free)
+          return 1;
+  return 0;
 }
 
-Ctx *ctx_new_fb (int width, int height, int drm)
+Ctx *ctx_new_termimg (int width, int height)
 {
+  Ctx *ctx = ctx_new ();
 #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)
-  {
-    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
-  {
-  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 (stdout, "\e[?1049h");
+  fprintf (stdout, "\e[?25l"); // cursor off
+  CtxTermImg *termimg = (CtxTermImg*)calloc (sizeof (CtxTermImg), 1);
 
-//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);
+  int maxwidth = ctx_terminal_width ();
 
-  if (fb->fb_bits == 16)
-    fb->fb_bits =
-      fb->vinfo.red.length +
-      fb->vinfo.green.length +
-      fb->vinfo.blue.length;
+  int colwidth = maxwidth/ctx_terminal_cols ();
+  maxwidth-=colwidth;
 
-   else if (fb->fb_bits == 8)
+  int maxheight = ctx_terminal_height ();
+  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  = maxwidth;
+    height = maxheight;
   }
+  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
 
-  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 ctx;
+}
 
-#if CTX_BABL
-  babl_init ();
 #endif
 
-  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
+#if CTX_FORMATTER
 
-  tiled->ctx      = ctx_new ();
-  tiled->ctx_copy = ctx_new ();
-  tiled->width    = width;
-  tiled->height   = height;
+typedef struct _CtxFormatter  CtxFormatter;
+struct _CtxFormatter 
+{
+  void *target; // FILE
+  int   longform;
+  int   indent;
 
-  ctx_set_renderer (tiled->ctx, fb);
-  ctx_set_renderer (tiled->ctx_copy, fb);
-  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
+  void (*add_str)(CtxFormatter *formatter, const char *str, int len);
+};
 
-  ctx_set_size (tiled->ctx, width, height);
-  ctx_set_size (tiled->ctx_copy, width, height);
+static inline void ctx_formatter_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  formatter->add_str (formatter, str, len);
+}
 
-  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 inline void ctx_formatter_addstrf (CtxFormatter *formatter, const char *format, ...)
+{
+   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_formatter_addstr (formatter, buffer, -1);
+   free (buffer);
+}
 
-  for (int i = 0; i < _ctx_max_threads; i++)
+static void _ctx_stream_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  if (!str || len == 0)
   {
-    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);
+    return;
   }
+  if (len < 0) len = strlen (str);
+  fwrite (str, len, 1, (FILE*)formatter->target);
+}
 
-  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*)ctx_tiled_render_fun, args);\
+void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  if (!str || len == 0)
+  {
+    return;
   }
-  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
+  if (len < 0) len = strlen (str);
+  ctx_string_append_data ((CtxString*)(formatter->target), str, len);
+}
 
-  ctx_flush (tiled->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;
-  }
+static void _ctx_print_endcmd (CtxFormatter *formatter)
+{
+  if (formatter->longform)
+    {
+      ctx_formatter_addstr (formatter, ");\n", 3);
+    }
+}
 
-  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;
-  }
+static void _ctx_indent (CtxFormatter *formatter)
+{
+  for (int i = 0; i < formatter->indent; i++)
+    { ctx_formatter_addstr (formatter, "  ", 2);
+    }
+}
 
-  fb->vt = st.v_active;
+const char *_ctx_code_to_name (int code)
+{
+      switch (code)
+        {
+          case CTX_REL_LINE_TO_X4:           return "relLinetoX4"; break;
+          case CTX_REL_LINE_TO_REL_CURVE_TO: return "relLineToRelCurveTo"; break;
+          case CTX_REL_CURVE_TO_REL_LINE_TO: return "relCurveToRelLineTo"; break;
+          case CTX_REL_CURVE_TO_REL_MOVE_TO: return "relCurveToRelMoveTo"; break;
+          case CTX_REL_LINE_TO_X2:           return "relLineToX2"; break;
+          case CTX_MOVE_TO_REL_LINE_TO:      return "moveToRelLineTo"; break;
+          case CTX_REL_LINE_TO_REL_MOVE_TO:  return "relLineToRelMoveTo"; break;
+          case CTX_FILL_MOVE_TO:             return "fillMoveTo"; break;
+          case CTX_REL_QUAD_TO_REL_QUAD_TO:  return "relQuadToRelQuadTo"; break;
+          case CTX_REL_QUAD_TO_S16:          return "relQuadToS16"; break;
 
-  struct vt_mode mode;
-  mode.mode   = VT_PROCESS;
-  mode.relsig = SIGUSR1;
-  mode.acqsig = SIGUSR2;
-  if (ioctl (0, VT_SETMODE, &mode) < 0)
+          case CTX_SET_KEY:              return "setParam"; break;
+          case CTX_COLOR:                return "setColor"; break;
+          case CTX_DEFINE_GLYPH:         return "defineGlyph"; break;
+          case CTX_KERNING_PAIR:         return "kerningPair"; break;
+          case CTX_SET_PIXEL:            return "setPixel"; break;
+          case CTX_GLOBAL_ALPHA:         return "globalAlpha"; break;
+          case CTX_TEXT:                 return "text"; break;
+          case CTX_STROKE_TEXT:          return "strokeText"; break;
+          case CTX_SAVE:                 return "save"; break;
+          case CTX_RESTORE:              return "restore"; break;
+          case CTX_STROKE_SOURCE:        return "strokeSource"; break;
+          case CTX_NEW_PAGE:             return "newPage"; break;
+          case CTX_START_GROUP:          return "startGroup"; break;
+          case CTX_END_GROUP:            return "endGroup"; break;
+          case CTX_RECTANGLE:            return "rectangle"; break;
+          case CTX_ROUND_RECTANGLE:      return "roundRectangle"; break;
+          case CTX_LINEAR_GRADIENT:      return "linearGradient"; break;
+          case CTX_RADIAL_GRADIENT:      return "radialGradient"; break;
+          case CTX_GRADIENT_STOP:        return "gradientAddStop"; break;
+          case CTX_VIEW_BOX:             return "viewBox"; break;
+          case CTX_MOVE_TO:              return "moveTo"; break;
+          case CTX_LINE_TO:              return "lineTo"; break;
+          case CTX_BEGIN_PATH:           return "beginPath"; break;
+          case CTX_REL_MOVE_TO:          return "relMoveTo"; break;
+          case CTX_REL_LINE_TO:          return "relLineTo"; break;
+          case CTX_FILL:                 return "fill"; break;
+          case CTX_EXIT:                 return "exit"; break;
+          case CTX_APPLY_TRANSFORM:      return "transform"; break;
+          case CTX_SOURCE_TRANSFORM:     return "sourceTransform"; break;
+          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;
+          case CTX_FLUSH:                return "flush"; break;
+          case CTX_RESET:                return "reset"; break;
+          case CTX_FONT:                 return "font"; break;
+          case CTX_STROKE:               return "stroke"; break;
+          case CTX_CLIP:                 return "clip"; break;
+          case CTX_ARC:                  return "arc"; break;
+          case CTX_SCALE:                return "scale"; break;
+          case CTX_TRANSLATE:            return "translate"; break;
+          case CTX_ROTATE:               return "rotate"; break;
+          case CTX_ARC_TO:               return "arcTo"; break;
+          case CTX_CURVE_TO:             return "curveTo"; break;
+          case CTX_REL_CURVE_TO:         return "relCurveTo"; break;
+          case CTX_REL_QUAD_TO:          return "relQuadTo"; break;
+          case CTX_QUAD_TO:              return "quadTo"; break;
+          case CTX_SMOOTH_TO:            return "smoothTo"; break;
+          case CTX_REL_SMOOTH_TO:        return "relSmoothTo"; break;
+          case CTX_SMOOTHQ_TO:           return "smoothqTo"; break;
+          case CTX_REL_SMOOTHQ_TO:       return "relSmoothqTo"; break;
+          case CTX_HOR_LINE_TO:          return "horLineTo"; break;
+          case CTX_VER_LINE_TO:          return "verLineTo"; break;
+          case CTX_REL_HOR_LINE_TO:      return "relHorLineTo"; break;
+          case CTX_REL_VER_LINE_TO:      return "relVerLineTo"; break;
+          case CTX_COMPOSITING_MODE:     return "compositingMode"; break;
+          case CTX_BLEND_MODE:           return "blendMode"; break;
+          case CTX_TEXT_ALIGN:           return "textAlign"; break;
+          case CTX_TEXT_BASELINE:        return "textBaseline"; break;
+          case CTX_TEXT_DIRECTION:       return "textDirection"; break;
+          case CTX_FONT_SIZE:            return "fontSize"; break;
+          case CTX_MITER_LIMIT:          return "miterLimit"; break;
+          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;
+        }
+      return NULL;
+}
+
+static void _ctx_print_name (CtxFormatter *formatter, int code)
+{
+#define CTX_VERBOSE_NAMES 1
+#if CTX_VERBOSE_NAMES
+  if (formatter->longform)
+    {
+      const char *name = NULL;
+      _ctx_indent (formatter);
+      //switch ((CtxCode)code)
+      name = _ctx_code_to_name (code);
+      if (name)
+        {
+          ctx_formatter_addstr (formatter, name, -1);
+          ctx_formatter_addstr (formatter, " (", 2);
+          if (code == CTX_SAVE)
+            { formatter->indent ++; }
+          else if (code == CTX_RESTORE)
+            { formatter->indent --; }
+          return;
+        }
+    }
+#endif
   {
-    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
-    return NULL;
+    char name[3];
+    name[0]=CTX_SET_KEY;
+    name[2]='\0';
+    switch (code)
+      {
+        case CTX_GLOBAL_ALPHA:      name[1]='a'; break;
+        case CTX_COMPOSITING_MODE:  name[1]='m'; break;
+        case CTX_BLEND_MODE:        name[1]='B'; break;
+        case CTX_TEXT_ALIGN:        name[1]='t'; break;
+        case CTX_TEXT_BASELINE:     name[1]='b'; break;
+        case CTX_TEXT_DIRECTION:    name[1]='d'; break;
+        case CTX_FONT_SIZE:         name[1]='f'; break;
+        case CTX_MITER_LIMIT:       name[1]='l'; break;
+        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;
+        case CTX_SHADOW_OFFSET_Y:   name[1]='y'; break;
+        case CTX_FILL_RULE:         name[1]='r'; break;
+        default:
+          name[0] = code;
+          name[1] = 0;
+          break;
+      }
+    ctx_formatter_addstr (formatter, name, -1);
+    if (formatter->longform)
+      ctx_formatter_addstr (formatter, " (", 2);
+    else
+      ctx_formatter_addstr (formatter, " ", 1);
   }
+}
+
+static void
+ctx_print_entry_enum (CtxFormatter *formatter, CtxEntry *entry, int args)
+{
+  _ctx_print_name (formatter, entry->code);
+  for (int i = 0; i <  args; i ++)
+    {
+      int val = ctx_arg_u8 (i);
+      if (i>0)
+        { 
+          ctx_formatter_addstr (formatter, " ", 1);
+        }
+#if CTX_VERBOSE_NAMES
+      if (formatter->longform)
+        {
+          const char *str = NULL;
+          switch (entry->code)
+            {
+              case CTX_TEXT_BASELINE:
+                switch (val)
+                  {
+                    case CTX_TEXT_BASELINE_ALPHABETIC: str = "alphabetic"; break;
+                    case CTX_TEXT_BASELINE_TOP:        str = "top";        break;
+                    case CTX_TEXT_BASELINE_BOTTOM:     str = "bottom";     break;
+                    case CTX_TEXT_BASELINE_HANGING:    str = "hanging";    break;
+                    case CTX_TEXT_BASELINE_MIDDLE:     str = "middle";     break;
+                    case CTX_TEXT_BASELINE_IDEOGRAPHIC:str = "ideographic";break;
+                  }
+                break;
+              case CTX_TEXT_ALIGN:
+                switch (val)
+                  {
+                    case CTX_TEXT_ALIGN_LEFT:   str = "left"; break;
+                    case CTX_TEXT_ALIGN_RIGHT:  str = "right"; break;
+                    case CTX_TEXT_ALIGN_START:  str = "start"; break;
+                    case CTX_TEXT_ALIGN_END:    str = "end"; break;
+                    case CTX_TEXT_ALIGN_CENTER: str = "center"; break;
+                  }
+                break;
+              case CTX_LINE_CAP:
+                switch (val)
+                  {
+                    case CTX_CAP_NONE:   str = "none"; break;
+                    case CTX_CAP_ROUND:  str = "round"; break;
+                    case CTX_CAP_SQUARE: str = "square"; break;
+                  }
+                break;
+              case CTX_LINE_JOIN:
+                switch (val)
+                  {
+                    case CTX_JOIN_MITER: str = "miter"; break;
+                    case CTX_JOIN_ROUND: str = "round"; break;
+                    case CTX_JOIN_BEVEL: str = "bevel"; break;
+                  }
+                break;
+              case CTX_FILL_RULE:
+                switch (val)
+                  {
+                    case CTX_FILL_RULE_WINDING:  str = "winding"; break;
+                    case CTX_FILL_RULE_EVEN_ODD: str = "evenodd"; break;
+                  }
+                break;
+              case CTX_BLEND_MODE:
+                val = ctx_arg_u32 (i);
+                switch (val)
+                  {
+            case CTX_BLEND_NORMAL:      str = "normal"; break;
+            case CTX_BLEND_MULTIPLY:    str = "multiply"; break;
+            case CTX_BLEND_SCREEN:      str = "screen"; break;
+            case CTX_BLEND_OVERLAY:     str = "overlay"; break;
+            case CTX_BLEND_DARKEN:      str = "darken"; break;
+            case CTX_BLEND_LIGHTEN:     str = "lighten"; break;
+            case CTX_BLEND_COLOR_DODGE: str = "colorDodge"; break;
+            case CTX_BLEND_COLOR_BURN:  str = "colorBurn"; break;
+            case CTX_BLEND_HARD_LIGHT:  str = "hardLight"; break;
+            case CTX_BLEND_SOFT_LIGHT:  str = "softLight"; break;
+            case CTX_BLEND_DIFFERENCE:  str = "difference"; break;
+            case CTX_BLEND_EXCLUSION:   str = "exclusion"; break;
+            case CTX_BLEND_HUE:         str = "hue"; break;
+            case CTX_BLEND_SATURATION:  str = "saturation"; break;
+            case CTX_BLEND_COLOR:       str = "color"; break; 
+            case CTX_BLEND_LUMINOSITY:  str = "luminosity"; break;
+                  }
+                break;
+              case CTX_COMPOSITING_MODE:
+                val = ctx_arg_u32 (i);
+                switch (val)
+                  {
+              case CTX_COMPOSITE_SOURCE_OVER: str = "sourceOver"; break;
+              case CTX_COMPOSITE_COPY: str = "copy"; break;
+              case CTX_COMPOSITE_CLEAR: str = "clear"; break;
+              case CTX_COMPOSITE_SOURCE_IN: str = "sourceIn"; break;
+              case CTX_COMPOSITE_SOURCE_OUT: str = "sourceOut"; break;
+              case CTX_COMPOSITE_SOURCE_ATOP: str = "sourceAtop"; break;
+              case CTX_COMPOSITE_DESTINATION: str = "destination"; break;
+              case CTX_COMPOSITE_DESTINATION_OVER: str = "destinationOver"; break;
+              case CTX_COMPOSITE_DESTINATION_IN: str = "destinationIn"; break;
+              case CTX_COMPOSITE_DESTINATION_OUT: str = "destinationOut"; break;
+              case CTX_COMPOSITE_DESTINATION_ATOP: str = "destinationAtop"; break;
+              case CTX_COMPOSITE_XOR: str = "xor"; break;
+                  }
 
-  return tiled->ctx;
-#else
-  return NULL;
+               break;
+            }
+          if (str)
+            {
+              ctx_formatter_addstr (formatter, str, -1);
+            }
+          else
+            {
+              ctx_formatter_addstrf (formatter, "%i", val);
+            }
+        }
+      else
 #endif
+        {
+          ctx_formatter_addstrf (formatter, "%i", val);
+        }
+    }
+  _ctx_print_endcmd (formatter);
 }
-#else
 
-int ctx_renderer_is_fb (Ctx *ctx)
+
+static void
+ctx_print_a85 (CtxFormatter *formatter, uint8_t *data, int length)
 {
-  return 0;
+  char *tmp = (char*)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);
 }
-#endif
-#endif
 
-#if CTX_SDL
-
-/**/
+static void
+ctx_print_yenc (CtxFormatter *formatter, uint8_t *data, int length)
+{
+  char *tmp = (char*)malloc (length * 2 + 2);// worst case scenario
+  int enclength = ctx_yenc ((char*)data, tmp, length);
+  data[enclength]=0;
+  ctx_formatter_addstr (formatter, " =", 2);
+  ctx_formatter_addstr (formatter, tmp, enclength);
+  ctx_formatter_addstr (formatter, "=y ", 2);
+  free (tmp);
+}
 
-typedef struct _CtxSDL CtxSDL;
-struct _CtxSDL
+static void
+ctx_print_escaped_string (CtxFormatter *formatter, const char *string)
 {
-   CtxTiled  tiled;
-   /* where we diverge from fb*/
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
-   int           lshift;
-   int           rshift;
-
-   SDL_Window   *window;
-   SDL_Renderer *renderer;
-   SDL_Texture  *texture;
-
-// cnd_t  cond;
-// mtx_t  mtx;
-   int           fullscreen;
-};
-
-#include "stb_image_write.h"
+  if (!string) { return; }
+  for (int i = 0; string[i]; i++)
+    {
+      switch (string[i])
+        {
+          case '"':
+            ctx_formatter_addstr (formatter, "\\\"", 2);
+            break;
+          case '\\':
+            ctx_formatter_addstr (formatter, "\\\\", 2);
+            break;
+          case '\n':
+            ctx_formatter_addstr (formatter, "\\n", 2);
+            break;
+          default:
+            ctx_formatter_addstr (formatter, &string[i], 1);
+        }
+    }
+}
 
-void ctx_screenshot (Ctx *ctx, const char *output_path)
+static void
+ctx_print_float (CtxFormatter *formatter, float val)
 {
-#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
-  // 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
+  char temp[128];
+  sprintf (temp, "%0.3f", val);
+  int j;
+  for (j = 0; temp[j]; j++)
+    if (j == ',') { temp[j] = '.'; }
+  j--;
+  if (j>0)
+    while (temp[j] == '0')
+      {
+        temp[j]=0;
+        j--;
+      }
+  if (temp[j]=='.')
+    { temp[j]='\0'; }
+  ctx_formatter_addstr (formatter, temp, -1);
+}
 
-  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
+static void
+ctx_print_int (CtxFormatter *formatter, int val)
+{
+  ctx_formatter_addstrf (formatter, "%i", val);
+}
 
-#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 void
+ctx_print_entry (CtxFormatter *formatter, CtxEntry *entry, int args)
+{
+  _ctx_print_name (formatter, entry->code);
+  for (int i = 0; i <  args; i ++)
+    {
+      float val = ctx_arg_float (i);
+      if (i>0 && val >= 0.0f)
+        {
+          if (formatter->longform)
+            {
+              ctx_formatter_addstr (formatter, ", ", 2);
+            }
+          else
+            {
+              if (val >= 0.0f)
+                ctx_formatter_addstr (formatter, " ", 1);
+            }
+        }
+      ctx_print_float (formatter, val);
+    }
+  _ctx_print_endcmd (formatter);
 }
 
-void ctx_sdl_set_title (void *self, const char *new_title)
+static void
+ctx_print_glyph (CtxFormatter *formatter, CtxEntry *entry, int args)
 {
-   CtxSDL *sdl = self;
-   SDL_SetWindowTitle (sdl->window, new_title);
+  _ctx_print_name (formatter, entry->code);
+  ctx_formatter_addstrf (formatter, "%i", entry->data.u32[0]);
+  _ctx_print_endcmd (formatter);
 }
 
-static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
+static void
+ctx_formatter_process (void *user_data, CtxCommand *c);
+
+
+static void
+ctx_formatter_process (void *user_data, CtxCommand *c)
 {
-  CtxTiled *tiled = &sdl->tiled;
-  if (tiled->shown_cursor != tiled->ctx->cursor)
-  {
-    tiled->shown_cursor = tiled->ctx->cursor;
-    SDL_Cursor *new_cursor =  NULL;
-    switch (tiled->shown_cursor)
+  CtxEntry *entry = &c->entry;
+  CtxFormatter *formatter = (CtxFormatter*)user_data;
+
+  switch (entry->code)
+  //switch ((CtxCode)(entry->code))
     {
-      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);
+      case CTX_GLYPH:
+        ctx_print_glyph (formatter, entry, 1);
         break;
-      case CTX_CURSOR_RESIZE_NE:
-      case CTX_CURSOR_RESIZE_SW:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
+      case CTX_LINE_TO:
+      case CTX_REL_LINE_TO:
+      case CTX_SCALE:
+      case CTX_TRANSLATE:
+      case CTX_MOVE_TO:
+      case CTX_REL_MOVE_TO:
+      case CTX_SMOOTHQ_TO:
+      case CTX_REL_SMOOTHQ_TO:
+        ctx_print_entry (formatter, entry, 2);
         break;
-      case CTX_CURSOR_RESIZE_NW:
-      case CTX_CURSOR_RESIZE_SE:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
+      case CTX_TEXTURE:
+        _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;
-    }
-    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);
-    }
-  }
-
-  if (tiled->shown_frame == tiled->render_frame)
-  {
-    return;
-  }
 
-  if (block)
-  {
-    int count = 0;
-    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-    {
-      usleep (50);
-      count ++;
-      if (count > 2000)
-      {
-        tiled->shown_frame = tiled->render_frame;
-        return;
-      }
-    }
-  }
-  else
-  {
-    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-      return;
-  }
+      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);
 
-  if (tiled->min_row == 100)
-  {
-  }
-  else
-  {
+        uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
 #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;
-}
-
-static const char *ctx_sdl_keysym_to_name (unsigned int sym, int *r_keycode)
-{
-  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;
-}
+        int stride = ctx_pixel_format_get_stride ((CtxPixelFormat)c->define_texture.format, 
c->define_texture.width);
+        int data_len = stride * c->define_texture.height;
+        if (c->define_texture.format == CTX_FORMAT_YUV420)
+          data_len = c->define_texture.height * c->define_texture.width +
+                       2*(c->define_texture.height/2) * (c->define_texture.width/2);
+        //fprintf (stderr, "encoding %i bytes\n", c->define_texture.height *stride);
+        //ctx_print_a85 (formatter, pixel_data, c->define_texture.height * stride);
+        ctx_print_yenc (formatter, pixel_data, data_len);
+#else
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, pixel_data);
+        ctx_formatter_addstrf (formatter, "\" ");
 
-int ctx_sdl_consume_events (Ctx *ctx)
-{
-  CtxTiled *tiled = (void*)ctx->renderer;
-  CtxSDL *sdl = (void*)ctx->renderer;
-  SDL_Event event;
-  int got_events = 0;
+#endif
 
-  ctx_sdl_show_frame (sdl, 0);
+        _ctx_print_endcmd (formatter);
+        }
+        break;
 
-  while (SDL_PollEvent (&event))
-  {
-    got_events ++;
-    switch (event.type)
-    {
-      case SDL_MOUSEBUTTONDOWN:
-        SDL_CaptureMouse (1);
-        ctx_pointer_press (ctx, event.button.x, event.button.y, event.button.button, 0);
+      case CTX_REL_ARC_TO:
+      case CTX_ARC_TO:
+      case CTX_ROUND_RECTANGLE:
+        ctx_print_entry (formatter, entry, 5);
         break;
-      case SDL_MOUSEBUTTONUP:
-        SDL_CaptureMouse (0);
-        ctx_pointer_release (ctx, event.button.x, event.button.y, event.button.button, 0);
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_ARC:
+      case CTX_RADIAL_GRADIENT:
+      case CTX_APPLY_TRANSFORM:
+      case CTX_SOURCE_TRANSFORM:
+        ctx_print_entry (formatter, entry, 6);
         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);
+      case CTX_QUAD_TO:
+      case CTX_RECTANGLE:
+      case CTX_REL_QUAD_TO:
+      case CTX_LINEAR_GRADIENT:
+      case CTX_VIEW_BOX:
+      case CTX_SMOOTH_TO:
+      case CTX_REL_SMOOTH_TO:
+        ctx_print_entry (formatter, entry, 4);
         break;
-      case SDL_FINGERMOTION:
-        ctx_pointer_motion (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
-            (event.tfinger.fingerId%10) + 4, 0);
+      case CTX_FONT_SIZE:
+      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:
+      case CTX_SHADOW_OFFSET_Y:
+      case CTX_VER_LINE_TO:
+      case CTX_HOR_LINE_TO:
+      case CTX_REL_VER_LINE_TO:
+      case CTX_REL_HOR_LINE_TO:
+        ctx_print_entry (formatter, entry, 1);
         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
+#if 0
+      case CTX_SET:
+        _ctx_print_name (formatter, entry->code);
+        switch (c->set.key_hash)
         {
-          ctx_pointer_press (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height, 
-          (event.tfinger.fingerId%10) + 4, 0);
-        }
+           case CTX_x: ctx_formatter_addstrf (formatter, " 'x' "); break;
+           case CTX_y: ctx_formatter_addstrf (formatter, " 'y' "); break;
+           case CTX_width: ctx_formatter_addstrf (formatter, " width "); break;
+           case CTX_height: ctx_formatter_addstrf (formatter, " height "); break;
+           default:
+             ctx_formatter_addstrf (formatter, " %d ", c->set.key_hash);
         }
-        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) )
-           )
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, (char*)c->set.utf8);
+        ctx_formatter_addstrf (formatter, "\"");
+        _ctx_print_endcmd (formatter);
+        break;
+#endif
+      case CTX_COLOR:
+        if (formatter->longform ||  1)
           {
-            const char *name = event.text.text;
-            int keycode = 0;
-            if (!strcmp (name, " ") ) { name = "space"; }
-            if (name[0] && name[1] == 0)
+            _ctx_indent (formatter);
+            int model = (int) c->set_color.model;
+            const char *suffix="";
+            if (model & 512)
             {
-              keycode = name[0];
-              keycode = toupper (keycode);
-              switch (keycode)
+              model = model & 511;
+              suffix = "S";
+            }
+            switch (model)
               {
-                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;
+                case CTX_GRAY:
+                  ctx_formatter_addstrf (formatter, "gray%s ", suffix);
+                  ctx_print_float (formatter, c->graya.g);
+                  break;
+                case CTX_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);
+                  break;
+                case CTX_RGBA:
+                  if (c->rgba.a != 1.0)
+                  {
+                    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);
+                    ctx_formatter_addstrf (formatter, " ");
+                    ctx_print_float (formatter, c->rgba.b);
+                    ctx_formatter_addstrf (formatter, " ");
+                    ctx_print_float (formatter, c->rgba.a);
+                    break;
+                  }
+                  /* FALLTHROUGH */
+                case CTX_RGB:
+                  if (c->rgba.r == c->rgba.g && c->rgba.g == c->rgba.b)
+                  {
+                    ctx_formatter_addstrf (formatter, "gray%s ", suffix);
+                    ctx_print_float (formatter, c->rgba.r);
+                    ctx_formatter_addstrf (formatter, " ");
+                    break;
+                  }
+                  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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.a);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.a);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  break;
+                case CTX_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);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.a);
+                  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
+        else
           {
-            sdl->key_repeat ++;
+            ctx_print_entry (formatter, entry, 1);
           }
-          switch (event.key.keysym.sym)
+        break;
+      case CTX_SET_RGBA_U8:
+        if (formatter->longform)
           {
-            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;
+            _ctx_indent (formatter);
+            ctx_formatter_addstrf (formatter, "rgba (");
           }
-          if (sdl->lshift | sdl->rshift | sdl->lctrl | sdl->lalt | sdl->rctrl)
+        else
           {
-            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;
+            ctx_formatter_addstrf (formatter, "rgba (");
           }
-          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))
+        for (int c = 0; c < 4; c++)
           {
-            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"))
+            if (c)
               {
-               ctx_key_press (ctx, keycode, name, 0);
+                if (formatter->longform)
+                  ctx_formatter_addstrf (formatter, ", ");
+                else
+                  ctx_formatter_addstrf (formatter, " ");
               }
+            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) );
           }
-          else
-          {
+        _ctx_print_endcmd (formatter);
+        break;
+      case CTX_SET_PIXEL:
 #if 0
-             ctx_key_press (ctx, 0, buf, 0);
+        ctx_set_pixel_u8 (d_ctx,
+                          ctx_arg_u16 (2), ctx_arg_u16 (3),
+                          ctx_arg_u8 (0),
+                          ctx_arg_u8 (1),
+                          ctx_arg_u8 (2),
+                          ctx_arg_u8 (3) );
 #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;
-           }
-
+      case CTX_FILL:
+      case CTX_RESET:
+      case CTX_STROKE:
+      case CTX_IDENTITY:
+      case CTX_CLIP:
+      case CTX_BEGIN_PATH:
+      case CTX_CLOSE_PATH:
+      case CTX_SAVE:
+      case CTX_PRESERVE:
+      case CTX_START_GROUP:
+      case CTX_NEW_PAGE:
+      case CTX_END_GROUP:
+      case CTX_RESTORE:
+      case CTX_STROKE_SOURCE:
+        ctx_print_entry (formatter, entry, 0);
+        break;
+      case CTX_TEXT_ALIGN:
+      case CTX_TEXT_BASELINE:
+      case CTX_TEXT_DIRECTION:
+      case CTX_FILL_RULE:
+      case CTX_LINE_CAP:
+      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:
+        _ctx_print_name (formatter, entry->code);
+        for (int c = 0; c < 4; c++)
           {
-            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;
+            if (c)
+              ctx_formatter_addstrf (formatter, " ");
+            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (4+c) ) );
           }
-
-           int keycode;
-           const char *name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
-           ctx_key_up (ctx, keycode, name, 0);
-        }
+        _ctx_print_endcmd (formatter);
         break;
-      case SDL_QUIT:
-        ctx_quit (ctx);
+      case CTX_TEXT:
+      case CTX_STROKE_TEXT:
+      case CTX_FONT:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, ctx_arg_string() );
+        ctx_formatter_addstrf (formatter, "\"");
+        _ctx_print_endcmd (formatter);
         break;
-      case SDL_WINDOWEVENT:
-        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
+      case CTX_CONT:
+      case CTX_EDGE:
+      case CTX_DATA:
+      case CTX_DATA_REV:
+      case CTX_FLUSH:
+        break;
+      case CTX_KERNING_PAIR:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
         {
-          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);
+           uint8_t utf8[16];
+           utf8[ctx_unichar_to_utf8 (c->kern.glyph_before, utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\", \"");
+           utf8[ctx_unichar_to_utf8 (c->kern.glyph_after, utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\"");
+           sprintf ((char*)utf8, ", %f", c->kern.amount / 256.0);
+           ctx_print_escaped_string (formatter, (char*)utf8);
+        }
+        _ctx_print_endcmd (formatter);
+        break;
 
-          tiled->width  = width;
-          tiled->height = height;
-          ctx_set_size (tiled->ctx, width, height);
-          ctx_set_size (tiled->ctx_copy, width, height);
+      case CTX_DEFINE_GLYPH:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        {
+           uint8_t utf8[16];
+           utf8[ctx_unichar_to_utf8 (entry->data.u32[0], utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\"");
+           sprintf ((char*)utf8, ", %f", entry->data.u32[1]/256.0);
+           ctx_print_escaped_string (formatter, (char*)utf8);
         }
+        _ctx_print_endcmd (formatter);
         break;
     }
+}
+
+void
+ctx_render_stream (Ctx *ctx, FILE *stream, int longform)
+{
+  CtxIterator iterator;
+  CtxFormatter formatter;
+  formatter.target= stream;
+  formatter.longform = longform;
+  formatter.indent = 0;
+  formatter.add_str = _ctx_stream_addstr;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_formatter_process (&formatter, command); }
+  fprintf (stream, "\n");
+}
+
+char *
+ctx_render_string (Ctx *ctx, int longform, int *retlen)
+{
+  CtxString *string = ctx_string_new ("");
+  CtxIterator iterator;
+  CtxFormatter formatter;
+  formatter.target= string;
+  formatter.longform = longform;
+  formatter.indent = 0;
+  formatter.add_str = _ctx_string_addstr;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_formatter_process (&formatter, command); }
+  char *ret = string->str;
+  if (retlen)
+    *retlen = string->length;
+  ctx_string_free (string, 0);
+  return ret;
+}
+
+
+#endif
+
+#if CTX_EVENTS
+int ctx_width (Ctx *ctx)
+{
+  return ctx->events.width;
+}
+int ctx_height (Ctx *ctx)
+{
+  return ctx->events.height;
+}
+#else
+int ctx_width (Ctx *ctx)
+{
+  return 512;
+}
+int ctx_height (Ctx *ctx)
+{
+  return 384;
+}
+#endif
+
+int ctx_rev (Ctx *ctx)
+{
+  return ctx->rev;
+}
+
+CtxState *ctx_get_state (Ctx *ctx)
+{
+  return &ctx->state;
+}
+
+void ctx_dirty_rect (Ctx *ctx, int *x, int *y, int *width, int *height)
+{
+  if ( (ctx->state.min_x > ctx->state.max_x) ||
+       (ctx->state.min_y > ctx->state.max_y) )
+    {
+      if (x) { *x = 0; }
+      if (y) { *y = 0; }
+      if (width) { *width = 0; }
+      if (height) { *height = 0; }
+      return;
+    }
+  if (ctx->state.min_x < 0)
+    { ctx->state.min_x = 0; }
+  if (ctx->state.min_y < 0)
+    { ctx->state.min_y = 0; }
+  if (x) { *x = ctx->state.min_x; }
+  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; }
+}
+
+#if CTX_CURRENT_PATH
+CtxIterator *
+ctx_current_path (Ctx *ctx)
+{
+  CtxIterator *iterator = &ctx->current_path_iterator;
+  ctx_iterator_init (iterator, &ctx->current_path, 0, CTX_ITERATOR_EXPAND_BITPACK);
+  return iterator;
+}
+
+void
+ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
+{
+  float minx = 50000.0;
+  float miny = 50000.0;
+  float maxx = -50000.0;
+  float maxy = -50000.0;
+  float x = 0;
+  float y = 0;
+
+  CtxIterator *iterator = ctx_current_path (ctx);
+  CtxCommand *command;
+
+  while ((command = ctx_iterator_next (iterator)))
+  {
+     int got_coord = 0;
+     switch (command->code)
+     {
+        // XXX missing many curve types
+        case CTX_LINE_TO:
+        case CTX_MOVE_TO:
+          x = command->move_to.x;
+          y = command->move_to.y;
+          got_coord++;
+          break;
+        case CTX_REL_LINE_TO:
+        case CTX_REL_MOVE_TO:
+          x += command->move_to.x;
+          y += command->move_to.y;
+          got_coord++;
+          break;
+        case CTX_CURVE_TO:
+          x = command->curve_to.x;
+          y = command->curve_to.y;
+          got_coord++;
+          break;
+        case CTX_REL_CURVE_TO:
+          x += command->curve_to.x;
+          y += command->curve_to.y;
+          got_coord++;
+          break;
+        case CTX_ARC:
+          minx = ctx_minf (minx, command->arc.x - command->arc.radius);
+          miny = ctx_minf (miny, command->arc.y - command->arc.radius);
+          maxx = ctx_maxf (maxx, command->arc.x + command->arc.radius);
+          maxy = ctx_maxf (maxy, command->arc.y + command->arc.radius);
+
+          break;
+        case CTX_RECTANGLE:
+        case CTX_ROUND_RECTANGLE:
+          x = command->rectangle.x;
+          y = command->rectangle.y;
+          minx = ctx_minf (minx, x);
+          miny = ctx_minf (miny, y);
+          maxx = ctx_maxf (maxx, x);
+          maxy = ctx_maxf (maxy, y);
+
+          x += command->rectangle.width;
+          y += command->rectangle.height;
+          got_coord++;
+          break;
+     }
+    if (got_coord)
+    {
+      minx = ctx_minf (minx, x);
+      miny = ctx_minf (miny, y);
+      maxx = ctx_maxf (maxx, x);
+      maxy = ctx_maxf (maxy, y);
+    }
   }
-  return 1;
+
+  if (ex1) *ex1 = minx;
+  if (ey1) *ey1 = miny;
+  if (ex2) *ex2 = maxx;
+  if (ey2) *ey2 = maxy;
 }
+
 #else
-void ctx_screenshot (Ctx *ctx, const char *path)
+void
+ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
 {
 }
 #endif
 
-#if CTX_SDL
 
-static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
+static inline void
+ctx_gstate_push (CtxState *state)
 {
-  if (text)
-    SDL_SetClipboardText (text);
+  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 char *ctx_sdl_get_clipboard (CtxSDL *sdl)
+static inline void
+ctx_gstate_pop (CtxState *state)
 {
-  return SDL_GetClipboardText ();
+  if (state->gstate_no <= 0)
+    { return; }
+  state->gstate = state->gstate_stack[state->gstate_no-1];
+  state->gstate_no--;
 }
 
-inline static void ctx_sdl_reset (CtxSDL *sdl)
+void
+ctx_close_path (Ctx *ctx)
 {
-  ctx_sdl_show_frame (sdl, 1);
+  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
 }
 
-inline static void ctx_sdl_flush (CtxSDL *sdl)
-{
-  ctx_tiled_flush ((void*)sdl);
-  //CtxTiled *tiled = (void*)sdl;
-}
+int _ctx_is_rasterizer (Ctx *ctx);
 
-void ctx_sdl_free (CtxSDL *sdl)
+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 (sdl->texture)
-  SDL_DestroyTexture (sdl->texture);
-  if (sdl->renderer)
-  SDL_DestroyRenderer (sdl->renderer);
-  if (sdl->window)
-  SDL_DestroyWindow (sdl->window);
-
-  ctx_tiled_free ((CtxTiled*)sdl);
-#if CTX_BABL
-  babl_exit ();
+   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
 }
 
-
-int ctx_renderer_is_sdl (Ctx *ctx)
+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)
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_sdl_free)
-          return 1;
-  return 0;
+   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);
 }
 
-void ctx_sdl_set_fullscreen (Ctx *ctx, int val)
+/* checking if an eid is valid also sets the frame for it
+ */
+static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h)
 {
-  CtxSDL *sdl = (void*)ctx->renderer;
-
-  if (val)
+  ctx = ctx->texture_cache;
+  CtxList *to_remove = NULL;
+  int ret = 0;
+  //fprintf (stderr, "{%i}\n", ctx->frame);
+  for (CtxList *l = ctx->eid_db; l; l = l->next)
   {
-    SDL_SetWindowFullscreen (sdl->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+    CtxEidInfo *eid_info = (CtxEidInfo*)l->data;
+    if (ctx->frame - eid_info->frame >= 2)
+            /* XXX XXX XXX this breaks texture caching since
+             *   it is wrong in some cases where more frames
+             *   have passed?
+             */
+    {
+      ctx_list_prepend (&to_remove, eid_info);
+    }
+    else if (!strcmp (eid_info->eid, eid) &&
+             ctx->frame - eid_info->frame < 2)
+    {
+    //fclose (f);
+      eid_info->frame = ctx->frame;
+      if (w) *w = eid_info->width;
+      if (h) *h = eid_info->height;
+      ret = 1;
+    }
   }
-  else
+  while (to_remove)
   {
-    SDL_SetWindowFullscreen (sdl->window, 0);
+    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);
   }
-  // XXX we're presuming success
-  sdl->fullscreen = val;
-}
-int ctx_sdl_get_fullscreen (Ctx *ctx)
-{
-  CtxSDL *sdl = (void*)ctx->renderer;
-  return sdl->fullscreen;
+  return ret;
 }
 
-
-Ctx *ctx_new_sdl (int width, int height)
+void ctx_texture (Ctx *ctx, const char *eid, float x, float y)
 {
-#if CTX_RASTERIZER
-
-  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
-  CtxTiled *tiled = (void*)sdl;
+  int eid_len = strlen (eid);
+  char ascii[41]="";
+  //fprintf (stderr, "tx %s\n", 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;
+  }
 
-  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
-  if (width <= 0 || height <= 0)
+    //FILE  *f = fopen ("/tmp/l", "a");
+  if (ctx_eid_valid (ctx, eid, 0, 0))
   {
-    width  = 1920;
-    height = 1080;
+    ctx_process_cmd_str_float (ctx, CTX_TEXTURE, eid, x, y);
+    //fprintf (stderr, "setting texture eid %s\n", eid);
   }
-  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)
+  else
   {
-     ctx_free (tiled->ctx);
-     free (sdl);
-     return NULL;
+    //fprintf (stderr, "tried setting invalid texture eid %s\n", eid);
   }
-#if CTX_BABL
-  babl_init ();
-#endif
-  sdl->fullscreen = 0;
-
-  ctx_sdl_events = 1;
-  sdl->texture = SDL_CreateTexture (sdl->renderer,
-        SDL_PIXELFORMAT_ABGR8888,
-        SDL_TEXTUREACCESS_STREAMING,
-        width, height);
+    //fclose (f);
+}
+int
+_ctx_frame (Ctx *ctx)
+{
+   return ctx->frame;
+}
+int
+_ctx_set_frame (Ctx *ctx, int frame)
+{
+   return ctx->frame = frame;
+}
 
-  SDL_StartTextInput ();
-  SDL_EnableScreenSaver ();
+void ctx_define_texture (Ctx *ctx,
+                         const char *eid,
+                         int width, int height, int stride, int format, void *data,
+                         char *ret_eid)
+{
+  uint8_t hash[20]="";
+  char ascii[41]="";
+  int dst_stride = width;
+  //fprintf (stderr, "df %s\n", eid);
 
-  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);
+  dst_stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
+  if (stride <= 0)
+    stride = dst_stride;
 
-  tiled->pixels = (uint8_t*)malloc (width * height * 4);
+  int data_len;
+ 
+  if (format == CTX_FORMAT_YUV420)
+  data_len = width * height + ((width/2) * (height/2)) * 2;
+  else
+  data_len = height * dst_stride;
 
-  ctx_set_size (tiled->ctx,      width, height);
-  ctx_set_size (tiled->ctx_copy, width, height);
+  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;
+  }
 
-  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;
+  int eid_len = strlen (eid);
 
-  for (int i = 0; i < _ctx_max_threads; i++)
+  if (eid_len > 50)
   {
-    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);
+    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;
   }
 
-  mtx_init (&tiled->mtx, mtx_plain);
-  cnd_init (&tiled->cond);
+  // we now have eid
 
-#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*)ctx_tiled_render_fun, args);\
+  if (ctx_eid_valid (ctx, eid, 0, 0))
+  {
+    ctx_texture (ctx, eid, 0.0, 0.0);
   }
-  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
+  else
 
-  ctx_flush (tiled->ctx);
-  return tiled->ctx;
-#else
-  return NULL;
-#endif
-}
-#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 ctx_renderer_is_sdl (Ctx *ctx)
-{
-  return 0;
-}
-#endif
+    int pos = 2;
 
-#if CTX_EVENTS
+    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;
 
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
+    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];
+#if 1
+      memcpy (dst, src, data_len);
+#else
+      for (int y = 0; y < height; y++)
+      {
+         memcpy (dst, src, dst_stride);
+         src += stride;
+         dst += dst_stride;
+      }
 #endif
+    }
+    ((char *) &commands[pos+1].data.u8[0])[data_len]=0;
 
-typedef struct CtxTermCell
-{
-  char    utf8[5];
-  uint8_t fg[4];
-  uint8_t bg[4];
-
-  char    prev_utf8[5];
-  uint8_t prev_fg[4];
-  uint8_t prev_bg[4];
-} CtxTermCell;
-
-typedef struct CtxTermLine
-{
-  CtxTermCell *cells;
-  int maxcol;
-  int size;
-} CtxTermLine;
-
-typedef enum
-{
-  CTX_TERM_ASCII,
-  CTX_TERM_ASCII_MONO,
-  CTX_TERM_SEXTANT,
-  CTX_TERM_BRAILLE_MONO,
-  CTX_TERM_BRAILLE,
-  CTX_TERM_QUARTER,
-} CtxTermMode;
-
-typedef struct _CtxTerm CtxTerm;
-struct _CtxTerm
-{
-   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      *host;
-   CtxList  *lines;
-   CtxTermMode mode;
-};
-
-static int ctx_term_ch = 8;
-static int ctx_term_cw = 8;
+    if (ctx->renderer && ctx->renderer->process)
+    {
+      ctx_process (ctx, commands);
+      free (commands);
+    }
+    else
+    {
+       ctx->drawlist.count += ctx_conts_for_entry (commands) + 1;
+    }
 
-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)
-  {
-    ctx_list_append (&term->lines, calloc (sizeof (CtxTermLine), 1));
+    CtxEidInfo *eid_info = (CtxEidInfo*)calloc (sizeof (CtxEidInfo), 1);
+    eid_info->width      = width;
+    eid_info->height     = height;
+    eid_info->frame      = ctx->texture_cache->frame;
+    //fprintf (stderr, "%i\n", eid_info->frame);
+    eid_info->eid        = strdup (eid);
+    ctx_list_prepend (&ctx->texture_cache->eid_db, eid_info);
   }
-  CtxTermLine *line = ctx_list_nth_data (term->lines, row-1);
-  assert (line);
-  if (line->size < col)
+
+  if (ret_eid)
   {
-     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;
+    strcpy (ret_eid, eid);
+    ret_eid[64]=0;
   }
-  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);
 }
 
-static int _ctx_term256 = 0; // XXX TODO implement autodetect for this
-static long _ctx_curfg = -1;
-static long _ctx_curbg = -1;
+void
+ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid)
+{
+  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;
+  }
 
-static long ctx_rgb_to_long (int r,int g, int b)
-{
-  return r * 256 * 256 + g * 256 + b;
-}
+  if (ctx_eid_valid (ctx, eid , tw, th))
+  {
+     if (reid)
+     {
+       strcpy (reid, eid);
+     }
+     return;
+  }
 
+#ifdef STBI_INCLUDE_STB_IMAGE_H
+  CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
+  int w, h, components;
+  unsigned char *pixels = NULL;
 
-static void ctx_term_set_fg (int red, int green, int blue)
-{
-  long lc = ctx_rgb_to_long (red, green, blue);
-  if (lc == _ctx_curfg)
-    return;
-  _ctx_curfg=lc;
-  if (_ctx_term256 == 0)
+  if (!strncmp (path, "file://", 7))
   {
-    printf("\e[38;2;%i;%i;%im", red,green,blue);
+    pixels = stbi_load (path + 7, &w, &h, &components, 0);
   }
   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))))
+    unsigned char *data = NULL;
+    long length = 0;
+    ctx_get_contents (path, &data, &length);
+    if (data)
     {
-      printf("\e[38;5;%im", 16 + 216 + gray);
+       pixels = stbi_load_from_memory (data, length, &w, &h, &components, 0);
+       free (data);
     }
-    else
-      printf("\e[38;5;%im", 16 + r * 6 * 6 + g * 6  + b);
   }
-}
 
-static void ctx_term_set_bg(int red, int green, int blue)
-{
-  long lc = ctx_rgb_to_long (red, green, blue);
-//if (lc == _ctx_curbg)
-//  return;
-  _ctx_curbg=lc;
-  if (_ctx_term256 == 0)
+  if (pixels)
   {
-    printf("\e[48;2;%i;%i;%im", red,green,blue);
+    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
   {
-    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[48;5;%im", 16 + 216 + gray);
-    }
-    else
-      printf("\e[48;5;%im", 16 + r * 6 * 6 + g * 6  + b);
+    fprintf (stderr, "texture loading problem for %s\n", path);
   }
+#endif
 }
 
-static int _ctx_term_force_full = 0;
-
-void ctx_term_scanout (CtxTerm *term)
+void
+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 row = 1;
-  printf ("\e[H");
-//  printf ("\e[?25l");
-  printf ("\e[0m");
-  for (CtxList *l = term->lines; l; l = l->next)
+  int tex_width  = 0;
+  int tex_height = 0;
+  if (ctx_eid_valid (ctx, eid , &tex_width, &tex_height))
   {
-    CtxTermLine *line = l->data;
-    for (int col = 1; col <= line->maxcol; col++)
+    if (width > 0.0 && height > 0.0)
     {
-      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)
+#if 0
+      if (clip_width > 0.0f)
       {
-        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);
+        ctx_rectangle (ctx, clip_x, clip_y, clip_width, clip_height);
+        ctx_clip (ctx);
+      }
+#endif
+      ctx_rectangle (ctx, x, y, width, height);
+      CtxMatrix matrix;
+      ctx_matrix_identity (&matrix);
+      
+      ctx_texture (ctx, eid, 0, 0);// / (width/tex_width), y / (height/tex_height));
+      //ctx_rgba (ctx, 1, 0,0,0.5);
+#if 1
+      if (clip_width > 0.0f)
+      {
+              // XXX scale is not yet determined to be correct
+              // in relation to the translate!
+        ctx_matrix_scale (&matrix, clip_width/width, clip_height/height);
+        ctx_matrix_translate (&matrix, -clip_x, -clip_y);
       }
       else
       {
-        // TODO: accumulate succesive such, and compress them
-        // into one
-        printf ("\e[C");
+        ctx_matrix_scale (&matrix, tex_width/width, tex_height/height);
       }
-      strcpy (cell->prev_utf8, cell->utf8);
-      memcpy (cell->prev_fg, cell->fg, 3);
-      memcpy (cell->prev_bg, cell->bg, 3);
+      ctx_matrix_translate (&matrix, x, y);
+#endif
+      //ctx_matrix_invert (&matrix);
+      ctx_source_transform_matrix (ctx, &matrix);
+      //ctx_texture (ctx, eid, x / (width/tex_width), y / (height/tex_height));
+      ctx_fill (ctx);
     }
-    if (row != term->rows)
-      printf ("\n\r");
-    row ++;
   }
-  printf ("\e[0m");
-  //printf ("\e[?25h");
-  //
 }
 
-// xx
-// xx
-// xx
-//
-
-static inline int _ctx_rgba8_manhattan_diff (const uint8_t *a, const uint8_t *b)
+void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, float h)
 {
-  int c;
-  int diff = 0;
-  for (c = 0; c<3;c++)
-    diff += ctx_pow2(a[c]-b[c]);
-  return sqrtf(diff);
-  return diff;
+  ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0);
 }
 
-static void ctx_term_output_buf_half (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
+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)
 {
-  int stride = width * 4;
-  const char *sextants[]={
-   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█",
-
-  };
-  for (int row = 0; row < height/2; row++)
-    {
-      for (int col = 0; col < width-3; col++)
-        {
-          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]);
-        }
-    }
+  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_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)
+void
+ctx_draw_image (Ctx *ctx, const char *path, float x, float y, float w, float h)
 {
-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)
-        {
-          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][0]);
-          if (diff > curdiff)
-          {
-            curdiff = diff;
-            for (int c = 0; c < 3; c++)
-              rgba[1][c] = pixels[noi + c];
-          }
-        }
-      }
-          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++)
-          {
-            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];
-          }
-          }
-
+  ctx_draw_image_clipped (ctx, path, x, y, w, h, 0,0,0,0);
 }
 
-
-
-static void ctx_term_output_buf_quarter (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
+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)
 {
-  int stride = width * 4;
-  const char *sextants[]={
-   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█"
+  CtxEntry command = ctx_u8 (CTX_SET_PIXEL, r, g, b, a, 0, 0, 0, 0);
+  command.data.u16[2]=x;
+  command.data.u16[3]=y;
+  ctx_process (ctx, &command);
+}
 
+void
+ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1)
+{
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_LINEAR_GRADIENT, x0, y0),
+    ctx_f (CTX_CONT,            x1, y1)
   };
-  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++)
-              {
-                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]);
-        }
-    }
+  ctx_process (ctx, command);
 }
 
-
-static void ctx_term_output_buf_sextant (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
+void
+ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1, float r1)
 {
-  int stride = width * 4;
-
-  const char *sextants[]={
-   " 
","🬀","🬁","🬂","🬃","🬄","🬅","🬆","🬇","🬈","🬉","🬊","🬋","🬌","🬍","🬎","🬏","🬐","🬑","🬒","🬓","▌","🬔","🬕","🬖","🬗","🬘","🬙","🬚","🬛","🬜","🬝","🬞","🬟","🬠","🬡","🬢","🬣","🬤","🬥","🬦","🬧","▐","🬨","🬩","🬪","🬫","🬬","🬭","🬮","🬯","🬰","🬱","🬲","🬳","🬴","🬵","🬶","🬷","🬸","🬹","🬺","🬻","█"
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_RADIAL_GRADIENT, x0, y0),
+    ctx_f (CTX_CONT,            r0, x1),
+    ctx_f (CTX_CONT,            y1, r1)
   };
+  ctx_process (ctx, command);
+}
 
-  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}};
+void ctx_preserve (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_PRESERVE);
+}
 
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
+void ctx_fill (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_FILL);
+}
 
-          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]))
+void ctx_stroke (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_STROKE);
+}
 
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
 
-          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_empty (Ctx *ctx)
+{
+#if CTX_RASTERIZER
+  if (ctx->renderer == NULL)
+#endif
+    {
+      ctx->drawlist.count = 0;
+      ctx->drawlist.bitpack_pos = 0;
     }
 }
 
-static void ctx_term_output_buf_ascii (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term,
-                          int mono)
+void _ctx_set_store_clear (Ctx *ctx)
 {
-  /* 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);
+  ctx->transformation |= CTX_TRANSFORMATION_STORE_CLEAR;
+}
 
+#if CTX_EVENTS
+static void
+ctx_event_free (void *event, void *user_data)
+{
+  free (event);
+}
 
-          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;
-          }
+static void
+ctx_collect_events (CtxEvent *event, void *data, void *data2)
+{
+  Ctx *ctx = (Ctx*)data;
+  CtxEvent *copy;
+  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_press (CtxEvent *event, void *data1, void *data2);
+#endif
 
-          int brightest_dark_diff = _ctx_rgba8_manhattan_diff (black, rgba[0]);
+void ctx_reset (Ctx *ctx)
+{
+        /* we do the callback reset first - maybe we need two cbs,
+         * one for before and one after default impl?
+         *
+         * tiled fb and sdl needs to sync
+         */
+  if (ctx->renderer && ctx->renderer->reset)
+    ctx->renderer->reset (ctx->renderer);
 
-          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]))
+  //CTX_PROCESS_VOID (CTX_RESET);
+  //if (ctx->transformation & CTX_TRANSFORMATION_STORE_CLEAR)
+  //  { return; }
+  ctx_empty (ctx);
+  ctx_state_init (&ctx->state);
+#if CTX_EVENTS
+  ctx_list_free (&ctx->events.items);
+  ctx->events.last_item = NULL;
 
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
+  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_UP, ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
+    ctx_listen_full (ctx, 0,0,0,0,
+                     CTX_KEY_DOWN, ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
 
-           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]);
-        }
-    }
+    ctx_listen_full (ctx, 0, 0, ctx->events.width, ctx->events.height,
+                     (CtxEventType)(CTX_PRESS|CTX_RELEASE|CTX_MOTION),
+                     ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
+  }
+#endif
 }
 
-static void ctx_term_output_buf_braille (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term,
-                          int mono)
+void ctx_begin_path (Ctx *ctx)
 {
-  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_PROCESS_VOID (CTX_BEGIN_PATH);
+}
 
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
+void ctx_clip (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_CLIP);
+}
 
+void
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
 
-          /* 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;
-          }
+void ctx_save (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_SAVE);
+}
+void ctx_restore (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_RESTORE);
+}
 
-          int pixels_set = 0;
-          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], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+void ctx_start_group (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_START_GROUP);
+}
 
-                int set = CHECK_IS_SET;
-                if (reverse) { set = !set; }
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                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;
+void ctx_end_group (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_END_GROUP);
+}
 
-            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;
+void ctx_line_width (Ctx *ctx, float x)
+{
+  if (ctx->state.gstate.line_width != x)
+    CTX_PROCESS_F1 (CTX_LINE_WIDTH, x);
+}
 
-#if 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]);
-            }
-          }
-        }
-    }
+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;
+}
 
-inline static void ctx_term_render (void *ctx,
-                                       CtxCommand *command)
+void ctx_line_dash_offset (Ctx *ctx, float x)
 {
-  CtxTerm *term = (void*)ctx;
-  /* directly forward */
-  ctx_process (term->host, &command->entry);
+  if (ctx->state.gstate.line_dash_offset != x)
+    CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x);
 }
 
-inline static void ctx_term_flush (CtxTerm *term)
+int ctx_get_image_smoothing (Ctx *ctx)
 {
-  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
-  //
+  return ctx->state.gstate.image_smoothing;
+}
 
-  //uint8_t rgba_bg[4]={0,0,0,0};
-  //uint8_t rgba_fg[4]={255,0,255,255};
+void ctx_image_smoothing (Ctx *ctx, int enabled)
+{
+  if (ctx_get_image_smoothing (ctx) != enabled)
+    CTX_PROCESS_U8 (CTX_IMAGE_SMOOTHING, enabled);
+}
 
-  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);
-  }
+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);
+}
 
-  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);
+void ctx_shadow_blur (Ctx *ctx, float x)
+{
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_blur != x)
 #endif
+    CTX_PROCESS_F1 (CTX_SHADOW_BLUR, x);
 }
 
-void ctx_term_free (CtxTerm *term)
+void ctx_shadow_rgba (Ctx *ctx, float r, float g, float b, float a)
 {
-  while (term->lines)
+  CtxEntry command[3]=
   {
-    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 */
+    ctx_f (CTX_SHADOW_COLOR, CTX_RGBA, r),
+    ctx_f (CTX_CONT, g, b),
+    ctx_f (CTX_CONT, a, 0)
+  };
+  ctx_process (ctx, command);
 }
 
-int ctx_renderer_is_term (Ctx *ctx)
+void ctx_shadow_offset_x (Ctx *ctx, float x)
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_term_free)
-          return 1;
-  return 0;
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_offset_x != x)
+#endif
+    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_X, x);
 }
 
-float ctx_term_get_cell_width (Ctx *ctx)
+void ctx_shadow_offset_y (Ctx *ctx, float x)
 {
-  return ctx_term_cw;
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_offset_y != x)
+#endif
+    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_Y, x);
 }
 
-float ctx_term_get_cell_height (Ctx *ctx)
+void
+ctx_global_alpha (Ctx *ctx, float global_alpha)
 {
-  return ctx_term_ch;
+  if (ctx->state.gstate.global_alpha_f != global_alpha)
+    CTX_PROCESS_F1 (CTX_GLOBAL_ALPHA, global_alpha);
 }
 
-Ctx *ctx_new_term (int width, int height)
+float
+ctx_get_global_alpha (Ctx *ctx)
 {
-  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_MONO;
-  //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")){
-    term->mode = CTX_TERM_BRAILLE_MONO;
-    ctx_term_ch = 4;
-  }
-  else {
-    fprintf (stderr, "recognized values for CTX_TERM_MODE:\n"
-                    " sextant ascii quarter braille\n");
-    exit (1);
-  }
+  return ctx->state.gstate.global_alpha_f;
+}
 
-  mode = getenv ("CTX_TERM_FORCE_FULL");
-  if (mode && strcmp (mode, "0") && strcmp (mode, "no"))
-    _ctx_term_force_full = 1;
+void
+ctx_font_size (Ctx *ctx, float x)
+{
+  CTX_PROCESS_F1 (CTX_FONT_SIZE, x);
+}
 
-  fprintf (stdout, "\e[?1049h");
-  fprintf (stdout, "\e[?25l"); // cursor off
+float ctx_get_font_size  (Ctx *ctx)
+{
+  return ctx->state.gstate.font_size;
+}
 
-  int maxwidth = ctx_terminal_cols  () * ctx_term_cw;
-  int maxheight = (ctx_terminal_rows ()) * ctx_term_ch;
-  if (width <= 0 || height <= 0)
-  {
-    width = maxwidth;
-    height = maxheight;
-  }
-  if (width > maxwidth) width = maxwidth;
-  if (height > maxheight) height = maxheight;
-  term->ctx = ctx;
-  term->width  = width;
-  term->height = height;
+void
+ctx_miter_limit (Ctx *ctx, float limit)
+{
+  CTX_PROCESS_F1 (CTX_MITER_LIMIT, limit);
+}
 
-  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
+float ctx_get_line_width (Ctx *ctx)
+{
+  return ctx->state.gstate.line_width;
+}
 
+void
+_ctx_font (Ctx *ctx, const char *name)
+{
+  ctx->state.gstate.font = ctx_resolve_font (name);
+}
 
-  return ctx;
+#if 0
+void
+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);
 }
 
+const char *
+ctx_get (Ctx *ctx, const char *key)
+{
+  static char retbuf[32];
+  int len = 0;
+  CTX_PROCESS_U32(CTX_GET, ctx_strhash (key), 0);
+  while (read (STDIN_FILENO, &retbuf[len], 1) != -1)
+    {
+      if(retbuf[len]=='\n')
+        break;
+      retbuf[++len]=0;
+    }
+  return retbuf;
+}
 #endif
 
-#if CTX_EVENTS
-
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
+void
+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
+}
 
-typedef struct _CtxTermImg CtxTermImg;
-struct _CtxTermImg
+void
+ctx_font (Ctx *ctx, const char *family_name)
 {
-   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;
-};
+  // should also parse size
+  ctx_font_family (ctx, family_name);
+}
 
-inline static void ctx_termimg_render (void       *ctx,
-                                       CtxCommand *command)
+const char *
+ctx_get_font (Ctx *ctx)
 {
-  CtxTermImg *termimg = (void*)ctx;
-  /* directly forward */
-  ctx_process (termimg->host, &command->entry);
+  return ctx_fonts[ctx->state.gstate.font].name;
 }
 
-inline static void ctx_termimg_flush (CtxTermImg *termimg)
+void ctx_line_to (Ctx *ctx, float x, float y)
 {
-  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);
+  if (CTX_UNLIKELY(!ctx->state.has_moved))
+    { CTX_PROCESS_F (CTX_MOVE_TO, x, y); }
+  else
+    { CTX_PROCESS_F (CTX_LINE_TO, x, y); }
+}
 
-  int i = 0;
+void ctx_move_to (Ctx *ctx, float x, float y)
+{
+  CTX_PROCESS_F (CTX_MOVE_TO,x,y);
+}
 
-  printf ("\e[H");
-  printf ("\e_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\e\\", width, height);
-  while (i <  encoded_len)
+void ctx_curve_to (Ctx *ctx, float x0, float y0,
+                   float x1, float y1,
+                   float x2, float y2)
+{
+  CtxEntry command[3]=
   {
-     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 (encoded);
-  
-  fflush (NULL);
+    ctx_f (CTX_CURVE_TO, x0, y0),
+    ctx_f (CTX_CONT,     x1, y1),
+    ctx_f (CTX_CONT,     x2, y2)
+  };
+  ctx_process (ctx, command);
 }
 
-void ctx_termimg_free (CtxTermImg *termimg)
+void ctx_round_rectangle (Ctx *ctx,
+                          float x0, float y0,
+                          float w, float h,
+                          float radius)
 {
-  while (termimg->lines)
+  CtxEntry command[3]=
   {
-    free (termimg->lines->data);
-    ctx_list_remove (&termimg->lines, termimg->lines->data);
-  }
-  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 */
+    ctx_f (CTX_ROUND_RECTANGLE, x0, y0),
+    ctx_f (CTX_CONT,            w, h),
+    ctx_f (CTX_CONT,            radius, 0)
+  };
+  ctx_process (ctx, command);
 }
 
-int ctx_renderer_is_termimg (Ctx *ctx)
+void ctx_view_box (Ctx *ctx,
+                   float x0, float y0,
+                   float w, float h)
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_termimg_free)
-          return 1;
-  return 0;
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_VIEW_BOX, x0, y0),
+    ctx_f (CTX_CONT,     w, h)
+  };
+  ctx_process (ctx, command);
 }
 
-Ctx *ctx_new_termimg (int width, int height)
+void ctx_rectangle (Ctx *ctx,
+                    float x0, float y0,
+                    float w, float h)
 {
-  Ctx *ctx = ctx_new ();
-#if CTX_RASTERIZER
-  fprintf (stdout, "\e[?1049h");
-  fprintf (stdout, "\e[?25l"); // cursor off
-  CtxTermImg *termimg = (CtxTermImg*)calloc (sizeof (CtxTermImg), 1);
-
-
-  int maxwidth = ctx_terminal_width ();
-
-  int colwidth = maxwidth/ctx_terminal_cols ();
-  maxwidth-=colwidth;
-
-  int maxheight = ctx_terminal_height ();
-  if (width <= 0 || height <= 0)
+  CtxEntry command[3]=
   {
-    width  = maxwidth;
-    height = maxheight;
-  }
-  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
-
-  return ctx;
+    ctx_f (CTX_RECTANGLE, x0, y0),
+    ctx_f (CTX_CONT,      w, h)
+  };
+  ctx_process (ctx, command);
 }
 
-#endif
-
-#if CTX_FORMATTER
-
-typedef struct _CtxFormatter  CtxFormatter;
-struct _CtxFormatter 
+void ctx_rel_line_to (Ctx *ctx, float x, float y)
 {
-  void *target; // FILE
-  int   longform;
-  int   indent;
-
-  void (*add_str)(CtxFormatter *formatter, const char *str, int len);
-};
+  if (!ctx->state.has_moved)
+    { return; }
+  CTX_PROCESS_F (CTX_REL_LINE_TO,x,y);
+}
 
-static void ctx_formatter_addstr (CtxFormatter *formatter, const char *str, int len)
+void ctx_rel_move_to (Ctx *ctx, float x, float y)
 {
-  formatter->add_str (formatter, str, len);
+  if (!ctx->state.has_moved)
+    {
+      CTX_PROCESS_F (CTX_MOVE_TO,x,y);
+      return;
+    }
+  CTX_PROCESS_F (CTX_REL_MOVE_TO,x,y);
 }
 
-static void ctx_formatter_addstrf (CtxFormatter *formatter, const char *format, ...)
+CtxLineJoin ctx_get_line_join (Ctx *ctx)
 {
-   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_formatter_addstr (formatter, buffer, -1);
-   free (buffer);
+  return ctx->state.gstate.line_join;
 }
 
-static void _ctx_stream_addstr (CtxFormatter *formatter, const char *str, int len)
+CtxCompositingMode ctx_get_compositing_mode (Ctx *ctx)
 {
-  if (!str || len == 0)
-  {
-    return;
-  }
-  if (len < 0) len = strlen (str);
-  fwrite (str, len, 1, (FILE*)formatter->target);
+  return ctx->state.gstate.compositing_mode;
 }
 
-void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len)
+CtxBlend ctx_get_blend_mode (Ctx *ctx)
 {
-  if (!str || len == 0)
-  {
-    return;
-  }
-  if (len < 0) len = strlen (str);
-  ctx_string_append_data ((CtxString*)(formatter->target), str, len);
+  return ctx->state.gstate.blend_mode;
 }
 
+CtxTextAlign ctx_get_text_align  (Ctx *ctx)
+{
+  return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_text_align);
+}
 
-static void _ctx_print_endcmd (CtxFormatter *formatter)
+CtxTextBaseline ctx_get_text_baseline (Ctx *ctx)
 {
-  if (formatter->longform)
-    {
-      ctx_formatter_addstr (formatter, ");\n", 3);
-    }
+  return (CtxTextBaseline)ctx_state_get (&ctx->state, CTX_text_baseline);
 }
 
-static void _ctx_indent (CtxFormatter *formatter)
+CtxLineCap ctx_get_line_cap (Ctx *ctx)
 {
-  for (int i = 0; i < formatter->indent; i++)
-    { ctx_formatter_addstr (formatter, "  ", 2);
-    }
+  return ctx->state.gstate.line_cap;
 }
 
-const char *_ctx_code_to_name (int code)
+CtxFillRule ctx_get_fill_rule (Ctx *ctx)
 {
-      switch (code)
-        {
-          case CTX_REL_LINE_TO_X4:           return "relLinetoX4"; break;
-          case CTX_REL_LINE_TO_REL_CURVE_TO: return "relLineToRelCurveTo"; break;
-          case CTX_REL_CURVE_TO_REL_LINE_TO: return "relCurveToRelLineTo"; break;
-          case CTX_REL_CURVE_TO_REL_MOVE_TO: return "relCurveToRelMoveTo"; break;
-          case CTX_REL_LINE_TO_X2:           return "relLineToX2"; break;
-          case CTX_MOVE_TO_REL_LINE_TO:      return "moveToRelLineTo"; break;
-          case CTX_REL_LINE_TO_REL_MOVE_TO:  return "relLineToRelMoveTo"; break;
-          case CTX_FILL_MOVE_TO:             return "fillMoveTo"; break;
-          case CTX_REL_QUAD_TO_REL_QUAD_TO:  return "relQuadToRelQuadTo"; break;
-          case CTX_REL_QUAD_TO_S16:          return "relQuadToS16"; break;
+  return ctx->state.gstate.fill_rule;
+}
 
-          case CTX_SET_KEY:              return "setParam"; break;
-          case CTX_COLOR:                return "setColor"; break;
-          case CTX_DEFINE_GLYPH:         return "defineGlyph"; break;
-          case CTX_KERNING_PAIR:         return "kerningPair"; break;
-          case CTX_SET_PIXEL:            return "setPixel"; break;
-          case CTX_GLOBAL_ALPHA:         return "globalAlpha"; break;
-          case CTX_TEXT:                 return "text"; break;
-          case CTX_STROKE_TEXT:          return "strokeText"; break;
-          case CTX_SAVE:                 return "save"; break;
-          case CTX_RESTORE:              return "restore"; break;
-          case CTX_STROKE_SOURCE:        return "strokeSource"; break;
-          case CTX_NEW_PAGE:             return "newPage"; break;
-          case CTX_START_GROUP:          return "startGroup"; break;
-          case CTX_END_GROUP:            return "endGroup"; break;
-          case CTX_RECTANGLE:            return "rectangle"; break;
-          case CTX_ROUND_RECTANGLE:      return "roundRectangle"; break;
-          case CTX_LINEAR_GRADIENT:      return "linearGradient"; break;
-          case CTX_RADIAL_GRADIENT:      return "radialGradient"; break;
-          case CTX_GRADIENT_STOP:        return "gradientAddStop"; break;
-          case CTX_VIEW_BOX:             return "viewBox"; break;
-          case CTX_MOVE_TO:              return "moveTo"; break;
-          case CTX_LINE_TO:              return "lineTo"; break;
-          case CTX_BEGIN_PATH:           return "beginPath"; break;
-          case CTX_REL_MOVE_TO:          return "relMoveTo"; break;
-          case CTX_REL_LINE_TO:          return "relLineTo"; break;
-          case CTX_FILL:                 return "fill"; break;
-          case CTX_EXIT:                 return "exit"; break;
-          case CTX_APPLY_TRANSFORM:      return "transform"; break;
-          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;
-          case CTX_FLUSH:                return "flush"; break;
-          case CTX_RESET:                return "reset"; break;
-          case CTX_FONT:                 return "font"; break;
-          case CTX_STROKE:               return "stroke"; break;
-          case CTX_CLIP:                 return "clip"; break;
-          case CTX_ARC:                  return "arc"; break;
-          case CTX_SCALE:                return "scale"; break;
-          case CTX_TRANSLATE:            return "translate"; break;
-          case CTX_ROTATE:               return "rotate"; break;
-          case CTX_ARC_TO:               return "arcTo"; break;
-          case CTX_CURVE_TO:             return "curveTo"; break;
-          case CTX_REL_CURVE_TO:         return "relCurveTo"; break;
-          case CTX_REL_QUAD_TO:          return "relQuadTo"; break;
-          case CTX_QUAD_TO:              return "quadTo"; break;
-          case CTX_SMOOTH_TO:            return "smoothTo"; break;
-          case CTX_REL_SMOOTH_TO:        return "relSmoothTo"; break;
-          case CTX_SMOOTHQ_TO:           return "smoothqTo"; break;
-          case CTX_REL_SMOOTHQ_TO:       return "relSmoothqTo"; break;
-          case CTX_HOR_LINE_TO:          return "horLineTo"; break;
-          case CTX_VER_LINE_TO:          return "verLineTo"; break;
-          case CTX_REL_HOR_LINE_TO:      return "relHorLineTo"; break;
-          case CTX_REL_VER_LINE_TO:      return "relVerLineTo"; break;
-          case CTX_COMPOSITING_MODE:     return "compositingMode"; break;
-          case CTX_BLEND_MODE:           return "blendMode"; break;
-          case CTX_TEXT_ALIGN:           return "textAlign"; break;
-          case CTX_TEXT_BASELINE:        return "textBaseline"; break;
-          case CTX_TEXT_DIRECTION:       return "textDirection"; break;
-          case CTX_FONT_SIZE:            return "fontSize"; break;
-          case CTX_MITER_LIMIT:          return "miterLimit"; break;
-          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;
-        }
-      return NULL;
+void ctx_line_cap (Ctx *ctx, CtxLineCap cap)
+{
+  if (ctx->state.gstate.line_cap != cap)
+    CTX_PROCESS_U8 (CTX_LINE_CAP, cap);
 }
 
-static void _ctx_print_name (CtxFormatter *formatter, int code)
+void ctx_fill_rule (Ctx *ctx, CtxFillRule fill_rule)
 {
-#define CTX_VERBOSE_NAMES 1
-#if CTX_VERBOSE_NAMES
-  if (formatter->longform)
-    {
-      const char *name = NULL;
-      _ctx_indent (formatter);
-      //switch ((CtxCode)code)
-      name = _ctx_code_to_name (code);
-      if (name)
-        {
-          ctx_formatter_addstr (formatter, name, -1);
-          ctx_formatter_addstr (formatter, " (", 2);
-          if (code == CTX_SAVE)
-            { formatter->indent ++; }
-          else if (code == CTX_RESTORE)
-            { formatter->indent --; }
-          return;
-        }
-    }
-#endif
-  {
-    char name[3];
-    name[0]=CTX_SET_KEY;
-    name[2]='\0';
-    switch (code)
-      {
-        case CTX_GLOBAL_ALPHA:      name[1]='a'; break;
-        case CTX_COMPOSITING_MODE:  name[1]='m'; break;
-        case CTX_BLEND_MODE:        name[1]='B'; break;
-        case CTX_TEXT_ALIGN:        name[1]='t'; break;
-        case CTX_TEXT_BASELINE:     name[1]='b'; break;
-        case CTX_TEXT_DIRECTION:    name[1]='d'; break;
-        case CTX_FONT_SIZE:         name[1]='f'; break;
-        case CTX_MITER_LIMIT:       name[1]='l'; break;
-        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;
-        case CTX_SHADOW_OFFSET_Y:   name[1]='y'; break;
-        case CTX_FILL_RULE:         name[1]='r'; break;
-        default:
-          name[0] = code;
-          name[1] = 0;
-          break;
-      }
-    ctx_formatter_addstr (formatter, name, -1);
-    if (formatter->longform)
-      ctx_formatter_addstr (formatter, " (", 2);
-    else
-      ctx_formatter_addstr (formatter, " ", 1);
-  }
+  if (ctx->state.gstate.fill_rule != fill_rule)
+    CTX_PROCESS_U8 (CTX_FILL_RULE, fill_rule);
+}
+void ctx_line_join (Ctx *ctx, CtxLineJoin join)
+{
+  if (ctx->state.gstate.line_join != join)
+    CTX_PROCESS_U8 (CTX_LINE_JOIN, join);
+}
+void ctx_blend_mode (Ctx *ctx, CtxBlend mode)
+{
+  if (ctx->state.gstate.blend_mode != mode)
+    CTX_PROCESS_U32 (CTX_BLEND_MODE, mode, 0);
+}
+void ctx_compositing_mode (Ctx *ctx, CtxCompositingMode mode)
+{
+  if (ctx->state.gstate.compositing_mode != mode)
+    CTX_PROCESS_U32 (CTX_COMPOSITING_MODE, mode, 0);
+}
+void ctx_text_align (Ctx *ctx, CtxTextAlign text_align)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_ALIGN, text_align);
+}
+void ctx_text_baseline (Ctx *ctx, CtxTextBaseline text_baseline)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_BASELINE, text_baseline);
+}
+void ctx_text_direction (Ctx *ctx, CtxTextDirection text_direction)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_DIRECTION, text_direction);
 }
 
-static void
-ctx_print_entry_enum (CtxFormatter *formatter, CtxEntry *entry, int args)
+void
+ctx_rel_curve_to (Ctx *ctx,
+                  float x0, float y0,
+                  float x1, float y1,
+                  float x2, float y2)
 {
-  _ctx_print_name (formatter, entry->code);
-  for (int i = 0; i <  args; i ++)
-    {
-      int val = ctx_arg_u8 (i);
-      if (i>0)
-        { 
-          ctx_formatter_addstr (formatter, " ", 1);
-        }
-#if CTX_VERBOSE_NAMES
-      if (formatter->longform)
-        {
-          const char *str = NULL;
-          switch (entry->code)
-            {
-              case CTX_TEXT_BASELINE:
-                switch (val)
-                  {
-                    case CTX_TEXT_BASELINE_ALPHABETIC: str = "alphabetic"; break;
-                    case CTX_TEXT_BASELINE_TOP:        str = "top";        break;
-                    case CTX_TEXT_BASELINE_BOTTOM:     str = "bottom";     break;
-                    case CTX_TEXT_BASELINE_HANGING:    str = "hanging";    break;
-                    case CTX_TEXT_BASELINE_MIDDLE:     str = "middle";     break;
-                    case CTX_TEXT_BASELINE_IDEOGRAPHIC:str = "ideographic";break;
-                  }
-                break;
-              case CTX_TEXT_ALIGN:
-                switch (val)
-                  {
-                    case CTX_TEXT_ALIGN_LEFT:   str = "left"; break;
-                    case CTX_TEXT_ALIGN_RIGHT:  str = "right"; break;
-                    case CTX_TEXT_ALIGN_START:  str = "start"; break;
-                    case CTX_TEXT_ALIGN_END:    str = "end"; break;
-                    case CTX_TEXT_ALIGN_CENTER: str = "center"; break;
-                  }
-                break;
-              case CTX_LINE_CAP:
-                switch (val)
-                  {
-                    case CTX_CAP_NONE:   str = "none"; break;
-                    case CTX_CAP_ROUND:  str = "round"; break;
-                    case CTX_CAP_SQUARE: str = "square"; break;
-                  }
-                break;
-              case CTX_LINE_JOIN:
-                switch (val)
-                  {
-                    case CTX_JOIN_MITER: str = "miter"; break;
-                    case CTX_JOIN_ROUND: str = "round"; break;
-                    case CTX_JOIN_BEVEL: str = "bevel"; break;
-                  }
-                break;
-              case CTX_FILL_RULE:
-                switch (val)
-                  {
-                    case CTX_FILL_RULE_WINDING:  str = "winding"; break;
-                    case CTX_FILL_RULE_EVEN_ODD: str = "evenodd"; break;
-                  }
-                break;
-              case CTX_BLEND_MODE:
-                switch (val)
-                  {
-            case CTX_BLEND_NORMAL:      str = "normal"; break;
-            case CTX_BLEND_MULTIPLY:    str = "multiply"; break;
-            case CTX_BLEND_SCREEN:      str = "screen"; break;
-            case CTX_BLEND_OVERLAY:     str = "overlay"; break;
-            case CTX_BLEND_DARKEN:      str = "darken"; break;
-            case CTX_BLEND_LIGHTEN:     str = "lighten"; break;
-            case CTX_BLEND_COLOR_DODGE: str = "colorDodge"; break;
-            case CTX_BLEND_COLOR_BURN:  str = "colorBurn"; break;
-            case CTX_BLEND_HARD_LIGHT:  str = "hardLight"; break;
-            case CTX_BLEND_SOFT_LIGHT:  str = "softLight"; break;
-            case CTX_BLEND_DIFFERENCE:  str = "difference"; break;
-            case CTX_BLEND_EXCLUSION:   str = "exclusion"; break;
-            case CTX_BLEND_HUE:         str = "hue"; break;
-            case CTX_BLEND_SATURATION:  str = "saturation"; break;
-            case CTX_BLEND_COLOR:       str = "color"; break; 
-            case CTX_BLEND_LUMINOSITY:  str = "luminosity"; break;
-                  }
-                break;
-              case CTX_COMPOSITING_MODE:
-                switch (val)
-                  {
-              case CTX_COMPOSITE_SOURCE_OVER: str = "sourceOver"; break;
-              case CTX_COMPOSITE_COPY: str = "copy"; break;
-              case CTX_COMPOSITE_CLEAR: str = "clear"; break;
-              case CTX_COMPOSITE_SOURCE_IN: str = "sourceIn"; break;
-              case CTX_COMPOSITE_SOURCE_OUT: str = "sourceOut"; break;
-              case CTX_COMPOSITE_SOURCE_ATOP: str = "sourceAtop"; break;
-              case CTX_COMPOSITE_DESTINATION: str = "destination"; break;
-              case CTX_COMPOSITE_DESTINATION_OVER: str = "destinationOver"; break;
-              case CTX_COMPOSITE_DESTINATION_IN: str = "destinationIn"; break;
-              case CTX_COMPOSITE_DESTINATION_OUT: str = "destinationOut"; break;
-              case CTX_COMPOSITE_DESTINATION_ATOP: str = "destinationAtop"; break;
-              case CTX_COMPOSITE_XOR: str = "xor"; break;
-                  }
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_REL_CURVE_TO, x0, y0),
+    ctx_f (CTX_CONT, x1, y1),
+    ctx_f (CTX_CONT, x2, y2)
+  };
+  ctx_process (ctx, command);
+}
 
-               break;
-            }
-          if (str)
-            {
-              ctx_formatter_addstr (formatter, str, -1);
-            }
-          else
-            {
-              ctx_formatter_addstrf (formatter, "%i", val);
-            }
-        }
-      else
-#endif
-        {
-          ctx_formatter_addstrf (formatter, "%i", val);
-        }
-    }
-  _ctx_print_endcmd (formatter);
+void
+ctx_rel_quad_to (Ctx *ctx,
+                 float cx, float cy,
+                 float x,  float y)
+{
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_REL_QUAD_TO, cx, cy),
+    ctx_f (CTX_CONT, x, y)
+  };
+  ctx_process (ctx, command);
 }
 
+void
+ctx_quad_to (Ctx *ctx,
+             float cx, float cy,
+             float x,  float y)
+{
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_QUAD_TO, cx, cy),
+    ctx_f (CTX_CONT, x, y)
+  };
+  ctx_process (ctx, command);
+}
 
-static void
-ctx_print_a85 (CtxFormatter *formatter, uint8_t *data, int length)
+void ctx_arc (Ctx  *ctx,
+              float x0, float y0,
+              float radius,
+              float angle1, float angle2,
+              int   direction)
 {
-  char *tmp = (char*)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);
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_ARC, x0, y0),
+    ctx_f (CTX_CONT, radius, angle1),
+    ctx_f (CTX_CONT, angle2, direction)
+  };
+  ctx_process (ctx, command);
 }
 
-static void
-ctx_print_escaped_string (CtxFormatter *formatter, const char *string)
+static int ctx_coords_equal (float x1, float y1, float x2, float y2, float tol)
 {
-  if (!string) { return; }
-  for (int i = 0; string[i]; i++)
-    {
-      switch (string[i])
-        {
-          case '"':
-            ctx_formatter_addstr (formatter, "\\\"", 2);
-            break;
-          case '\\':
-            ctx_formatter_addstr (formatter, "\\\\", 2);
-            break;
-          case '\n':
-            ctx_formatter_addstr (formatter, "\\n", 2);
-            break;
-          default:
-            ctx_formatter_addstr (formatter, &string[i], 1);
-        }
-    }
+  float dx = x2 - x1;
+  float dy = y2 - y1;
+  return dx*dx + dy*dy < tol*tol;
 }
 
-static void
-ctx_print_float (CtxFormatter *formatter, float val)
+static float
+ctx_point_seg_dist_sq (float x, float y,
+                       float vx, float vy, float wx, float wy)
 {
-  char temp[128];
-  sprintf (temp, "%0.3f", val);
-  int j;
-  for (j = 0; temp[j]; j++)
-    if (j == ',') { temp[j] = '.'; }
-  j--;
-  if (j>0)
-    while (temp[j] == '0')
-      {
-        temp[j]=0;
-        j--;
-      }
-  if (temp[j]=='.')
-    { temp[j]='\0'; }
-  ctx_formatter_addstr (formatter, temp, -1);
+  float l2 = ctx_pow2 (vx-wx) + ctx_pow2 (vy-wy);
+  if (l2 < 0.0001)
+    { return ctx_pow2 (x-vx) + ctx_pow2 (y-vy); }
+  float t = ( (x - vx) * (wx - vx) + (y - vy) * (wy - vy) ) / l2;
+  t = ctx_maxf (0, ctx_minf (1, t) );
+  float ix = vx + t * (wx - vx);
+  float iy = vy + t * (wy - vy);
+  return ctx_pow2 (x-ix) + ctx_pow2 (y-iy);
 }
 
 static void
-ctx_print_int (CtxFormatter *formatter, int val)
+ctx_normalize (float *x, float *y)
 {
-  ctx_formatter_addstrf (formatter, "%i", val);
+  float length = ctx_hypotf ( (*x), (*y) );
+  if (length > 1e-6f)
+    {
+      float r = 1.0f / length;
+      *x *= r;
+      *y *= r;
+    }
 }
 
-static void
-ctx_print_entry (CtxFormatter *formatter, CtxEntry *entry, int args)
+void
+ctx_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
 {
-  _ctx_print_name (formatter, entry->code);
-  for (int i = 0; i <  args; i ++)
+  // XXX : should partially move into rasterizer to preserve comand
+  //       even if an arc preserves all geometry, just to ensure roundtripping
+  //       of data
+  /* from nanovg - but not quite working ; uncertain if arc or wrong
+   * transfusion is the cause.
+   */
+  float x0 = ctx->state.x;
+  float y0 = ctx->state.y;
+  float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
+  int dir;
+  if (!ctx->state.has_moved)
+    { return; }
+  if (1)
     {
-      float val = ctx_arg_float (i);
-      if (i>0 && val >= 0.0f)
+      // Handle degenerate cases.
+      if (ctx_coords_equal (x0,y0, x1,y1, 0.5f) ||
+          ctx_coords_equal (x1,y1, x2,y2, 0.5f) ||
+          ctx_point_seg_dist_sq (x1,y1, x0,y0, x2,y2) < 0.5 ||
+          radius < 0.5)
         {
-          if (formatter->longform)
-            {
-              ctx_formatter_addstr (formatter, ", ", 2);
-            }
-          else
-            {
-              if (val >= 0.0f)
-                ctx_formatter_addstr (formatter, " ", 1);
-            }
+          ctx_line_to (ctx, x1,y1);
+          return;
         }
-      ctx_print_float (formatter, val);
     }
-  _ctx_print_endcmd (formatter);
+  // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
+  dx0 = x0-x1;
+  dy0 = y0-y1;
+  dx1 = x2-x1;
+  dy1 = y2-y1;
+  ctx_normalize (&dx0,&dy0);
+  ctx_normalize (&dx1,&dy1);
+  a = ctx_acosf (dx0*dx1 + dy0*dy1);
+  d = radius / ctx_tanf (a/2.0f);
+#if 0
+  if (d > 10000.0f)
+    {
+      ctx_line_to (ctx, x1, y1);
+      return;
+    }
+#endif
+  if ( (dx1*dy0 - dx0*dy1) > 0.0f)
+    {
+      cx = x1 + dx0*d + dy0*radius;
+      cy = y1 + dy0*d + -dx0*radius;
+      a0 = ctx_atan2f (dx0, -dy0);
+      a1 = ctx_atan2f (-dx1, dy1);
+      dir = 0;
+    }
+  else
+    {
+      cx = x1 + dx0*d + -dy0*radius;
+      cy = y1 + dy0*d + dx0*radius;
+      a0 = ctx_atan2f (-dx0, dy0);
+      a1 = ctx_atan2f (dx1, -dy1);
+      dir = 1;
+    }
+  ctx_arc (ctx, cx, cy, radius, a0, a1, dir);
 }
 
-static void
-ctx_print_glyph (CtxFormatter *formatter, CtxEntry *entry, int args)
+void
+ctx_rel_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
 {
-  _ctx_print_name (formatter, entry->code);
-  ctx_formatter_addstrf (formatter, "%i", entry->data.u32[0]);
-  _ctx_print_endcmd (formatter);
+  x1 += ctx->state.x;
+  y1 += ctx->state.y;
+  x2 += ctx->state.x;
+  y2 += ctx->state.y;
+  ctx_arc_to (ctx, x1, y1, x2, y2, radius);
 }
 
-static void
-ctx_formatter_process (void *user_data, CtxCommand *c);
-
+void
+ctx_exit (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_EXIT);
+}
 
-static void
-ctx_formatter_process (void *user_data, CtxCommand *c)
+void
+ctx_flush (Ctx *ctx)
 {
-  CtxEntry *entry = &c->entry;
-  CtxFormatter *formatter = (CtxFormatter*)user_data;
+  /* XXX: should be fully moved into the renderers
+   *      to permit different behavior and get rid
+   *      of the extranous flush() vfunc.
+   */
+  ctx->rev++;
+//  CTX_PROCESS_VOID (CTX_FLUSH);
+#if 0
+  //printf (" \e[?2222h");
+  ctx_drawlist_compact (&ctx->drawlist);
+  for (int i = 0; i < ctx->drawlist.count - 1; i++)
+    {
+      CtxEntry *entry = &ctx->drawlist.entries[i];
+      fwrite (entry, 9, 1, stdout);
+#if 0
+      uint8_t  *buf = (void *) entry;
+      for (int j = 0; j < 9; j++)
+        { printf ("%c", buf[j]); }
+#endif
+    }
+  printf ("Xx.Xx.Xx.");
+  fflush (NULL);
+#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);
+}
+
+////////////////////////////////////////
 
+static inline void
+ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
+{
+  CtxCommand *c = (CtxCommand *) entry;
   switch (entry->code)
-  //switch ((CtxCode)(entry->code))
     {
-      case CTX_GLYPH:
-        ctx_print_glyph (formatter, entry, 1);
+      case CTX_LINE_DASH_OFFSET:
+        state->gstate.line_dash_offset = ctx_arg_float (0);
         break;
-      case CTX_LINE_TO:
-      case CTX_REL_LINE_TO:
-      case CTX_SCALE:
-      case CTX_TRANSLATE:
-      case CTX_MOVE_TO:
-      case CTX_REL_MOVE_TO:
-      case CTX_SMOOTHQ_TO:
-      case CTX_REL_SMOOTHQ_TO:
-        ctx_print_entry (formatter, entry, 2);
+      case CTX_LINE_WIDTH:
+        state->gstate.line_width = ctx_arg_float (0);
         break;
-      case CTX_TEXTURE:
-        _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);
+#if CTX_ENABLE_SHADOW_BLUR
+      case CTX_SHADOW_BLUR:
+        state->gstate.shadow_blur = ctx_arg_float (0);
+        break;
+      case CTX_SHADOW_OFFSET_X:
+        state->gstate.shadow_offset_x = ctx_arg_float (0);
+        break;
+      case CTX_SHADOW_OFFSET_Y:
+        state->gstate.shadow_offset_y = ctx_arg_float (0);
+        break;
+#endif
+      case CTX_LINE_CAP:
+        state->gstate.line_cap = (CtxLineCap) ctx_arg_u8 (0);
+        break;
+      case CTX_FILL_RULE:
+        state->gstate.fill_rule = (CtxFillRule) ctx_arg_u8 (0);
+        break;
+      case CTX_LINE_JOIN:
+        state->gstate.line_join = (CtxLineJoin) ctx_arg_u8 (0);
+        break;
+      case CTX_COMPOSITING_MODE:
+        state->gstate.compositing_mode = (CtxCompositingMode) ctx_arg_u32 (0);
+        break;
+      case CTX_BLEND_MODE:
+        state->gstate.blend_mode = (CtxBlend) ctx_arg_u32 (0);
+        break;
+      case CTX_TEXT_ALIGN:
+        ctx_state_set (state, CTX_text_align, ctx_arg_u8 (0) );
+        break;
+      case CTX_TEXT_BASELINE:
+        ctx_state_set (state, CTX_text_baseline, ctx_arg_u8 (0) );
+        break;
+      case CTX_TEXT_DIRECTION:
+        ctx_state_set (state, CTX_text_direction, ctx_arg_u8 (0) );
+        break;
+      case CTX_GLOBAL_ALPHA:
+        state->gstate.global_alpha_u8 = ctx_float_to_u8 (ctx_arg_float (0) );
+        state->gstate.global_alpha_f = ctx_arg_float (0);
+        break;
+      case CTX_FONT_SIZE:
+        state->gstate.font_size = ctx_arg_float (0);
+        break;
+      case CTX_MITER_LIMIT:
+        state->gstate.miter_limit = ctx_arg_float (0);
+        break;
+      case CTX_COLOR_SPACE:
+        /* move this out of this function and only do it in rasterizer? XXX */
+        ctx_rasterizer_colorspace_icc (state, (CtxColorSpace)c->colorspace.space_slot,
+                                              (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_STROKE_SOURCE:
+        state->source = 1;
         break;
 
-      case CTX_DEFINE_TEXTURE:
+      case CTX_COLOR:
         {
-        _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);
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = 0;
 
-        uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
-#if 1
+          source->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) // XXX remove 511 after stroke source is complete
+            {
+              case CTX_RGB:
+                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
+                break;
+              case CTX_RGBA:
+                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+              case CTX_DRGBA:
+                ctx_color_set_drgba (state, &source->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, &source->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, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
+                break;
+              case CTX_DCMYKA:
+                ctx_color_set_dcmyka (state, &source->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, &source->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, &source->color, c->graya.g, c->graya.a);
+                break;
+              case CTX_GRAY:
+                ctx_color_set_graya (state, &source->color, c->graya.g, 1.0f);
+                break;
+            }
+        }
+        break;
+      case CTX_SET_RGBA_U8:
+        //ctx_source_deinit (&state->gstate.source);
+        //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = 0;
 
-        int stride = ctx_pixel_format_get_stride ((CtxPixelFormat)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, "\" ");
+          source->type = CTX_SOURCE_COLOR;
+
+          ctx_color_set_RGBA8 (state, &source->color,
+                               ctx_arg_u8 (0),
+                               ctx_arg_u8 (1),
+                               ctx_arg_u8 (2),
+                               ctx_arg_u8 (3) );
+        }
+        //for (int i = 0; i < 4; i ++)
+        //  state->gstate.source.color.rgba[i] = ctx_arg_u8(i);
+        break;
+      //case CTX_TEXTURE:
+      //  state->gstate.source.type = CTX_SOURCE_
+      //  break;
+      case CTX_LINEAR_GRADIENT:
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = is_stroke ? 2 : 0;
+
+          float x0 = ctx_arg_float (0);
+          float y0 = ctx_arg_float (1);
+          float x1 = ctx_arg_float (2);
+          float y1 = ctx_arg_float (3);
+          float dx, dy, length, start, end;
+
+          length = ctx_hypotf (x1-x0,y1-y0);
+          dx = (x1-x0) / length;
+          dy = (y1-y0) / length;
+          start = (x0 * dx + y0 * dy) / length;
+          end =   (x1 * dx + y1 * dy) / length;
+          source->linear_gradient.length = length;
+          source->linear_gradient.dx = dx;
+          source->linear_gradient.dy = dy;
+          source->linear_gradient.start = start;
+          source->linear_gradient.end = end;
+          source->linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
+          source->type = CTX_SOURCE_LINEAR_GRADIENT;
+          source->transform = state->gstate.transform;
+          ctx_matrix_invert (&source->transform);
+        }
+        break;
+      case CTX_RADIAL_GRADIENT:
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = is_stroke ? 2 : 0;
+
+          float x0 = ctx_arg_float (0);
+          float y0 = ctx_arg_float (1);
+          float r0 = ctx_arg_float (2);
+          float x1 = ctx_arg_float (3);
+          float y1 = ctx_arg_float (4);
+          float r1 = ctx_arg_float (5);
+          source->radial_gradient.x0 = x0;
+          source->radial_gradient.y0 = y0;
+          source->radial_gradient.r0 = r0;
+          source->radial_gradient.x1 = x1;
+          source->radial_gradient.y1 = y1;
+          source->radial_gradient.r1 = r1;
+          source->radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
+          source->type      = CTX_SOURCE_RADIAL_GRADIENT;
+          source->transform = state->gstate.transform;
+          ctx_matrix_invert (&source->transform);
+        }
+        break;
+    }
+}
 
+static inline void
+ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data)
+{
+  switch (entry->code)
+    {
+      case CTX_SAVE:
+        ctx_gstate_push (state);
+        break;
+      case CTX_RESTORE:
+        ctx_gstate_pop (state);
+        break;
+      case CTX_IDENTITY:
+        _ctx_matrix_identity (&state->gstate.transform);
+        break;
+      case CTX_TRANSLATE:
+        ctx_matrix_translate (&state->gstate.transform,
+                              ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_SCALE:
+        ctx_matrix_scale (&state->gstate.transform,
+                          ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_ROTATE:
+        ctx_matrix_rotate (&state->gstate.transform, ctx_arg_float (0) );
+        break;
+      case CTX_APPLY_TRANSFORM:
+        {
+          CtxMatrix m;
+          ctx_matrix_set (&m,
+                          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_matrix_multiply (&state->gstate.transform,
+                                &state->gstate.transform, &m); // XXX verify order
+        }
+#if 0
+        ctx_matrix_set (&state->gstate.transform,
+                        ctx_arg_float (0), ctx_arg_float (1),
+                        ctx_arg_float (2), ctx_arg_float (3),
+                        ctx_arg_float (4), ctx_arg_float (5) );
 #endif
+        break;
+    }
+}
 
-        _ctx_print_endcmd (formatter);
+/*
+ * this transforms the contents of entry according to ctx->transformation
+ */
+static inline void
+ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data)
+{
+  CtxCommand *c = (CtxCommand *) entry;
+  float start_x = state->x;
+  float start_y = state->y;
+  int had_moved = state->has_moved;
+  switch (entry->code)
+    {
+      case CTX_MOVE_TO:
+      case CTX_LINE_TO:
+        {
+          float x = c->c.x0;
+          float y = c->c.y0;
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              _ctx_user_to_device (state, &x, &y);
+              ctx_arg_float (0) = x;
+              ctx_arg_float (1) = y;
+            }
         }
         break;
-
-      case CTX_REL_ARC_TO:
-      case CTX_ARC_TO:
-      case CTX_ROUND_RECTANGLE:
-        ctx_print_entry (formatter, entry, 5);
-        break;
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
       case CTX_ARC:
-      case CTX_RADIAL_GRADIENT:
-      case CTX_APPLY_TRANSFORM:
-        ctx_print_entry (formatter, entry, 6);
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          {
+            float temp;
+            _ctx_user_to_device (state, &c->arc.x, &c->arc.y);
+            temp = 0;
+            _ctx_user_to_device_distance (state, &c->arc.radius, &temp);
+          }
         break;
-      case CTX_QUAD_TO:
-      case CTX_RECTANGLE:
-      case CTX_REL_QUAD_TO:
       case CTX_LINEAR_GRADIENT:
-      case CTX_VIEW_BOX:
-      case CTX_SMOOTH_TO:
-      case CTX_REL_SMOOTH_TO:
-        ctx_print_entry (formatter, entry, 4);
-        break;
-      case CTX_FONT_SIZE:
-      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:
-      case CTX_SHADOW_OFFSET_Y:
-      case CTX_VER_LINE_TO:
-      case CTX_HOR_LINE_TO:
-      case CTX_REL_VER_LINE_TO:
-      case CTX_REL_HOR_LINE_TO:
-        ctx_print_entry (formatter, entry, 1);
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+        {
+        _ctx_user_to_device (state, &c->linear_gradient.x1, &c->linear_gradient.y1);
+        _ctx_user_to_device (state, &c->linear_gradient.x2, &c->linear_gradient.y2);
+        }
         break;
-#if 0
-      case CTX_SET:
-        _ctx_print_name (formatter, entry->code);
-        switch (c->set.key_hash)
+      case CTX_RADIAL_GRADIENT:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
         {
-           case CTX_x: ctx_formatter_addstrf (formatter, " 'x' "); break;
-           case CTX_y: ctx_formatter_addstrf (formatter, " 'y' "); break;
-           case CTX_width: ctx_formatter_addstrf (formatter, " width "); break;
-           case CTX_height: ctx_formatter_addstrf (formatter, " height "); break;
-           default:
-             ctx_formatter_addstrf (formatter, " %d ", c->set.key_hash);
+          float temp;
+          _ctx_user_to_device (state, &c->radial_gradient.x1, &c->radial_gradient.y1);
+          temp = 0;
+          _ctx_user_to_device_distance (state, &c->radial_gradient.r1, &temp);
+          _ctx_user_to_device (state, &c->radial_gradient.x2, &c->radial_gradient.y2);
+          temp = 0;
+          _ctx_user_to_device_distance (state, &c->radial_gradient.r2, &temp);
         }
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, (char*)c->set.utf8);
-        ctx_formatter_addstrf (formatter, "\"");
-        _ctx_print_endcmd (formatter);
         break;
-#endif
-      case CTX_COLOR:
-        if (formatter->longform ||  1)
+      case CTX_CURVE_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
           {
-            _ctx_indent (formatter);
-            int model = (int) c->set_color.model;
-            const char *suffix="";
-            if (model & 512)
-            {
-              model = model & 511;
-              suffix = "S";
-            }
-            switch (model)
+            for (int c = 0; c < 3; c ++)
               {
-                case CTX_GRAY:
-                  ctx_formatter_addstrf (formatter, "gray%s ", suffix);
-                  ctx_print_float (formatter, c->graya.g);
-                  break;
-                case CTX_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);
-                  break;
-                case CTX_RGBA:
-                  if (c->rgba.a != 1.0)
-                  {
-                    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);
-                    ctx_formatter_addstrf (formatter, " ");
-                    ctx_print_float (formatter, c->rgba.b);
-                    ctx_formatter_addstrf (formatter, " ");
-                    ctx_print_float (formatter, c->rgba.a);
-                    break;
-                  }
-                  /* FALLTHROUGH */
-                case CTX_RGB:
-                  if (c->rgba.r == c->rgba.g && c->rgba.g == c->rgba.b)
-                  {
-                    ctx_formatter_addstrf (formatter, "gray%s ", suffix);
-                    ctx_print_float (formatter, c->rgba.r);
-                    ctx_formatter_addstrf (formatter, " ");
-                    break;
-                  }
-                  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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.a);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.a);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  break;
-                case CTX_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);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.a);
-                  break;
+                float x = entry[c].data.f[0];
+                float y = entry[c].data.f[1];
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
               }
           }
-        else
-          {
-            ctx_print_entry (formatter, entry, 1);
-          }
         break;
-      case CTX_SET_RGBA_U8:
-        if (formatter->longform)
-          {
-            _ctx_indent (formatter);
-            ctx_formatter_addstrf (formatter, "rgba (");
-          }
-        else
+      case CTX_QUAD_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
           {
-            ctx_formatter_addstrf (formatter, "rgba (");
+            for (int c = 0; c < 2; c ++)
+              {
+                float x = entry[c].data.f[0];
+                float y = entry[c].data.f[1];
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
+              }
           }
-        for (int c = 0; c < 4; c++)
+        break;
+      case CTX_REL_MOVE_TO:
+      case CTX_REL_LINE_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
           {
-            if (c)
+            for (int c = 0; c < 1; c ++)
               {
-                if (formatter->longform)
-                  ctx_formatter_addstrf (formatter, ", ");
-                else
-                  ctx_formatter_addstrf (formatter, " ");
+                float x = state->x;
+                float y = state->y;
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
               }
-            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) );
+            if (entry->code == CTX_REL_MOVE_TO)
+              { entry->code = CTX_MOVE_TO; }
+            else
+              { entry->code = CTX_LINE_TO; }
           }
-        _ctx_print_endcmd (formatter);
         break;
-      case CTX_SET_PIXEL:
-#if 0
-        ctx_set_pixel_u8 (d_ctx,
-                          ctx_arg_u16 (2), ctx_arg_u16 (3),
-                          ctx_arg_u8 (0),
-                          ctx_arg_u8 (1),
-                          ctx_arg_u8 (2),
-                          ctx_arg_u8 (3) );
-#endif
+      case CTX_REL_CURVE_TO:
+        {
+          float nx = state->x + ctx_arg_float (4);
+          float ny = state->y + ctx_arg_float (5);
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              for (int c = 0; c < 3; c ++)
+                {
+                  float x = nx + entry[c].data.f[0];
+                  float y = ny + entry[c].data.f[1];
+                  _ctx_user_to_device (state, &x, &y);
+                  entry[c].data.f[0] = x;
+                  entry[c].data.f[1] = y;
+                }
+              entry->code = CTX_CURVE_TO;
+            }
+        }
         break;
-      case CTX_FILL:
+      case CTX_REL_QUAD_TO:
+        {
+          float nx = state->x + ctx_arg_float (2);
+          float ny = state->y + ctx_arg_float (3);
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              for (int c = 0; c < 2; c ++)
+                {
+                  float x = nx + entry[c].data.f[0];
+                  float y = ny + entry[c].data.f[1];
+                  _ctx_user_to_device (state, &x, &y);
+                  entry[c].data.f[0] = x;
+                  entry[c].data.f[1] = y;
+                }
+              entry->code = CTX_QUAD_TO;
+            }
+        }
+        break;
+    }
+  if ((((Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE))
+    {
+      int components = 0;
+      _ctx_user_to_device (state, &start_x, &start_y);
+      switch (entry->code)
+        {
+          case CTX_MOVE_TO:
+            if (had_moved) { components = 1; }
+            break;
+          case CTX_LINE_TO:
+            components = 1;
+            break;
+          case CTX_CURVE_TO:
+            components = 3;
+            break;
+          case CTX_QUAD_TO:
+            components = 2;
+            break;
+        }
+      if (components)
+        {
+          for (int c = 0; c < components; c++)
+            {
+              entry[c].data.f[0] -= start_x;
+              entry[c].data.f[1] -= start_y;
+            }
+          switch (entry->code)
+            {
+              case CTX_MOVE_TO:
+                entry[0].code = CTX_REL_MOVE_TO;
+                break;
+              case CTX_LINE_TO:
+                entry[0].code = CTX_REL_LINE_TO;
+                break;
+                break;
+              case CTX_CURVE_TO:
+                entry[0].code = CTX_REL_CURVE_TO;
+                break;
+              case CTX_QUAD_TO:
+                entry[0].code = CTX_REL_QUAD_TO;
+                break;
+            }
+        }
+    }
+}
+
+static inline void
+ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data)
+{
+  switch (entry->code)
+    {
       case CTX_RESET:
-      case CTX_STROKE:
-      case CTX_IDENTITY:
+        ctx_state_init (state);
+        break;
       case CTX_CLIP:
       case CTX_BEGIN_PATH:
-      case CTX_CLOSE_PATH:
-      case CTX_SAVE:
-      case CTX_PRESERVE:
-      case CTX_START_GROUP:
-      case CTX_NEW_PAGE:
-      case CTX_END_GROUP:
-      case CTX_RESTORE:
-      case CTX_STROKE_SOURCE:
-        ctx_print_entry (formatter, entry, 0);
+      case CTX_FILL:
+      case CTX_STROKE:
+        state->has_moved = 0;
         break;
-      case CTX_TEXT_ALIGN:
-      case CTX_TEXT_BASELINE:
-      case CTX_TEXT_DIRECTION:
-      case CTX_FILL_RULE:
-      case CTX_LINE_CAP:
-      case CTX_LINE_JOIN:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-      case CTX_IMAGE_SMOOTHING:
-        ctx_print_entry_enum (formatter, entry, 1);
+      case CTX_MOVE_TO:
+      case CTX_LINE_TO:
+        state->x = ctx_arg_float (0);
+        state->y = ctx_arg_float (1);
+        state->has_moved = 1;
         break;
-      case CTX_GRADIENT_STOP:
-        _ctx_print_name (formatter, entry->code);
-        for (int c = 0; c < 4; c++)
-          {
-            if (c)
-              ctx_formatter_addstrf (formatter, " ");
-            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (4+c) ) );
-          }
-        _ctx_print_endcmd (formatter);
+      case CTX_CURVE_TO:
+        state->x = ctx_arg_float (4);
+        state->y = ctx_arg_float (5);
+        state->has_moved = 1;
         break;
-      case CTX_TEXT:
-      case CTX_STROKE_TEXT:
-      case CTX_FONT:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, ctx_arg_string() );
-        ctx_formatter_addstrf (formatter, "\"");
-        _ctx_print_endcmd (formatter);
+      case CTX_QUAD_TO:
+        state->x = ctx_arg_float (2);
+        state->y = ctx_arg_float (3);
+        state->has_moved = 1;
         break;
-      case CTX_CONT:
-      case CTX_EDGE:
-      case CTX_DATA:
-      case CTX_DATA_REV:
-      case CTX_FLUSH:
+      case CTX_ARC:
+        state->x = ctx_arg_float (0) + ctx_cosf (ctx_arg_float (4) ) * ctx_arg_float (2);
+        state->y = ctx_arg_float (1) + ctx_sinf (ctx_arg_float (4) ) * ctx_arg_float (2);
         break;
-      case CTX_KERNING_PAIR:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        {
-           uint8_t utf8[16];
-           utf8[ctx_unichar_to_utf8 (c->kern.glyph_before, utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\", \"");
-           utf8[ctx_unichar_to_utf8 (c->kern.glyph_after, utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\"");
-           sprintf ((char*)utf8, ", %f", c->kern.amount / 256.0);
-           ctx_print_escaped_string (formatter, (char*)utf8);
-        }
-        _ctx_print_endcmd (formatter);
+      case CTX_REL_MOVE_TO:
+      case CTX_REL_LINE_TO:
+        state->x += ctx_arg_float (0);
+        state->y += ctx_arg_float (1);
+        break;
+      case CTX_REL_CURVE_TO:
+        state->x += ctx_arg_float (4);
+        state->y += ctx_arg_float (5);
+        break;
+      case CTX_REL_QUAD_TO:
+        state->x += ctx_arg_float (2);
+        state->y += ctx_arg_float (3);
         break;
+        // XXX missing some smooths
+    }
+}
+
+static inline void
+ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data)
+{
+  if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ||
+       ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE) )
+    {
+      ctx_interpret_pos_transform (state, entry, data);
+    }
+  ctx_interpret_pos_bare (state, entry, data);
+}
+
+#if CTX_BABL
+void ctx_colorspace_babl (CtxState   *state,
+                          CtxColorSpace  icc_slot,
+                          const Babl *space);
+#endif
+
+static void
+ctx_state_init (CtxState *state)
+{
+  ctx_memset (state, 0, sizeof (CtxState) );
+  state->gstate.global_alpha_u8 = 255;
+  state->gstate.global_alpha_f  = 1.0;
+  state->gstate.font_size       = 23; // default HTML canvas is 10px sans
+  state->gstate.line_width      = 2.0;
+  state->gstate.image_smoothing = 1;
+  state->gstate.source_stroke.type = CTX_SOURCE_INHERIT_FILL;
+  ctx_state_set (state, CTX_line_spacing, 1.0f);
+  state->min_x                  = 8192;
+  state->min_y                  = 8192;
+  state->max_x                  = -8192;
+  state->max_y                  = -8192;
+  _ctx_matrix_identity (&state->gstate.transform);
+#if CTX_CM
+#if CTX_BABL
+  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_USER_RGB,   babl_space ("sRGB"));
+  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_DEVICE_RGB, babl_space ("ACEScg"));
+#endif
+#endif
+}
+
+void _ctx_set_transformation (Ctx *ctx, int transformation)
+{
+  ctx->transformation = transformation;
+}
+
+static void
+_ctx_init (Ctx *ctx)
+{
+  for (int i = 0; i <256;i++)
+    ctx_u8_float[i] = i/255.0f;
+
+  ctx_state_init (&ctx->state);
+
+  ctx->renderer = NULL;
+#if CTX_CURRENT_PATH
+  ctx->current_path.flags |= CTX_DRAWLIST_CURRENT_PATH;
+#endif
+  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_SCREEN_SPACE;
+  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_RELATIVE;
+#if CTX_BITPACK
+  ctx->drawlist.flags |= CTX_TRANSFORMATION_BITPACK;
+#endif
+  ctx->texture_cache = ctx;
+}
+
+static void ctx_setup ();
+
+#if CTX_DRAWLIST_STATIC
+static Ctx ctx_state;
+#endif
+
+void ctx_set_renderer (Ctx  *ctx,
+                       void *renderer)
+{
+  if (ctx->renderer && ctx->renderer->free)
+    ctx->renderer->free (ctx->renderer);
+  ctx->renderer = (CtxImplementation*)renderer;
+}
 
-      case CTX_DEFINE_GLYPH:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        {
-           uint8_t utf8[16];
-           utf8[ctx_unichar_to_utf8 (entry->data.u32[0], utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\"");
-           sprintf ((char*)utf8, ", %f", entry->data.u32[1]/256.0);
-           ctx_print_escaped_string (formatter, (char*)utf8);
-        }
-        _ctx_print_endcmd (formatter);
-        break;
+void *ctx_get_renderer (Ctx *ctx)
+{
+  return ctx->renderer;
+}
+
+Ctx *
+ctx_new (void)
+{
+  ctx_setup ();
+#if CTX_DRAWLIST_STATIC
+  Ctx *ctx = &ctx_state;
+#else
+  Ctx *ctx = (Ctx *) malloc (sizeof (Ctx) );
+#endif
+  ctx_memset (ctx, 0, sizeof (Ctx) );
+  _ctx_init (ctx);
+  return ctx;
+}
+
+static inline void
+ctx_drawlist_deinit (CtxDrawlist *drawlist)
+{
+#if !CTX_DRAWLIST_STATIC
+  if (drawlist->entries && ! (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES) )
+    {
+      free (drawlist->entries); 
+    }
+#endif
+  drawlist->entries = NULL;
+  drawlist->size = 0;
+}
+
+static void ctx_deinit (Ctx *ctx)
+{
+  if (ctx->renderer)
+    {
+      if (ctx->renderer->free)
+        ctx->renderer->free (ctx->renderer);
+      ctx->renderer    = NULL;
     }
+  ctx_drawlist_deinit (&ctx->drawlist);
+#if CTX_CURRENT_PATH
+  ctx_drawlist_deinit (&ctx->current_path);
+#endif
+}
+
+void ctx_free (Ctx *ctx)
+{
+  if (!ctx)
+    { return; }
+#if CTX_EVENTS
+  ctx_clear_bindings (ctx);
+#endif
+  ctx_deinit (ctx);
+#if !CTX_DRAWLIST_STATIC
+  free (ctx);
+#endif
+}
+
+Ctx *ctx_new_for_drawlist (void *data, size_t length)
+{
+  Ctx *ctx = ctx_new ();
+  ctx->drawlist.flags   |= CTX_DRAWLIST_DOESNT_OWN_ENTRIES;
+  ctx->drawlist.entries  = (CtxEntry *) data;
+  ctx->drawlist.count    = length / sizeof (CtxEntry);
+  return ctx;
+}
+
+static void ctx_setup ()
+{
+  ctx_font_setup ();
 }
 
 void
-ctx_render_stream (Ctx *ctx, FILE *stream, int longform)
+ctx_render_ctx (Ctx *ctx, Ctx *d_ctx)
 {
   CtxIterator iterator;
-  CtxFormatter formatter;
-  formatter.target= stream;
-  formatter.longform = longform;
-  formatter.indent = 0;
-  formatter.add_str = _ctx_stream_addstr;
   CtxCommand *command;
   ctx_iterator_init (&iterator, &ctx->drawlist, 0,
                      CTX_ITERATOR_EXPAND_BITPACK);
   while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_formatter_process (&formatter, command); }
-  fprintf (stream, "\n");
+    {
+       ctx_process (d_ctx, &command->entry);
+    }
 }
 
-char *
-ctx_render_string (Ctx *ctx, int longform, int *retlen)
+void
+ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx)
 {
-  CtxString *string = ctx_string_new ("");
   CtxIterator iterator;
-  CtxFormatter formatter;
-  formatter.target= string;
-  formatter.longform = longform;
-  formatter.indent = 0;
-  formatter.add_str = _ctx_string_addstr;
   CtxCommand *command;
   ctx_iterator_init (&iterator, &ctx->drawlist, 0,
                      CTX_ITERATOR_EXPAND_BITPACK);
   while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_formatter_process (&formatter, command); }
-  char *ret = string->str;
-  if (retlen)
-    *retlen = string->length;
-  ctx_string_free (string, 0);
-  return ret;
+    {
+       switch (command->code)
+       {
+         default:
+                 //fprintf (stderr, "[%c]", command->code);
+                 break;
+         case CTX_TEXTURE:
+             //fprintf (stderr, "t:%s\n", command->texture.eid);
+             ctx_process (d_ctx, &command->entry);
+             break;
+         case CTX_DEFINE_TEXTURE:
+             //fprintf (stderr, "d:%s\n", command->define_texture.eid);
+             ctx_process (d_ctx, &command->entry);
+           break;
+       }
+    }
 }
 
-
+void ctx_quit (Ctx *ctx)
+{
+#if CTX_EVENTS
+  ctx->quit ++;
 #endif
+}
 
+int  ctx_has_quit (Ctx *ctx)
+{
 #if CTX_EVENTS
-int ctx_width (Ctx *ctx)
+  return (ctx->quit);
+#else
+  return 1; 
+#endif
+}
+
+int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format)
 {
-  return ctx->events.width;
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->bpp;
+  return -1;
 }
-int ctx_height (Ctx *ctx)
+
+int ctx_pixel_format_get_stride (CtxPixelFormat format, int width)
 {
-  return ctx->events.height;
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+  {
+    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;
 }
-#else
-int ctx_width (Ctx *ctx)
+
+int ctx_pixel_format_ebpp (CtxPixelFormat format)
 {
-  return 512;
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->ebpp;
+  return -1;
 }
-int ctx_height (Ctx *ctx)
+
+int ctx_pixel_format_components (CtxPixelFormat format)
 {
-  return 384;
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->components;
+  return -1;
 }
-#endif
 
-int ctx_rev (Ctx *ctx)
+#if CTX_EVENTS
+void         ctx_set_cursor (Ctx *ctx, CtxCursor cursor)
 {
-  return ctx->rev;
+  if (ctx->cursor != cursor)
+  {
+    ctx_set_dirty (ctx, 1);
+    ctx->cursor = cursor;
+  }
+}
+CtxCursor    ctx_get_cursor (Ctx *ctx)
+{
+  return ctx->cursor;
 }
 
-CtxState *ctx_get_state (Ctx *ctx)
+void ctx_set_clipboard (Ctx *ctx, const char *text)
 {
-  return &ctx->state;
+  if (ctx->renderer && ctx->renderer->set_clipboard)
+  {
+    ctx->renderer->set_clipboard (ctx->renderer, text);
+    return;
+  }
 }
 
-void ctx_dirty_rect (Ctx *ctx, int *x, int *y, int *width, int *height)
+char *ctx_get_clipboard (Ctx *ctx)
 {
-  if ( (ctx->state.min_x > ctx->state.max_x) ||
-       (ctx->state.min_y > ctx->state.max_y) )
-    {
-      if (x) { *x = 0; }
-      if (y) { *y = 0; }
-      if (width) { *width = 0; }
-      if (height) { *height = 0; }
-      return;
-    }
-  if (ctx->state.min_x < 0)
-    { ctx->state.min_x = 0; }
-  if (ctx->state.min_y < 0)
-    { ctx->state.min_y = 0; }
-  if (x) { *x = ctx->state.min_x; }
-  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; }
+  if (ctx->renderer && ctx->renderer->get_clipboard)
+  {
+    return ctx->renderer->get_clipboard (ctx->renderer);
+  }
+  return strdup ("");
 }
 
-#if CTX_CURRENT_PATH
-CtxIterator *
-ctx_current_path (Ctx *ctx)
+void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source)
 {
-  CtxIterator *iterator = &ctx->current_path_iterator;
-  ctx_iterator_init (iterator, &ctx->current_path, 0, CTX_ITERATOR_EXPAND_BITPACK);
-  return iterator;
+  ((CtxRasterizer*)ctx->renderer)->texture_source = texture_source;
 }
 
-void
-ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
+void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache)
 {
-  float minx = 50000.0;
-  float miny = 50000.0;
-  float maxx = -50000.0;
-  float maxy = -50000.0;
-  float x = 0;
-  float y = 0;
+  ctx->texture_cache = texture_cache;
+}
 
-  CtxIterator *iterator = ctx_current_path (ctx);
-  CtxCommand *command;
+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);
+}
 
-  while ((command = ctx_iterator_next (iterator)))
+#endif
+
+#if CTX_GET_CONTENTS
+
+#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 = NULL; // XXX XXX breaks with data uri's
+  int   success  = -1;
+
+  if (uri[0] == '/')
   {
-     int got_coord = 0;
-     switch (command->code)
-     {
-        // XXX missing many curve types
-        case CTX_LINE_TO:
-        case CTX_MOVE_TO:
-          x = command->move_to.x;
-          y = command->move_to.y;
-          got_coord++;
-          break;
-        case CTX_REL_LINE_TO:
-        case CTX_REL_MOVE_TO:
-          x += command->move_to.x;
-          y += command->move_to.y;
-          got_coord++;
-          break;
-        case CTX_CURVE_TO:
-          x = command->curve_to.x;
-          y = command->curve_to.y;
-          got_coord++;
-          break;
-        case CTX_REL_CURVE_TO:
-          x += command->curve_to.x;
-          y += command->curve_to.y;
-          got_coord++;
-          break;
-        case CTX_ARC:
-          minx = ctx_minf (minx, command->arc.x - command->arc.radius);
-          miny = ctx_minf (miny, command->arc.y - command->arc.radius);
-          maxx = ctx_maxf (maxx, command->arc.x + command->arc.radius);
-          maxy = ctx_maxf (maxy, command->arc.y + command->arc.radius);
+    temp_uri = (char*) malloc (strlen (uri) + 8);
+    sprintf (temp_uri, "file://%s", uri);
+    uri = temp_uri;
+  }
 
-          break;
-        case CTX_RECTANGLE:
-        case CTX_ROUND_RECTANGLE:
-          x = command->rectangle.x;
-          y = command->rectangle.y;
-          minx = ctx_minf (minx, x);
-          miny = ctx_minf (miny, y);
-          maxx = ctx_maxf (maxx, x);
-          maxy = ctx_maxf (maxy, y);
+  if (strchr (uri, '#'))
+   strchr (uri, '#')[0]=0;
 
-          x += command->rectangle.width;
-          y += command->rectangle.height;
-          got_coord++;
-          break;
-     }
-    if (got_coord)
+  for (CtxList *l = registered_contents; l; l = l->next)
+  {
+    CtxFileContent *c = (CtxFileContent*)l->data;
+    if (!strcmp (c->path, uri))
     {
-      minx = ctx_minf (minx, x);
-      miny = ctx_minf (miny, y);
-      maxx = ctx_maxf (maxx, x);
-      maxy = ctx_maxf (maxy, y);
+      contents = malloc (c->length+1);
+      contents[c->length]=0;
+      if (length) *length = c->length;
+      free (temp_uri);
+      return 0;
     }
   }
 
-  if (ex1) *ex1 = minx;
-  if (ey1) *ey1 = miny;
-  if (ex2) *ex2 = maxx;
-  if (ey2) *ey2 = maxy;
-}
+  if (!strncmp (uri, "file://", 5))
+  {
+    if (strchr (uri, '?'))
+     strchr (uri, '?')[0]=0;
+  }
 
+  if (!strncmp (uri, "file://", 7))
+    success = __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);
+     success = 0;
+  }
 #else
-void
-ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
-{
+    success = __ctx_file_get_contents (uri, contents, length);
+#endif
+  }
+  free (temp_uri);
+  return success;
 }
 #endif
 
+#endif // CTX_IMPLEMENTATION
+#ifndef __CTX_CLIENTS_H
+#define __CTX_CLIENTS_H
+
+typedef enum CtxClientFlags {
+  ITK_CLIENT_UI_RESIZABLE = 1<<0,
+  ITK_CLIENT_CAN_LAUNCH   = 1<<1,
+  ITK_CLIENT_MAXIMIZED    = 1<<2,
+  ITK_CLIENT_ICONIFIED    = 1<<3,
+  ITK_CLIENT_SHADED       = 1<<4,
+  ITK_CLIENT_TITLEBAR     = 1<<5
+} CtxClientFlags;
+
+struct _CtxClient {
+  VT    *vt;
+  Ctx   *ctx;
+  char  *title;
+  int    x;
+  int    y;
+  int    width;
+  int    height;
+  CtxClientFlags flags;
+#if 0
+  int    shaded;
+  int    iconified;
+  int    maximized;
+  int    resizable;
+#endif
+  int    unmaximized_x;
+  int    unmaximized_y;
+  int    unmaximized_width;
+  int    unmaximized_height;
+  int    do_quit;
+  long   drawn_rev;
+  int    id;
+  int    internal; // render a settings window rather than a vt
+#if CTX_THREADS
+  mtx_t  mtx;
+#endif
+#if VT_RECORD
+  Ctx   *recording;
+#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;
-}
+typedef struct _CtxClient CtxClient;
 
-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)
+extern CtxList *clients;
+extern CtxClient *active;
+extern CtxClient *active_tab;
+
+
+int ctx_client_resize (int id, int width, int height);
+void ctx_client_maximize (int id);
+
+CtxClient *vt_get_client (VT *vt);
+CtxClient *ctx_client_new (Ctx *ctx, const char *commandline,
+                       int x, int y, int width, int height,
+                       CtxClientFlags flags);
+CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int width, int height, 
CtxClientFlags flags);
+int ctx_clients_need_redraw (Ctx *ctx);
+
+extern float ctx_shape_cache_rate;
+extern int _ctx_max_threads;
+
+void ctx_client_move (int id, int x, int y);
+int ctx_client_resize (int id, int w, int h);
+void ctx_client_shade_toggle (int id);
+float ctx_client_min_y_pos (Ctx *ctx);
+float ctx_client_max_y_pos (Ctx *ctx);
+
+CtxClient *client_by_id (int id);
+
+void ctx_client_remove (Ctx *ctx, CtxClient *client);
+
+int ctx_client_height (int id);
+
+int ctx_client_x (int id);
+int ctx_client_y (int id);
+void ctx_client_raise_top (int id);
+void ctx_client_lower_bottom (int id);
+void ctx_client_iconify (int id);
+int ctx_client_is_iconified (int id);
+void ctx_client_uniconify (int id);
+void ctx_client_maximize (int id);
+int ctx_client_is_maximized (int id);
+void ctx_client_unmaximize (int id);
+void ctx_client_maximized_toggle (int id);
+void ctx_client_shade (int id);
+int ctx_client_is_shaded (int id);
+void ctx_client_unshade (int id);
+void ctx_client_toggle_maximized (int id);
+void ctx_client_shade_toggle (int id);
+void ctx_client_move (int id, int x, int y);
+int ctx_client_resize (int id, int width, int height);
+
+
+#endif
+#ifndef MRG_UTF8_H
+#define MRG_UTF8_H
+
+#if !__COSMOPOLITAN__
+#include <string.h>
+#include <stdint.h>
+#endif
+
+static inline int mrg_utf8_len (const unsigned char first_byte)
 {
-  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
+  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;
 }
 
-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)
+static inline const char *mrg_utf8_skip (const char *s, int utf8_length)
 {
-   if (0)
-   {
-   }
-#if CTX_RASTERIZER
-   else if (_ctx_is_rasterizer (ctx))
+   int count;
+   if (!s)
+     return NULL;
+   for (count = 0; *s; s++)
    {
-     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;
-     }
+     if ((*s & 0xC0) != 0x80)
+       count++;
+     if (count == utf8_length+1)
+       return s;
    }
+   return s;
+}
+
+int          mrg_unichar_to_utf8       (unsigned int   ch,
+                                        unsigned char *dest);
+unsigned int mrg_utf8_to_unichar       (unsigned char *utf8);
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern hoehrmann de>
+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 1
+
+static const uint8_t utf8d[] = {
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
+  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
+  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
+  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
+  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
+};
+
+static inline uint32_t
+utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
+  uint32_t type = utf8d[byte];
+
+  *codep = (*state != UTF8_ACCEPT) ?
+    (byte & 0x3fu) | (*codep << 6) :
+    (0xff >> type) & (byte);
+
+  *state = utf8d[256 + *state*16 + type];
+  return *state;
+}
+
 #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;
-     }
-   }
+#if CTX_VT
+
+/* 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 VT_LINE_H
+#define VT_LINE_H
+
+#include "ctx.h"
+
+#ifndef CTX_UNLIKELY
+#define CTX_UNLIKELY(x)    __builtin_expect(!!(x), 0)
+#define CTX_LIKELY(x)      __builtin_expect(!!(x), 1)
 #endif
-}
+#ifndef CTX_MAX
+#define CTX_MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+typedef struct _VtLine   VtLine;
+
+struct _VtLine
+{
+  CtxString string;
+  /* line extends string, permitting string ops to operate on it  */
+
+  uint64_t *style;
+  int       style_size;
+
+  void     *ctx; // each line can have an attached ctx context;
+  char     *prev;
+  int       prev_length;
+  CtxString *frame;
+
+  int       wrapped;
+
+  void     *ctx_copy; // each line can have an attached ctx context;
+  // clearing should be brutal enough to unset the context of the current
+  // at least in alt-screen mode
+  int       double_width;
+  int       double_height_top;
+  int       double_height_bottom;
+  int       contains_proportional;
+  float     xscale;
+  float     yscale;
+  float     y_offset;
+  int       in_scrolling_region;
+
+  /*  XXX:  needs refactoring to a CtxList of links/images */
+  void     *images[4];
+  int       image_col[4];
+  float     image_X[4]; // 0.0 - 1.0 offset in cell
+  float     image_Y[4];
+  int       image_rows[4];
+  int       image_cols[4];
+  int       image_subx[4];
+  int       image_suby[4];
+  int       image_subw[4];
+  int       image_subh[4];
+};
 
-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)
+
+static inline uint64_t vt_line_get_style (VtLine *string, int pos)
 {
-   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);
+  if (string->string.is_line==0)
+    return 0;
+  if (pos < 0 || pos >= string->style_size)
+    return 0;
+  return string->style[pos];
 }
 
-/* checking if an eid is valid also sets the frame for it
- */
-static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h)
+#if !__COSMOPOLITAN__
+#include <stdlib.h>
+#endif
+
+static inline void vt_line_set_style (VtLine *string, int pos, uint64_t style)
 {
-  ctx = ctx->texture_cache;
-  CtxList *to_remove = NULL;
-  int ret = 0;
-  //fprintf (stderr, "{%i}\n", ctx->frame);
-  for (CtxList *l = ctx->eid_db; l; l = l->next)
-  {
-    CtxEidInfo *eid_info = (CtxEidInfo*)l->data;
-    if (ctx->frame - eid_info->frame >= 2)
-            /* XXX XXX XXX this breaks texture caching since
-             *   it is wrong in some cases where more frames
-             *   have passed?
-             */
-    {
-      ctx_list_prepend (&to_remove, eid_info);
-    }
-    else if (!strcmp (eid_info->eid, eid) &&
-             ctx->frame - eid_info->frame < 2)
+  if (string->string.is_line==0)
+    return;
+  if (pos < 0 || pos >= 512)
+    return;
+  if (pos >= string->style_size)
     {
-    //fclose (f);
-      eid_info->frame = ctx->frame;
-      if (w) *w = eid_info->width;
-      if (h) *h = eid_info->height;
-      ret = 1;
+      int new_size = pos + 16;
+      string->style = realloc (string->style, new_size * sizeof (uint64_t) );
+      memset (&string->style[string->style_size], 0, (new_size - string->style_size) * sizeof (uint64_t) );
+      string->style_size = new_size;
     }
-  }
-  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;
+  string->style[pos] = style;
 }
 
-void ctx_texture (Ctx *ctx, const char *eid, float x, float y)
+VtLine *vt_line_new_with_size (const char *initial, int initial_size);
+VtLine *vt_line_new (const char *initial);
+
+static inline void        vt_line_free           (VtLine *line, int freealloc)
 {
-  int eid_len = strlen (eid);
-  char ascii[41]="";
-  //fprintf (stderr, "tx %s\n", 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;
-  }
+  CtxString *string = (CtxString*)line;
 
-    //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 (stderr, "setting texture eid %s\n", eid);
-  }
-  else
+#if 1
+  //if (string->is_line)
   {
-    //fprintf (stderr, "tried setting invalid texture eid %s\n", eid);
+    VtLine *line = (VtLine*)string;
+    if (line->frame)
+      ctx_string_free (line->frame, 1);
+    if (line->style)
+      { free (line->style); }
+    if (line->ctx)
+      { ctx_free (line->ctx); }
+    if (line->ctx_copy)
+      { ctx_free (line->ctx_copy); }
   }
-    //fclose (f);
+#endif
+
+  ctx_string_free (string, freealloc);
 }
-int
-_ctx_frame (Ctx *ctx)
+static inline const char *vt_line_get            (VtLine *line)
 {
-   return ctx->frame;
+  CtxString *string = (CtxString*)line;
+  return ctx_string_get (string);
 }
-int
-_ctx_set_frame (Ctx *ctx, int frame)
+static inline uint32_t    vt_line_get_unichar    (VtLine *line, int pos)
 {
-   return ctx->frame = frame;
+  CtxString *string = (CtxString*)line;
+  return ctx_string_get_unichar (string, pos);
+}
+static inline int         vt_line_get_length     (VtLine *line)
+{
+  CtxString *string = (CtxString*)line;
+  return ctx_string_get_length (string);
+}
+static inline int         vt_line_get_utf8length     (VtLine *line)
+{
+  CtxString *string = (CtxString*)line;
+  return ctx_string_get_utf8length (string);
+}
+static inline void        vt_line_set            (VtLine *line, const char *new_string)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_set (string, new_string);
+}
+static inline void        vt_line_clear          (VtLine *line)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_clear (string);
+}
+static inline void        vt_line_append_str     (VtLine *line, const char *str)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_append_str (string, str);
 }
 
-void ctx_define_texture (Ctx *ctx, const char *eid, int width, int height, int stride, int format, void 
*data, char *ret_eid)
+#if 0
+static inline void _ctx_string_append_byte (CtxString *string, char  val)
 {
-  uint8_t hash[20]="";
-  char ascii[41]="";
-  int dst_stride = width;
-  //fprintf (stderr, "df %s\n", eid);
+  if (CTX_LIKELY((val & 0xC0) != 0x80))
+    { string->utf8_length++; }
+  if (CTX_UNLIKELY(string->length + 2 >= string->allocated_length))
+    {
+      char *old = string->str;
+      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
+      string->str = (char*)realloc (old, string->allocated_length);
+    }
+  string->str[string->length++] = val;
+  string->str[string->length] = '\0';
+}
+#endif
 
-  dst_stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
-  if (stride <= 0)
-    stride = dst_stride;
+static inline void        vt_line_append_byte    (VtLine *line, char  val)
+{
+  CtxString *string = (CtxString*)line;
+  _ctx_string_append_byte (string, val);
+}
+static inline void        vt_line_append_string  (VtLine *line, CtxString *string2)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_append_string (string, string2);
+}
+static inline void        vt_line_append_unichar (VtLine *line, unsigned int unichar)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_append_unichar (string, unichar);
+}
 
-  int data_len = height * dst_stride;
 
-  if (eid == NULL)
-  {
-    CtxSHA1 *sha1 = ctx_sha1_new ();
+static inline void vt_line_append_data    (VtLine *line, const char *data, int len)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_append_data (string, data, len);
+}
+static inline void vt_line_append_utf8char (VtLine *line, const char *str)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_append_utf8char (string, str);
+}
+static inline void vt_line_replace_utf8   (VtLine *line, int pos, const char *new_glyph)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_replace_utf8 (string, pos, new_glyph);
+}
+static inline void vt_line_insert_utf8    (VtLine *line, int pos, const char *new_glyph)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_insert_utf8 (string, pos, new_glyph);
+  int len = vt_line_get_length (line);
+  for (int i = pos; i < len; i++)
+    vt_line_set_style (line, i, vt_line_get_style (line, i-1));
+}
 
-    {
-      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;
-  }
+static inline void vt_line_insert_unichar (VtLine *line, int pos, uint32_t new_glyph)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_insert_unichar (string, pos, new_glyph);
+  int len = vt_line_get_length (line);
+  for (int i = 1; i < len; i++)
+    vt_line_set_style (line, i, vt_line_get_style (line, i-1));
+}
+static inline void vt_line_replace_unichar (VtLine *line, int pos, uint32_t unichar)
+{
+  CtxString *string = (CtxString*)line;
+  ctx_string_replace_unichar (string, pos, unichar);
+}
 
-  int eid_len = strlen (eid);
+static inline void vt_line_remove (VtLine *line, int pos)
+{ 
+  CtxString *string = (CtxString*)line;
+  ctx_string_remove (string, pos);
 
-  if (eid_len > 50)
+  for (int i = pos; i < line->style_size-1; i++)
   {
-    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;
+    line->style[i] = line->style[i+1];
   }
+}
 
-  // we now have eid
 
-  if (ctx_eid_valid (ctx, eid, 0, 0))
-  {
-    ctx_texture (ctx, eid, 0.0, 0.0);
-  }
-  else
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
 
-  {
-    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;
+#endif
+/* 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/>.
+ */
 
-    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;
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
 
-    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 !__COSMOPOLITAN__
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
 
-    if (ctx->renderer && ctx->renderer->process)
-    {
-      ctx_process (ctx, commands);
-      free (commands);
-    }
-    else
-    {
-       ctx->drawlist.count += ctx_conts_for_entry (commands) + 1;
-    }
+int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
+#define mrg_unichar_to_utf8 ctx_unichar_to_utf8
+void ctx_string_init (CtxString *string, int initial_size);
 
-    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);
-  }
+VtLine *vt_line_new_with_size (const char *initial, int initial_size)
+{
+  VtLine *line = calloc (sizeof (VtLine), 1);
+  CtxString *string = (CtxString*)line;
+  ctx_string_init (string, initial_size);
+  if (initial)
+    { ctx_string_append_str (string, initial); }
+  line->style = calloc (sizeof (uint64_t), initial_size);
+  line->style_size = initial_size;
+  string->is_line = 1;
+  return line;
+}
 
-  if (ret_eid)
-  {
-    strcpy (ret_eid, eid);
-    ret_eid[64]=0;
-  }
+VtLine *vt_line_new (const char *initial)
+{
+  return vt_line_new_with_size (initial, 8);
 }
 
-void
-ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid)
+typedef struct VtPty
 {
-  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;
-  }
+  int        pty;
+  pid_t      pid;
+  int        done;
+} VtPty;
+ssize_t vtpty_read     (void *vtpty, void *buf, size_t count);
+ssize_t vtpty_write    (void *vtpty, const void *buf, size_t count);
+void    vtpty_resize   (void *vtpty, int cols, int rows,
+                        int px_width, int px_height);
+int     vtpty_waitdata (void  *vtpty, int timeout);
+extern  CtxList *vts;
+#define MAX_COLS 2048 // used for tabstops
 
-  if (ctx_eid_valid (ctx, eid , tw, th))
-  {
-     if (reid)
-     {
-       strcpy (reid, eid);
-     }
-     return;
-  }
 
-#ifdef STBI_INCLUDE_STB_IMAGE_H
-    CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
-  int w, h, components;
-  unsigned char *pixels = NULL;
+typedef struct AudioState
+{
+  int action;
+  int samplerate; // 8000
+  int channels;   // 1
+  int bits;       // 8
+  int type;       // 'u'    u-law  f-loat  s-igned u-nsigned
+  int buffer_size; // desired size of audiofragment in frames
+  // (both for feeding SDL and as desired chunking
+  //  size)
 
-  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
-}
+  int mic;        // <- should
+  //    request permisson,
+  //    and if gotten, start streaming
+  //    audio packets in the incoming direction
+  //
+  int encoding;   // 'a' ascci85 'b' base64
+  int compression; // '0': none , 'z': zlib   'o': opus(reserved)
+
+  int frames;
+
+  uint8_t *data;
+  int      data_size;
+} AudioState;
+
+typedef struct GfxState
+{
+  int action;
+  int id;
+  int buf_width;
+  int buf_height;
+  int format;
+  int compression;
+  int transmission;
+  int multichunk;
+  int buf_size;
+  int x;
+  int y;
+  int w;
+  int h;
+  int x_cell_offset;
+  int y_cell_offset;
+  int columns;
+  int rows;
+  int z_index;
+  int delete;
+
+  uint8_t *data;
+  int   data_size;
+} GfxState;
+
+struct _VT
+{
+  VtPty      vtpty;
+  int       id;
+  unsigned char buf[BUFSIZ]; // need one per vt
+  int keyrepeat;
+  int       lastx;
+  int       lasty;
+  int        result;
+  long       rev;
+  //SDL_Rect   dirty;
+  float  dirtpad;
+  float  dirtpad1;
+  float  dirtpad2;
+  float  dirtpad3;
+
+  void  *client;
+
+  ssize_t (*write)   (void *serial_obj, const void *buf, size_t count);
+  ssize_t (*read)    (void *serial_obj, void *buf, size_t count);
+  int     (*waitdata)(void *serial_obj, int timeout);
+  void    (*resize)  (void *serial_obj, int cols, int rows, int px_width, int px_height);
+
+
+  char     *title;
+  void    (*state) (VT *vt, int byte);
+
+  AudioState audio; // < want to move this one level up and share impl
+  GfxState   gfx;
+
+  CtxList   *saved_lines;
+  int       in_alt_screen;
+  int       saved_line_count;
+  CtxList   *lines;
+  int       line_count;
+  CtxList   *scrollback;
+  int       scrollback_count;
+  int       leds[4];
+  uint64_t  cstyle;
+
+  uint8_t   fg_color[3];
+  uint8_t   bg_color[3];
+
+  int       in_smooth_scroll;
+  int       smooth_scroll;
+  float     scroll_offset;
+  int       debug;
+  int       bell;
+  int       origin;
+  int       at_line_home;
+  int       charset[4];
+  int       saved_charset[4];
+  int       shifted_in;
+  int       reverse_video;
+  int       echo;
+  int       bracket_paste;
+  int       ctx_events;
+  int       font_is_mono;
+  int       palette_no;
+  int       has_blink; // if any of the set characters are blinking
+  // updated on each draw of the screen
+  
+  int can_launch;
 
-void
-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 unit_pixels;
+  int mouse;
+  int mouse_drag;
+  int mouse_all;
+  int mouse_decimal;
+
+
+  uint8_t    utf8_holding[64]; /* only 4 needed for utf8 - but it's purpose
+                                 is also overloaded for ctx journal command
+                                 buffering , and the bigger sizes for the svg-like
+                                 ctx parsing mode */
+  int        utf8_expected_bytes;
+  int        utf8_pos;
+
+
+  int        ref_len;
+  char       reference[16];
+  int        in_prev_match;
+  CtxParser *ctxp;
+  // text related data
+  float      letter_spacing;
+
+  float      word_spacing;
+  float      font_stretch;  // horizontal expansion
+  float      font_size_adjust;
+  // font-variant
+  // font-weight
+  // text-decoration
+
+  int        encoding;  // 0 = utf8 1=pc vga 2=ascii
+
+  int        local_editing; /* terminal operates without pty  */
+
+  int        insert_mode;
+  int        autowrap;
+  int        justify;
+  float      cursor_x;
+  int        cursor_y;
+  int        cols;
+  int        rows;
+  VtLine    *current_line;
+
+
+  int        cr_on_lf;
+  int        cursor_visible;
+  int        saved_x;
+  int        saved_y;
+  uint32_t   saved_style;
+  int        saved_origin;
+  int        cursor_key_application;
+  int        margin_top;
+  int        margin_bottom;
+  int        margin_left;
+  int        margin_right;
+
+  int        left_right_margin_mode;
+
+  int        scrollback_limit;
+  float      scroll;
+  int        scroll_on_input;
+  int        scroll_on_output;
+
+  char       *argument_buf;
+  int        argument_buf_len;
+  int        argument_buf_cap;
+  uint8_t    tabs[MAX_COLS];
+  int        inert;
+
+  int        width;
+  int        height;
+
+  int        cw; // cell width
+  int        ch; // cell height
+  float      font_to_cell_scale;
+  float      font_size; // when set with set_font_size, cw and ch are recomputed
+  float      line_spacing; // using line_spacing
+  float      scale_x;
+  float      scale_y;
+
+  int        ctx_pos;  // 1 is graphics above text, 0 or -1 is below text
+  Ctx       *root_ctx; /* only used for knowledge of top-level dimensions */
+
+  int        blink_state;
+
+  FILE      *log;
+
+  int cursor_down;
+
+  int select_begin_col;
+  int select_begin_row;
+  int select_start_col;
+  int select_start_row;
+  int select_end_col;
+  int select_end_row;
+  int select_begin_x;
+  int select_begin_y;
+  int select_active;
+
+  int popped;
+
+  /* used to make runs of background on one line be drawn
+   * as a single filled rectangle
+   */
+  int   bg_active;
+  float bg_x0;
+  float bg_y0;
+  float bg_width;
+  float bg_height;
+  uint8_t bg_rgba[4];
+};
+
+
+VT *vt_new (const char *command, int width, int height, float font_size, float line_spacing, int id, int 
can_launch);
+
+void vt_open_log (VT *vt, const char *path);
+
+void        vt_set_px_size        (VT *vt, int width, int height);
+void        vt_set_term_size      (VT *vt, int cols, int rows);
+
+int         vt_cw                 (VT *vt);
+int         vt_ch                 (VT *vt);
+void        vt_set_font_size      (VT *vt, float font_size);
+float       vt_get_font_size      (VT *vt);
+void        vt_set_line_spacing   (VT *vt, float line_spacing);
+
+const char *vt_find_shell_command (void);
+
+int         vt_keyrepeat          (VT *vt);
+
+int         vt_get_result         (VT *vt);
+int         vt_is_done            (VT *vt);
+int         vt_poll               (VT *vt, int timeout);
+long        vt_rev                (VT *vt);
+void        vt_destroy            (VT *vt);
+int         vt_has_blink (VT *vt);
+
+/* this is how mrg/mmm based key-events are fed into the vt engine
+ */
+void        vt_feed_keystring     (VT *vt, CtxEvent *event, const char *str);
+
+void        vt_paste              (VT *vt, const char *str);
+
+/* not needed when passing a commandline for command to
+ * run, but could be used for injecting commands, or
+ * output from stored shell commands/sessions to display
+ */
+//void        vt_feed_byte          (VT *vt, int byte);
+
+//)#define DEFAULT_SCROLLBACK   (1<<16)
+#define DEFAULT_SCROLLBACK   (1<<13)
+#define DEFAULT_ROWS         24
+#define DEFAULT_COLS         80
+
+int         vt_get_line_count       (VT *vt);
+
+pid_t       vt_get_pid              (VT *vt);
+
+const char *vt_get_line             (VT *vt, int no);
+
+void        vt_set_scrollback_lines (VT *vt, int scrollback_lines);
+int         vt_get_scrollback_lines (VT *vt);
+
+void        vt_set_scroll           (VT *vt, int scroll);
+int         vt_get_scroll           (VT *vt);
+
+int         vt_get_cols             (VT *vt);
+int         vt_get_rows             (VT *vt);
+
+char       *vt_get_selection        (VT *vt);
+int         vt_get_cursor_x         (VT *vt);
+int         vt_get_cursor_y         (VT *vt);
+
+void        vt_draw                 (VT *vt, Ctx *ctx, double x, double y);
+void        vt_register_events      (VT *vt, Ctx *ctx, double x0, double y0);
+
+void        vt_rev_inc              (VT *vt);
+
+int         vt_mic (VT *vt);
+void        vt_set_ctx (VT *vt, Ctx *ctx);  /* XXX: rename, this sets the parent/global ctx  */
+
+
+int         vt_get_local (VT *vt);           // this is a hack for the settings tab
+void        vt_set_local (VT *vt, int local);
+
+
+typedef enum VtMouseEvent
 {
-  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);
-    }
-  }
-}
+  VT_MOUSE_MOTION = 0,
+  VT_MOUSE_PRESS,
+  VT_MOUSE_DRAG,
+  VT_MOUSE_RELEASE,
+} VtMouseEvent;
 
-void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, float h)
+void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, int y, int px_x, int px_y);
+
+static ssize_t vt_write (VT *vt, const void *buf, size_t count)
 {
-  ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0);
+  if (!vt->write) { return 0; }
+  return vt->write (&vt->vtpty, buf, count);
 }
-
-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)
+static ssize_t vt_read (VT *vt, void *buf, size_t count)
 {
-  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);
-  }
+  if (!vt->read) { return 0; }
+  return vt->read (&vt->vtpty, buf, count);
+}
+static int vt_waitdata (VT *vt, int timeout)
+{
+  if (!vt->waitdata) { return 0; }
+  return vt->waitdata (&vt->vtpty, timeout);
+}
+static void vt_resize (VT *vt, int cols, int rows, int px_width, int px_height)
+{
+  if (vt && vt->resize)
+    { vt->resize (&vt->vtpty, cols, rows, px_width, px_height); }
 }
 
-void
-ctx_draw_image (Ctx *ctx, const char *path, float x, float y, float w, float h)
+/* 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/>. 
+ */
+
+
+#ifndef NO_SDL
+#include <SDL.h>
+#include <zlib.h>
+
+static int ydec (const void *srcp, void *dstp, int count)
 {
-  ctx_draw_image_clipped (ctx, path, x, y, w, h, 0,0,0,0);
+  const char *src = srcp;
+  char *dst = dstp;
+  int out_len = 0;
+  for (int i = 0; i < count; i ++)
+  {
+    int o = src[i];
+    switch (o)
+    {
+      case '=':
+              i++;
+              o = src[i];
+              o = (o-42-64) % 256;
+              break;
+      case '\n':
+      case '\033':
+      case '\r':
+      case '\0':
+              break;
+      default:
+              o = (o-42) % 256;
+              break;
+    }
+    dst[out_len++] = o;
+  }
+  dst[out_len]=0;
+  return out_len;
 }
 
-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)
-{
-  CtxEntry command = ctx_u8 (CTX_SET_PIXEL, r, g, b, a, 0, 0, 0, 0);
-  command.data.u16[2]=x;
-  command.data.u16[3]=y;
-  ctx_process (ctx, &command);
-}
+#ifndef NO_SDL
+static SDL_AudioDeviceID speaker_device = 0;
+#endif
 
-void
-ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1)
-{
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_LINEAR_GRADIENT, x0, y0),
-    ctx_f (CTX_CONT,            x1, y1)
-  };
-  ctx_process (ctx, command);
-}
+//#define AUDIO_CHUNK_SIZE 512
 
-void
-ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1, float r1)
+// our pcm queue is currently always 16 bit
+// signed stereo
+
+static int16_t pcm_queue[1<<18];
+static int     pcm_write_pos = 0;
+static int     pcm_read_pos  = 0;
+
+void terminal_queue_pcm (int16_t sample_left, int16_t sample_right)
 {
-  CtxEntry command[3]=
+  if (pcm_write_pos >= (1<<18)-1)
   {
-    ctx_f (CTX_RADIAL_GRADIENT, x0, y0),
-    ctx_f (CTX_CONT,            r0, x1),
-    ctx_f (CTX_CONT,            y1, r1)
-  };
-  ctx_process (ctx, command);
+    /*  TODO  :  fix cyclic buffer */
+    pcm_write_pos = 0;
+    pcm_read_pos  = 0;
+  }
+  pcm_queue[pcm_write_pos++]=sample_left;
+  pcm_queue[pcm_write_pos++]=sample_right;
 }
 
+float click_volume = 0.05;
 
-void ctx_preserve (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_PRESERVE);
-}
+void vt_feed_audio (VT *vt, void *samples, int bytes);
+int mic_device = 0;   // when non 0 we have an active mic device
 
-void ctx_fill (Ctx *ctx)
+
+/*  https://jonathanhays.me/2018/11/14/mu-law-and-a-law-compression-tutorial/
+ */
+
+#if 0
+static char MuLawCompressTable[256] =
 {
-  CTX_PROCESS_VOID (CTX_FILL);
-}
+   0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
 
-void ctx_stroke (Ctx *ctx)
+unsigned char LinearToMuLawSample(int16_t sample)
 {
-  CTX_PROCESS_VOID (CTX_STROKE);
-}
+  const int cBias = 0x84;
+  const int cClip = 32635;
+  int sign = (sample >> 8) & 0x80;
 
+  if (sign)
+    sample = (int16_t)-sample;
 
-static void ctx_empty (Ctx *ctx)
-{
-#if CTX_RASTERIZER
-  if (ctx->renderer == NULL)
-#endif
-    {
-      ctx->drawlist.count = 0;
-      ctx->drawlist.bitpack_pos = 0;
-    }
-}
+  if (sample > cClip)
+    sample = cClip;
 
-void _ctx_set_store_clear (Ctx *ctx)
-{
-  ctx->transformation |= CTX_TRANSFORMATION_STORE_CLEAR;
-}
+  sample = (int16_t)(sample + cBias);
 
-#if CTX_EVENTS
-static void
-ctx_event_free (void *event, void *user_data)
-{
-  free (event);
-}
+  int exponent = (int)MuLawCompressTable[(sample>>7) & 0xFF];
+  int mantissa = (sample >> (exponent+3)) & 0x0F;
 
-static void
-ctx_collect_events (CtxEvent *event, void *data, void *data2)
-{
-  Ctx *ctx = (Ctx*)data;
-  CtxEvent *copy;
-  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
+  int compressedByte = ~ (sign | (exponent << 4) | mantissa);
 
-#if CTX_EVENTS
-static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2);
+  return (unsigned char)compressedByte;
+}
 #endif
 
-void ctx_reset (Ctx *ctx)
+void vt_feed_audio (VT *vt, void *samples, int bytes)
 {
-        /* we do the callback reset first - maybe we need two cbs,
-         * one for before and one after default impl?
-         *
-         * tiled fb and sdl needs to sync
-         */
-  if (ctx->renderer && ctx->renderer->reset)
-    ctx->renderer->reset (ctx->renderer);
+  char buf[256];
+  AudioState *audio = &vt->audio;
+  uint8_t *data = samples;
+  int frames = bytes / (audio->bits/8) / audio->channels;
 
-  //CTX_PROCESS_VOID (CTX_RESET);
-  //if (ctx->transformation & CTX_TRANSFORMATION_STORE_CLEAR)
-  //  { return; }
-  ctx_empty (ctx);
-  ctx_state_init (&ctx->state);
-#if CTX_EVENTS
-  ctx_list_free (&ctx->events.items);
-  ctx->events.last_item = NULL;
+  if (audio->compression == 'z')
+  {
+    uLongf len = compressBound(bytes);
+    data = malloc (len);
+    int z_result = compress (data, &len, samples, len);
+    if (z_result != Z_OK)
+    {
+      char buf[256]= "\033_Ao=z;zlib error2\033\\";
+      vt_write (vt, buf, strlen(buf));
+      data = samples;
+    }
+    else
+    {
+      bytes = len;
+    }
+  }
 
-  if (ctx->events.ctx_get_event_enabled)
+  char *encoded = malloc (bytes * 2);
+  encoded[0]=0;
+  if (audio->encoding == 'a')
   {
-    ctx_clear_bindings (ctx);
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_PRESS, _ctx_bindings_key_press, ctx, ctx,
-                     NULL, NULL);
+    ctx_a85enc (data, encoded, bytes);
+  }
+  else /* if (audio->encoding == 'b')  */
+  {
+    ctx_bin2base64 (data, bytes, encoded);
+  }
 
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_UP, ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_DOWN, ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
+  sprintf (buf, "\033[_Af=%i;", frames);
+  vt_write (vt, buf, strlen (buf));
+  vt_write (vt, encoded, strlen(encoded));
+  free (encoded);
 
-    ctx_listen_full (ctx, 0, 0, ctx->events.width, ctx->events.height,
-                     (CtxEventType)(CTX_PRESS|CTX_RELEASE|CTX_MOTION),
-                     ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-  }
-#endif
-}
+  if (data != samples)
+    free (data);
 
-void ctx_begin_path (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_BEGIN_PATH);
+  //vt_write (vt, samples, bytes);
+  buf[0]='\033';
+  buf[1]='\\';
+  buf[2]=0;
+  vt_write (vt, buf, 2);
 }
 
-void ctx_clip (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_CLIP);
-}
+#define MIC_BUF_LEN 40960
 
-void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
+uint8_t mic_buf[MIC_BUF_LEN];
+int     mic_buf_pos = 0;
 
-void ctx_save (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_SAVE);
-}
-void ctx_restore (Ctx *ctx)
+static void mic_callback(void*     userdata,
+                         uint8_t * stream,
+                         int       len)
 {
-  CTX_PROCESS_VOID (CTX_RESTORE);
-}
+  AudioState *audio = userdata;
+  int16_t *sstream = (void*)stream;
+  int frames;
+  int channels = audio->channels;
 
-void ctx_start_group (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_START_GROUP);
-}
+  frames = len / 2;
 
-void ctx_end_group (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_END_GROUP);
+  if (audio->bits == 8)
+  {
+    if (audio->type == 'u')
+    {
+      for (int i = 0; i < frames; i++)
+      {
+        for (int c = 0; c < channels; c++)
+        {
+          mic_buf[mic_buf_pos++] = LinearToMuLawSample (sstream[i]);
+          if (mic_buf_pos >= MIC_BUF_LEN - 4)
+            mic_buf_pos = 0;
+        }
+      }
+    }
+    else
+    {
+      for (int i = 0; i < frames; i++)
+      {
+        for (int c = 0; c <  audio->channels; c++)
+        {
+          mic_buf[mic_buf_pos++] = (sstream[i]) / 256;
+          if (mic_buf_pos >= MIC_BUF_LEN - 4)
+            mic_buf_pos = 0;
+        }
+      }
+    }
+  }
+  else
+  {
+    for (int i = 0; i < frames; i++)
+    {
+      for (int c = 0; c < audio->channels; c++)
+      {
+        *((int16_t*)(&mic_buf[mic_buf_pos])) = (sstream[i]);
+        mic_buf_pos+=2;
+        if (mic_buf_pos >= MIC_BUF_LEN - 4)
+          mic_buf_pos = 0;
+      }
+    }
+  }
 }
 
-void ctx_line_width (Ctx *ctx, float x)
+static long int ticks (void)
 {
-  if (ctx->state.gstate.line_width != x)
-    CTX_PROCESS_F1 (CTX_LINE_WIDTH, x);
+  struct timeval tp;
+  gettimeofday(&tp, NULL);
+  return tp.tv_sec * 1000 + tp.tv_usec / 1000;
 }
 
-float ctx_get_miter_limit (Ctx *ctx)
-{
-  return ctx->state.gstate.miter_limit;
-}
+static long int silence_start = 0;
 
-float ctx_get_line_dash_offset (Ctx *ctx)
+static void sdl_audio_init ()
 {
-  return ctx->state.gstate.line_dash_offset;
+  static int done = 0;
+  if (!done)
+  {
+#ifndef NO_SDL
+  if (SDL_Init(SDL_INIT_AUDIO) < 0)
+  {
+    fprintf (stderr, "sdl audio init fail\n");
+  }
+#endif
+  done = 1;
+  }
 }
 
-void ctx_line_dash_offset (Ctx *ctx, float x)
+void vt_audio_task (VT *vt, int click)
 {
-  if (ctx->state.gstate.line_dash_offset != x)
-    CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x);
-}
+  if (!vt) return;
+  AudioState *audio = &vt->audio;
+#ifndef NO_SDL
 
-int ctx_get_image_smoothing (Ctx *ctx)
-{
-  return ctx->state.gstate.image_smoothing;
-}
+  if (audio->mic)
+  {
+    if (mic_device == 0)
+    {
+      SDL_AudioSpec spec_want, spec_got;
+      sdl_audio_init ();
 
-void ctx_image_smoothing (Ctx *ctx, int enabled)
-{
-  if (ctx_get_image_smoothing (ctx) != enabled)
-    CTX_PROCESS_U8 (CTX_IMAGE_SMOOTHING, enabled);
-}
+      spec_want.freq     = audio->samplerate;
+      spec_want.channels = 1;
+      spec_want.format   = AUDIO_S16;
+      spec_want.samples  = audio->buffer_size;
+      spec_want.callback = mic_callback;
+      spec_want.userdata = audio;
+      mic_device = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, SDL_TRUE), 1, &spec_want, &spec_got, 0);
 
+      SDL_PauseAudioDevice(mic_device, 0);
+    }
 
-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);
-}
+    if (mic_buf_pos)
+    {
+      SDL_LockAudioDevice (mic_device);
+      vt_feed_audio (vt, mic_buf, mic_buf_pos);
+      mic_buf_pos = 0;
+      SDL_UnlockAudioDevice (mic_device);
+    }
+  }
+  else
+  {
+    if (mic_device)
+    {
+      SDL_PauseAudioDevice(mic_device, 1);
+      SDL_CloseAudioDevice(mic_device);
+      mic_device = 0;
+    }
+  }
 
-void ctx_shadow_blur (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_blur != x)
-#endif
-    CTX_PROCESS_F1 (CTX_SHADOW_BLUR, x);
-}
+  int free_frames = audio->buffer_size - SDL_GetQueuedAudioSize(speaker_device);
+  int queued = (pcm_write_pos - pcm_read_pos)/2; // 2 for stereo
+  //if (free_frames > 6) free_frames -= 4;
+  int frames = queued;
 
-void ctx_shadow_rgba (Ctx *ctx, float r, float g, float b, float a)
-{
-  CtxEntry command[3]=
+  if (frames > free_frames) frames = free_frames;
+  if (frames > 0)
   {
-    ctx_f (CTX_SHADOW_COLOR, CTX_RGBA, r),
-    ctx_f (CTX_CONT, g, b),
-    ctx_f (CTX_CONT, a, 0)
-  };
-  ctx_process (ctx, command);
-}
+    if (speaker_device == 0)
+    {
+      SDL_AudioSpec spec_want, spec_got;
+      sdl_audio_init ();
 
-void ctx_shadow_offset_x (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_offset_x != x)
-#endif
-    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_X, x);
-}
+       spec_want.freq = audio->samplerate;
+       if (audio->bits == 8 && audio->type == 'u')
+       {
+         spec_want.format = AUDIO_S16;
+         spec_want.channels = 2;
+       }
+       else if (audio->bits == 8 && audio->type == 's')
+       {
+         spec_want.format = AUDIO_S8;
+         spec_want.channels = audio->channels;
+       }
+       else if (audio->bits == 16 && audio->type == 's')
+       {
+         spec_want.format = AUDIO_S16;
+         spec_want.channels = audio->channels;
+       }
+       else
+       {
+         spec_want.format = AUDIO_S16; // XXX  : error
+         spec_want.channels = audio->channels;
+       }
 
-void ctx_shadow_offset_y (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_offset_y != x)
+       /* In SDL we always set 16bit stereo, but with the
+        * requested sample rate.
+        */
+      spec_want.format = AUDIO_S16;
+      spec_want.channels = 2;
+
+      spec_want.samples = audio->buffer_size;
+      spec_want.callback = NULL;
+
+      speaker_device = SDL_OpenAudioDevice (NULL, 0, &spec_want, &spec_got, 0);
+      if (!speaker_device){
+        fprintf (stderr, "sdl openaudiodevice fail\n");
+      }
+      SDL_PauseAudioDevice (speaker_device, 0);
+    }
+
+#if 0
+    {
+       int i;
+       unsigned char *b = (void*)(&pcm_queue[pcm_read_pos]);
+       for (i = 0; i < frames * 4; i++)
+       {
+         if ((b[i] > ' ') && (b[i] <= '~'))
+           fprintf (stderr, "[%c]", b[i]);
+         else
+           fprintf (stderr, "[%i]", b[i]);
+       }
+    }
+#endif
+    SDL_QueueAudio (speaker_device, (void*)&pcm_queue[pcm_read_pos], frames * 4);
+    pcm_read_pos += frames*2;
+    silence_start = ticks();
+  }
+  else
+  {
+    if (speaker_device &&  (ticks() - silence_start >  2000))
+    {
+      SDL_PauseAudioDevice(speaker_device, 1);
+      SDL_CloseAudioDevice(speaker_device);
+      speaker_device = 0;
+    }
+  }
 #endif
-    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_Y, x);
 }
 
-void
-ctx_global_alpha (Ctx *ctx, float global_alpha)
-{
-  if (ctx->state.gstate.global_alpha_f != global_alpha)
-    CTX_PROCESS_F1 (CTX_GLOBAL_ALPHA, global_alpha);
-}
+void terminal_queue_pcm (int16_t sample_left, int16_t sample_right);
 
-float
-ctx_get_global_alpha (Ctx *ctx)
-{
-  return ctx->state.gstate.global_alpha_f;
-}
+static unsigned char vt_bell_audio[] = {
+#if 1
+  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+#else
+  0x7e, 0xfe, 0x7e, 0x7d, 0x7e, 0x7e, 0x7e, 0x7d, 0x7e, 0x7e, 0x7e, 0xff,
+  0xff, 0xfe, 0xfe, 0x7e, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd,
+  0xfe, 0xfe, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d,
+  0xfe, 0x7e, 0x7e, 0x7e, 0x7e, 0xfd, 0xfd, 0x7e, 0x7e, 0xfd, 0xfe, 0xfe,
+  0xfe, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0xfe, 0xfe, 0xff, 0xfe,
+  0xfe, 0xfe, 0x7d, 0x7c, 0xfb, 0xfa, 0xfc, 0xfd, 0xfc, 0x76, 0x75, 0xfa,
+  0xfb, 0x7b, 0xfc, 0xef, 0xf6, 0x77, 0x6d, 0x7b, 0xf8, 0x78, 0x78, 0xfa,
+  0xf7, 0xfd, 0xfd, 0xfc, 0xfc, 0xfa, 0xf5, 0xf7, 0x7d, 0x7b, 0x78, 0x77,
+  0x7c, 0x6f, 0x7b, 0xf5, 0xfb, 0x7b, 0x7c, 0x78, 0x76, 0xea, 0xf2, 0x6d,
+  0xfd, 0xed, 0x7a, 0x6d, 0x6e, 0x71, 0xfe, 0x76, 0x6d, 0xfb, 0xef, 0x7e,
+  0xfa, 0xef, 0xec, 0xed, 0xf8, 0xf0, 0xea, 0xf9, 0x70, 0x7c, 0x7c, 0x6b,
+  0x6d, 0x75, 0xfb, 0xf1, 0xf9, 0xfe, 0xec, 0xea, 0x7c, 0x75, 0xff, 0xfb,
+  0x7d, 0x77, 0x7a, 0x71, 0x6e, 0x6c, 0x6e, 0x7b, 0x7e, 0x7a, 0x7c, 0xf4,
+  0xf9, 0x7b, 0x7b, 0xfa, 0xfe, 0x73, 0x79, 0xfe, 0x7b, 0x76, 0xfe, 0xf3,
+  0xf9, 0x76, 0x77, 0x7e, 0x7e, 0x7d, 0x7c, 0xf9, 0xee, 0xf2, 0x7d, 0xf8,
+  0xec, 0xee, 0xf7, 0xfa, 0xf7, 0xf6, 0xfd, 0x77, 0x75, 0x7b, 0xfa, 0xfe,
+  0x78, 0x79, 0x7c, 0x76, 0x7e, 0xf7, 0xfb, 0xf5, 0xf6, 0x75, 0x6f, 0x74,
+  0x6e, 0x6e, 0x6d, 0x6c, 0x7a, 0xf9, 0x75, 0x77, 0xf4, 0xf0, 0xf0, 0xf1,
+  0xef, 0xf3, 0xf6, 0xfd, 0xfc, 0xfb, 0xfd, 0xfc, 0xf6, 0xf8, 0xfb, 0xf9,
+  0xfa, 0xfd, 0xfb, 0xfc, 0x7a, 0x7c, 0x77, 0x75, 0x78, 0x7a, 0x7a, 0x78,
+  0x7a, 0xfa, 0xf9, 0x7c, 0xff, 0xfb, 0x7d, 0x77, 0x73, 0x6c, 0x6e, 0x7b,
+  0xfc, 0xfe, 0x7e, 0xfb, 0xf1, 0xeb, 0xee, 0xf6, 0xf6, 0xef, 0xf7, 0x7c,
+  0x76, 0x76, 0x7b, 0x7a, 0x7b, 0x73, 0x73, 0x7c, 0x79, 0x70, 0x79, 0xfb,
+  0xfd, 0xf8, 0xf9, 0xfc, 0xfc, 0xf8, 0xfb, 0xff, 0xfc, 0xf9, 0x75, 0x6f,
+  0x74, 0xfe, 0xff, 0xfd, 0x7d, 0xf5, 0xef, 0xee, 0xf8, 0xfd, 0xfd, 0xf3,
+  0xfa, 0xfe, 0xfe, 0x7c, 0x77, 0x7a, 0xfb, 0x79, 0x7e, 0x7b, 0xfd, 0x6d,
+  0xfc, 0x7a, 0xf0, 0x74, 0xee, 0x79, 0xea, 0x79, 0xf9, 0x6d, 0xf7, 0x71,
+  0x79, 0x76, 0x7c, 0x77, 0x6f, 0xf3, 0x6c, 0xe8, 0x67, 0xe3, 0x5e, 0xdc,
+  0x58, 0xd8, 0x4e, 0xce, 0x46, 0xc5, 0x40, 0x67, 0xba, 0x49, 0xac, 0x26,
+  0xba, 0x3e, 0xc5, 0xc8, 0x2b, 0xa8, 0x32, 0xbd, 0xe4, 0x3e, 0xb7, 0x3b,
+  0xb7, 0x3a, 0x33, 0xab, 0x3f, 0xc8, 0x46, 0x5f, 0xb7, 0x69, 0xd4, 0x3d,
+  0xc0, 0x4c, 0xf2, 0xdb, 0x3b, 0xdd, 0x69, 0xc5, 0x5f, 0xd8, 0xd8, 0xda,
+  0xc6, 0x39, 0xba, 0x3f, 0x35, 0xb3, 0x3e, 0xbb, 0x4a, 0x4a, 0xe7, 0x60,
+  0xae, 0x2c, 0xcb, 0x53, 0x45, 0xaf, 0x2a, 0xae, 0x3e, 0x4a, 0xae, 0x2a,
+  0xad, 0x38, 0xcc, 0xbb, 0x36, 0xae, 0x2c, 0xc6, 0xce, 0x38, 0xb1, 0x2f,
+  0xb9, 0x54, 0x7c, 0xb3, 0x28, 0xae, 0x3d, 0xcf, 0xbb, 0x2e, 0xb4, 0x41,
+  0xc6, 0x78, 0x39, 0xbc, 0x41, 0xc8, 0x59, 0x5b, 0xc7, 0x43, 0xbc, 0x45,
+  0xf3, 0xdc, 0x69, 0xd6, 0x48, 0xc9, 0x4e, 0xd9, 0x59, 0x61, 0xde, 0x4b,
+  0xc9, 0x44, 0xc8, 0xf5, 0x43, 0xc5, 0x37, 0xba, 0x65, 0x4d, 0xc8, 0x31,
+  0xaf, 0x47, 0xdb, 0xd6, 0x36, 0xad, 0x37, 0xbb, 0x61, 0x3a, 0xae, 0x2d,
+  0xb4, 0x47, 0x49, 0xb2, 0x30, 0xac, 0x3a, 0xcd, 0xbc, 0x2e, 0xaf, 0x32,
+  0xbd, 0xd7, 0x34, 0xaf, 0x32, 0xbb, 0x55, 0x4a, 0xb4, 0x30, 0xbb, 0x40,
+  0xeb, 0xbf, 0x39, 0xba, 0x3a, 0xd6, 0xd3, 0x48, 0xc0, 0x3b, 0xce, 0x5e,
+  0xe7, 0xd3, 0x46, 0xcb, 0x4c, 0xce, 0x74, 0x7e, 0x7e, 0x55, 0xcf, 0x44,
+  0xc4, 0x5b, 0x7c, 0xd3, 0x3f, 0xbc, 0x44, 0xcb, 0xfa, 0x46, 0xb9, 0x37,
+  0xb8, 0x51, 0x54, 0xbe, 0x33, 0xb1, 0x3d, 0xce, 0xc4, 0x34, 0xaf, 0x2f,
+  0xbd, 0xf8, 0x37, 0xb0, 0x2d, 0xb1, 0x4c, 0x4a, 0xb3, 0x2c, 0xb0, 0x3c,
+  0xe4, 0xbf, 0x2f, 0xaf, 0x35, 0xc0, 0xdb, 0x39, 0xb3, 0x31, 0xbb, 0x5d,
+  0x4c, 0xb8, 0x37, 0xb9, 0x48, 0xe8, 0xc7, 0x3d, 0xba, 0x43, 0xce, 0xdd,
+  0x52, 0xc6, 0x46, 0xce, 0x55, 0xdf, 0xe8, 0x52, 0xd5, 0x48, 0xca, 0x4d,
+  0xef, 0x68, 0x4c, 0xc7, 0x42, 0xc2, 0x49, 0x78, 0xce, 0x3e, 0xb9, 0x3c,
+  0xc8, 0xef, 0x43, 0xb7, 0x35, 0xb8, 0x4a, 0x53, 0xb8, 0x32, 0xaf, 0x3b,
+  0xde, 0xc1, 0x34, 0xaf, 0x32, 0xc3, 0xde, 0x3b, 0xaf, 0x2e, 0xb6, 0x4e,
+  0x48, 0xb4, 0x2e, 0xb2, 0x3d, 0xf0, 0xbf, 0x33, 0xb2, 0x37, 0xc8, 0xd9,
+  0x3d, 0xb5, 0x36, 0xbc, 0x56, 0x4f, 0xbc, 0x39, 0xbc, 0x47, 0xf6, 0xcf,
+  0x44, 0xbf, 0x46, 0xce, 0x68, 0x5b, 0xd0, 0x4a, 0xcc, 0x4d, 0xd3, 0x60,
+  0x6a, 0xcf, 0x49, 0xc8, 0x45, 0xd0, 0x7b, 0x58, 0xc3, 0x3c, 0xbf, 0x48,
+  0xe2, 0xc9, 0x3b, 0xb7, 0x39, 0xc5, 0xdb, 0x40, 0xb6, 0x31, 0xb9, 0x50,
+  0x50, 0xb9, 0x2f, 0xb3, 0x3b, 0xdc, 0xbf, 0x33, 0xaf, 0x32, 0xc1, 0xd6,
+  0x3b, 0xb0, 0x2f, 0xb8, 0x54, 0x4a, 0xb6, 0x30, 0xb4, 0x3f, 0xfd, 0xc0,
+  0x36, 0xb5, 0x39, 0xcc, 0xd9, 0x41, 0xb9, 0x39, 0xc2, 0x59, 0x57, 0xc1,
+  0x3e, 0xc2, 0x49, 0xe2, 0xd7, 0x4c, 0xcb, 0x47, 0xcf, 0x5b, 0xec, 0xe0,
+  0x53, 0xcb, 0x4b, 0xca, 0x55, 0xf6, 0xdb, 0x48, 0xc0, 0x43, 0xc9, 0x5f,
+  0x54, 0xc0, 0x3c, 0xbb, 0x43, 0xe8, 0xc8, 0x39, 0xb5, 0x39, 0xc6, 0xde,
+  0x3d, 0xb4, 0x32, 0xba, 0x4f, 0x4c, 0xb9, 0x30, 0xb2, 0x3c, 0xec, 0xc1,
+  0x33, 0xaf, 0x35, 0xc4, 0xd7, 0x3a, 0xb2, 0x31, 0xba, 0x56, 0x48, 0xb9,
+  0x33, 0xb7, 0x44, 0x7e, 0xc3, 0x39, 0xb7, 0x3d, 0xcd, 0xe3, 0x42, 0xbd,
+  0x3d, 0xc2, 0x58, 0x5d, 0xcb, 0x43, 0xc4, 0x4c, 0xd8, 0xf8, 0x58, 0xcd,
+  0x4c, 0xcb, 0x4e, 0xda, 0x71, 0x5c, 0xcc, 0x46, 0xc4, 0x49, 0xdc, 0xdc,
+  0x46, 0xbe, 0x3d, 0xc4, 0x59, 0x53, 0xbe, 0x38, 0xb8, 0x41, 0xe1, 0xc5,
+  0x39, 0xb3, 0x38, 0xc4, 0xde, 0x3d, 0xb2, 0x32, 0xb9, 0x4e, 0x4b, 0xb7,
+  0x30, 0xb3, 0x3d, 0xf2, 0xbf, 0x33, 0xb1, 0x36, 0xc9, 0xd9, 0x3a, 0xb4,
+  0x33, 0xbc, 0x58, 0x49, 0xba, 0x36, 0xb9, 0x46, 0x7e, 0xc8, 0x3c, 0xba,
+  0x3f, 0xcd, 0xe8, 0x4b, 0xc1, 0x41, 0xc7, 0x57, 0xfe, 0xd3, 0x4e, 0xc9,
+  0x4d, 0xd0, 0x5e, 0x7c, 0xda, 0x4e, 0xca, 0x47, 0xcd, 0x5b, 0x68, 0xcc,
+  0x40, 0xbf, 0x42, 0xd2, 0xe4, 0x42, 0xbd, 0x3a, 0xbf, 0x56, 0x50, 0xbd,
+  0x36, 0xb6, 0x40, 0xe2, 0xc5, 0x36, 0xb2, 0x37, 0xc5, 0xde, 0x3c, 0xb3,
+  0x32, 0xba, 0x52, 0x4a, 0xb7, 0x31, 0xb4, 0x3f, 0xef, 0xbf, 0x34, 0xb2,
+  0x39, 0xc8, 0xd3, 0x3c, 0xb6, 0x37, 0xbe, 0x5c, 0x4c, 0xbd, 0x39, 0xbc,
+  0x49, 0xf2, 0xcc, 0x3f, 0xbf, 0x44, 0xcf, 0xfd, 0x51, 0xca, 0x48, 0xcb,
+  0x54, 0xe4, 0xeb, 0x57, 0xcf, 0x4d, 0xcc, 0x4f, 0xe0, 0xee, 0x51, 0xc7,
+  0x44, 0xc6, 0x4f, 0x78, 0xcc, 0x3f, 0xbd, 0x3e, 0xce, 0xe5, 0x42, 0xba,
+  0x38, 0xbe, 0x50, 0x4f, 0xbb, 0x35, 0xb6, 0x3e, 0xe8, 0xc2, 0x36, 0xb2,
+  0x37, 0xc6, 0xda, 0x3c, 0xb3, 0x32, 0xba, 0x55, 0x4a, 0xb7, 0x33, 0xb5,
+  0x41, 0x7e, 0xbf, 0x37, 0xb4, 0x3b, 0xcd, 0xd8, 0x3e, 0xb8, 0x39, 0xc2,
+  0x5b, 0x4f, 0xc0, 0x3c, 0xbf, 0x4a, 0xee, 0xd1, 0x47, 0xc5, 0x47, 0xd0,
+  0x68, 0x63, 0xd0, 0x4d, 0xcd, 0x4e, 0xd6, 0x67, 0x68, 0xd6, 0x4b, 0xc9,
+  0x4a, 0xd1, 0x6e, 0x52, 0xc5, 0x3f, 0xc0, 0x4b, 0xfd, 0xcb, 0x3d, 0xba,
+  0x3d, 0xcc, 0xe2, 0x41, 0xb8, 0x37, 0xbc, 0x53, 0x4e, 0xba, 0x34, 0xb6,
+  0x3f, 0xee, 0xc1, 0x36, 0xb2, 0x38, 0xc8, 0xd6, 0x3c, 0xb3, 0x34, 0xbc,
+  0x58, 0x49, 0xb9, 0x34, 0xb7, 0x44, 0x73, 0xc3, 0x38, 0xb7, 0x3d, 0xce,
+  0xd9, 0x40, 0xbc, 0x3c, 0xc5, 0x5e, 0x55, 0xc6, 0x40, 0xc3, 0x4d, 0xe5,
+  0xde, 0x4d, 0xca, 0x4b, 0xce, 0x5c, 0xfa, 0xe1, 0x54, 0xcd, 0x4d, 0xcd,
+  0x56, 0xf3, 0xd9, 0x4a, 0xc4, 0x44, 0xcb, 0x67, 0x53, 0xc3, 0x3d, 0xbe,
+  0x48, 0xf0, 0xca, 0x3c, 0xb8, 0x3b, 0xca, 0xdf, 0x3f, 0xb7, 0x36, 0xbc,
+  0x54, 0x4c, 0xb9, 0x34, 0xb6, 0x40, 0xf7, 0xc1, 0x36, 0xb3, 0x39, 0xca,
+  0xd6, 0x3c, 0xb4, 0x35, 0xbe, 0x5b, 0x49, 0xba, 0x36, 0xba, 0x47, 0x6f,
+  0xc5, 0x3b, 0xba, 0x3f, 0xd2, 0xdd, 0x46, 0xbe, 0x3f, 0xc9, 0x5c, 0x5d,
+  0xcc, 0x47, 0xc8, 0x4e, 0xdd, 0xf5, 0x5a, 0xd1, 0x4e, 0xcf, 0x52, 0xde,
+  0x7d, 0x5c, 0xcf, 0x49, 0xc9, 0x4d, 0xdd, 0xde, 0x49, 0xc1, 0x3f, 0xc6,
+  0x5d, 0x53, 0xc0, 0x3b, 0xbc, 0x46, 0xeb, 0xc8, 0x3b, 0xb7, 0x3b, 0xc8,
+  0xde, 0x3e, 0xb6, 0x35, 0xbb, 0x57, 0x4c, 0xb9, 0x34, 0xb6, 0x42, 0xff,
+  0xc1, 0x36, 0xb4, 0x3a, 0xcc, 0xd7, 0x3d, 0xb7, 0x37, 0xbf, 0x5e, 0x4a,
+  0xbc, 0x38, 0xbc, 0x4a, 0x6e, 0xc9, 0x3e, 0xbe, 0x43, 0xd5, 0xe2, 0x4b,
+  0xc5, 0x45, 0xcb, 0x5e, 0x6e, 0xd6, 0x4e, 0xcd, 0x51, 0xd7, 0x65, 0x74,
+  0xdc, 0x54, 0xcd, 0x4d, 0xd1, 0x5e, 0x6b, 0xcf, 0x46, 0xc4, 0x47, 0xd7,
+  0xe3, 0x48, 0xbe, 0x3d, 0xc3, 0x58, 0x54, 0xbe, 0x39, 0xba, 0x43, 0xee,
+  0xc7, 0x3a, 0xb6, 0x3a, 0xc9, 0xdc, 0x3e, 0xb5, 0x35, 0xbd, 0x57, 0x4b,
+  0xb9, 0x34, 0xb7, 0x43, 0x6f, 0xc1, 0x38, 0xb6, 0x3c, 0xcf, 0xd3, 0x3e,
+  0xb8, 0x3a, 0xc3, 0x64, 0x4c, 0xbe, 0x3c, 0xbf, 0x4d, 0x72, 0xcc, 0x42,
+  0xc1, 0x48, 0xd9, 0xed, 0x51, 0xcc, 0x4b, 0xcf, 0x5a, 0xef, 0xe8, 0x59,
+  0xd3, 0x50, 0xd1, 0x58, 0xe1, 0xec, 0x56, 0xcc, 0x49, 0xca, 0x55, 0x7c,
+  0xcf, 0x44, 0xbf, 0x43, 0xcf, 0xe5, 0x47, 0xbc, 0x3b, 0xbf, 0x56, 0x52,
+  0xbe, 0x38, 0xb9, 0x42, 0xee, 0xc6, 0x39, 0xb6, 0x3a, 0xca, 0xdc, 0x3d,
+  0xb6, 0x36, 0xbd, 0x59, 0x4a, 0xba, 0x35, 0xb9, 0x45, 0x6b, 0xc2, 0x39,
+  0xb8, 0x3d, 0xd2, 0xd5, 0x3f, 0xbb, 0x3c, 0xc6, 0x69, 0x4f, 0xc1, 0x3f,
+  0xc3, 0x50, 0x7d, 0xd0, 0x49, 0xc8, 0x4c, 0xd8, 0x7d, 0x5d, 0xd4, 0x4f,
+  0xd2, 0x57, 0xde, 0x6e, 0x69, 0xda, 0x50, 0xcd, 0x4e, 0xd6, 0x71, 0x59,
+  0xca, 0x44, 0xc5, 0x4d, 0xf3, 0xce, 0x41, 0xbd, 0x3f, 0xcd, 0xe5, 0x45,
+  0xbb, 0x3a, 0xbf, 0x55, 0x51, 0xbd, 0x37, 0xb9, 0x43, 0xf1, 0xc5, 0x39,
+  0xb6, 0x3b, 0xcc, 0xd9, 0x3d, 0xb7, 0x37, 0xbf, 0x5d, 0x49, 0xbb, 0x37,
+  0xba, 0x48, 0x69, 0xc4, 0x3b, 0xba, 0x40, 0xd5, 0xd7, 0x42, 0xbd, 0x3f,
+  0xc9, 0x69, 0x52, 0xc7, 0x44, 0xc7, 0x52, 0xfa, 0xda, 0x4f, 0xcd, 0x4f,
+  0xd8, 0x66, 0x72, 0xdf, 0x59, 0xd4, 0x52, 0xd6, 0x5d, 0xef, 0xde, 0x4f,
+  0xca, 0x49, 0xce, 0x64, 0x5a, 0xc8, 0x40, 0xc1, 0x4a, 0xec, 0xcd, 0x3f,
+  0xbc, 0x3e, 0xcc, 0xe5, 0x43, 0xba, 0x39, 0xbe, 0x55, 0x4f, 0xbc, 0x37,
+  0xb9, 0x43, 0xf8, 0xc4, 0x39, 0xb6, 0x3b, 0xcd, 0xd7, 0x3e, 0xb7, 0x38,
+  0xc0, 0x60, 0x4b, 0xbc, 0x39, 0xbc, 0x4b, 0x6b, 0xc6, 0x3d, 0xbd, 0x43,
+  0xd9, 0xda, 0x47, 0xc1, 0x42, 0xcd, 0x66, 0x5a, 0xcd, 0x48, 0xcc, 0x54,
+  0xe9, 0xe9, 0x59, 0xd5, 0x54, 0xd5, 0x5c, 0xe4, 0xfb, 0x61, 0xd4, 0x4f,
+  0xcd, 0x51, 0xdf, 0xe3, 0x4e, 0xc5, 0x45, 0xca, 0x5e, 0x5b, 0xc6, 0x3e,
+  0xbf, 0x48, 0xea, 0xcc, 0x3e, 0xbb, 0x3d, 0xcb, 0xe4, 0x42, 0xba, 0x38,
+  0xbe, 0x56, 0x4e, 0xbc, 0x37, 0xb9, 0x44, 0x7b, 0xc4, 0x39, 0xb7, 0x3c,
+  0xcf, 0xd7, 0x3e, 0xb9, 0x3a, 0xc2, 0x62, 0x4c, 0xbd, 0x3b, 0xbe, 0x4d,
+  0x6b, 0xc9, 0x3f, 0xbf, 0x46, 0xd9, 0xde, 0x4b, 0xc5, 0x47, 0xce, 0x63,
+  0x65, 0xd3, 0x4e, 0xcf, 0x55, 0xdf, 0x74, 0x67, 0xdc, 0x55, 0xd3, 0x54,
+  0xda, 0x68, 0x67, 0xd5, 0x4c, 0xca, 0x4d, 0xdc, 0xe7, 0x4d, 0xc4, 0x42,
+  0xc8, 0x5b, 0x59, 0xc4, 0x3d, 0xbe, 0x47, 0xec, 0xcc, 0x3d, 0xba, 0x3d,
+  0xcc, 0xe1, 0x42, 0xba, 0x39, 0xbf, 0x5a, 0x4f, 0xbc, 0x38, 0xba, 0x46,
+  0x7d, 0xc5, 0x3b, 0xb9, 0x3e, 0xd0, 0xd8, 0x40, 0xbb, 0x3c, 0xc5, 0x63,
+  0x4d, 0xc0, 0x3d, 0xc1, 0x4e, 0x6e, 0xcd, 0x44, 0xc4, 0x49, 0xdb, 0xec,
+  0x50, 0xcc, 0x4a, 0xd1, 0x5c, 0x7b, 0xde, 0x56, 0xd2, 0x54, 0xd8, 0x62,
+  0xf2, 0xe2, 0x58, 0xcf, 0x4e, 0xd0, 0x5d, 0x72, 0xd3, 0x4a, 0xc5, 0x49,
+  0xd6, 0xe8, 0x4b, 0xc0, 0x3f, 0xc5, 0x5b, 0x58, 0xc2, 0x3c, 0xbd, 0x47,
+  0xee, 0xca, 0x3d, 0xba, 0x3d, 0xcd, 0xdf, 0x41, 0xba, 0x3a, 0xc0, 0x5b,
+  0x4d, 0xbd, 0x39, 0xbc, 0x48, 0x73, 0xc6, 0x3c, 0xbb, 0x3f, 0xd4, 0xd9,
+  0x43, 0xbd, 0x3e, 0xc8, 0x67, 0x50, 0xc4, 0x40, 0xc4, 0x51, 0x7b, 0xd1,
+  0x4a, 0xc8, 0x4d, 0xd9, 0xf6, 0x5c, 0xd0, 0x51, 0xd2, 0x5c, 0xe8, 0xf2,
+  0x62, 0xd8, 0x55, 0xd4, 0x56, 0xe1, 0x7c, 0x59, 0xcf, 0x49, 0xcc, 0x53,
+  0x7a, 0xd4, 0x46, 0xc4, 0x45, 0xd5, 0xef, 0x49, 0xc0, 0x3d, 0xc5, 0x57,
+  0x55, 0xc2, 0x3b, 0xbd, 0x47, 0xed, 0xc9, 0x3d, 0xb9, 0x3e, 0xcc, 0xdb,
+  0x43, 0xb9, 0x3b, 0xc0, 0x61, 0x4f, 0xbd, 0x3b, 0xbc, 0x4b, 0x75, 0xc6,
+  0x3d, 0xbc, 0x43, 0xd7, 0xd9, 0x45, 0xbf, 0x40, 0xcc, 0x68, 0x54, 0xc9,
+  0x44, 0xca, 0x53, 0x7d, 0xda, 0x4d, 0xce, 0x4f, 0xdb, 0x6d, 0x68, 0xdd,
+  0x55, 0xd6, 0x56, 0xdc, 0x67, 0x71, 0xde, 0x53, 0xce, 0x4f, 0xd6, 0x6e,
+  0x5c, 0xcc, 0x47, 0xc7, 0x4f, 0xef, 0xd0, 0x45, 0xbf, 0x44, 0xcf, 0xe6,
+  0x48, 0xbe, 0x3d, 0xc3, 0x5a, 0x54, 0xc0, 0x3b, 0xbc, 0x47, 0xfa, 0xc9,
+  0x3c, 0xba, 0x3e, 0xd0, 0xdc, 0x41, 0xbb, 0x3b, 0xc4, 0x5f, 0x4d, 0xbf,
+  0x3c, 0xbf, 0x4c, 0x6d, 0xc9, 0x3f, 0xbe, 0x46, 0xda, 0xdc, 0x49, 0xc3,
+  0x45, 0xce, 0x6b, 0x5b, 0xcd, 0x4a, 0xcd, 0x57, 0xee, 0xe4, 0x58, 0xd4,
+  0x54, 0xd9, 0x60, 0xf1, 0xed, 0x5f, 0xd7, 0x53, 0xd3, 0x5a, 0xeb, 0xe1,
+  0x52, 0xca, 0x4b, 0xce, 0x68, 0x5c, 0xc9, 0x44, 0xc4, 0x4e, 0xec, 0xce,
+  0x43, 0xbe, 0x42, 0xcf, 0xe4, 0x47, 0xbd, 0x3c, 0xc3, 0x5b, 0x50, 0xbf,
+  0x3b, 0xbd, 0x48, 0x73, 0xc8, 0x3c, 0xbb, 0x3f, 0xd4, 0xda, 0x41, 0xbc,
+  0x3d, 0xc7, 0x67, 0x4d, 0xc0, 0x3d, 0xc2, 0x4f, 0x68, 0xcb, 0x42, 0xc2,
+  0x4a, 0xdd, 0xdd, 0x4d, 0xc7, 0x4a, 0xd1, 0x6d, 0x65, 0xd3, 0x52, 0xd1,
+  0x5b, 0xe3, 0xf8, 0x68, 0xdd, 0x5a, 0xd8, 0x59, 0xde, 0x6d, 0x68, 0xd9,
+  0x4e, 0xce, 0x4f, 0xe1, 0xeb, 0x4e, 0xc9, 0x45, 0xcd, 0x5d, 0x58, 0xc9,
+  0x3f, 0xc2, 0x4b, 0xf6, 0xce, 0x3f, 0xbd, 0x40, 0xcf, 0xe0, 0x45, 0xbc,
+  0x3d, 0xc3, 0x5e, 0x51, 0xbf, 0x3c, 0xbd, 0x4b, 0x7b, 0xc7, 0x3e, 0xbb,
+  0x42, 0xd4, 0xd6, 0x45, 0xbd, 0x3f, 0xc9, 0x6f, 0x50, 0xc3, 0x40, 0xc5,
+  0x53, 0x6c, 0xce, 0x46, 0xc7, 0x4c, 0xe0, 0xeb, 0x51, 0xce, 0x4d, 0xd7,
+  0x5f, 0x6c, 0xe3, 0x57, 0xd9, 0x57, 0xde, 0x64, 0xfd, 0xe9, 0x5b, 0xd5,
+  0x52, 0xd5, 0x61, 0x78, 0xd6, 0x4d, 0xc9, 0x4e, 0xd8, 0xe5, 0x4e, 0xc4,
+  0x44, 0xc9, 0x5f, 0x5a, 0xc6, 0x3f, 0xc0, 0x4b, 0xf5, 0xcd, 0x3f, 0xbd,
+  0x40, 0xd2, 0xdf, 0x43, 0xbd, 0x3c, 0xc6, 0x5e, 0x4e, 0xbf, 0x3b, 0xbf,
+  0x4c, 0x6b, 0xc9, 0x3e, 0xbd, 0x44, 0xda, 0xd8, 0x45, 0xbf, 0x42, 0xcc,
+  0x75, 0x52, 0xc6, 0x45, 0xc8, 0x58, 0x76, 0xd2, 0x4c, 0xca, 0x51, 0xdd,
+  0xed, 0x5d, 0xd3, 0x55, 0xd7, 0x61, 0xec, 0xef, 0x65, 0xdc, 0x58, 0xd7,
+  0x5a, 0xe4, 0xfd, 0x5c, 0xd2, 0x4c, 0xcf, 0x57, 0x77, 0xd8, 0x49, 0xc7,
+  0x48, 0xd8, 0xeb, 0x4b, 0xc3, 0x40, 0xc8, 0x5d, 0x57, 0xc4, 0x3e, 0xbf,
+  0x4b, 0xf8, 0xca, 0x3f, 0xbc, 0x42, 0xd1, 0xd9, 0x45, 0xbc, 0x3e, 0xc6,
+  0x6a, 0x4f, 0xbf, 0x3d, 0xc0, 0x4f, 0x67, 0xc9, 0x3f, 0xbf, 0x47, 0xdf,
+  0xdb, 0x47, 0xc4, 0x44, 0xd1, 0x6c, 0x53, 0xcc, 0x48, 0xce, 0x58, 0x74,
+  0xdc, 0x50, 0xd1, 0x54, 0xdf, 0x74, 0x6a, 0xde, 0x5b, 0xd9, 0x5d, 0xdd,
+  0x6e, 0xfc, 0xdd, 0x5a, 0xcf, 0x54, 0xd6, 0x7b, 0x60, 0xcd, 0x4b, 0xc9,
+  0x55, 0xf2, 0xd2, 0x48, 0xc3, 0x47, 0xd5, 0xe6, 0x4a, 0xc1, 0x3f, 0xc8,
+  0x5d, 0x52, 0xc4, 0x3d, 0xc0, 0x4b, 0x71, 0xcb, 0x3e, 0xbd, 0x41, 0xd7,
+  0xdc, 0x43, 0xbe, 0x3e, 0xc9, 0x6a, 0x4e, 0xc1, 0x3e, 0xc3, 0x52, 0x6a,
+  0xca, 0x43, 0xc1, 0x4b, 0xdd, 0xda, 0x4c, 0xc6, 0x4a, 0xd1, 0x77, 0x5d,
+  0xce, 0x4e, 0xcf, 0x5c, 0xf3, 0xe5, 0x5a, 0xd8, 0x58, 0xdd, 0x62, 0xfb,
+  0xf4, 0x5e, 0xdc, 0x54, 0xd9, 0x5a, 0xf6, 0xea, 0x52, 0xce, 0x4c, 0xd4,
+  0x67, 0x5c, 0xcc, 0x46, 0xc8, 0x50, 0xf8, 0xd0, 0x45, 0xc0, 0x46, 0xd3,
+  0xe1, 0x49, 0xbf, 0x3f, 0xc6, 0x63, 0x54, 0xc1, 0x3e, 0xbf, 0x4d, 0x76,
+  0xc9, 0x3f, 0xbd, 0x44, 0xd8, 0xda, 0x44, 0xbe, 0x3f, 0xcb, 0x6b, 0x4e,
+  0xc4, 0x3f, 0xc7, 0x53, 0x66, 0xcd, 0x45, 0xc6, 0x4d, 0xe3, 0xdf, 0x4e,
+  0xcb, 0x4d, 0xd5, 0x6f, 0x65, 0xd6, 0x55, 0xd4, 0x5e, 0xe5, 0xf1, 0x6b,
+  0xdc, 0x5d, 0xd8, 0x5e, 0xdf, 0x79, 0x6d, 0xd9, 0x53, 0xcf, 0x56, 0xe3,
+  0xe8, 0x52, 0xcb, 0x49, 0xcf, 0x61, 0x5a, 0xcb, 0x43, 0xc6, 0x4d, 0x7c,
+  0xd1, 0x42, 0xc0, 0x43, 0xd6, 0xe3, 0x47, 0xbf, 0x3e, 0xc8, 0x63, 0x51,
+  0xc1, 0x3e, 0xc0, 0x4e, 0x6f, 0xc9, 0x3f, 0xbe, 0x47, 0xd9, 0xd7, 0x47,
+  0xbf, 0x43, 0xcc, 0x79, 0x51, 0xc6, 0x44, 0xc9, 0x58, 0x6a, 0xd0, 0x49,
+  0xca, 0x4f, 0xe6, 0xea, 0x54, 0xd1, 0x50, 0xdb, 0x65, 0x6e, 0xe4, 0x5a,
+  0xdc, 0x5a, 0xe1, 0x67, 0xfe, 0xea, 0x5d, 0xd7, 0x55, 0xd8, 0x63, 0x74,
+  0xd8, 0x4f, 0xcb, 0x4f, 0xdc, 0xe5, 0x50, 0xc7, 0x47, 0xcc, 0x65, 0x5b,
+  0xc8, 0x43, 0xc4, 0x4e, 0xfa, 0xcd, 0x42, 0xbf, 0x44, 0xd6, 0xdf, 0x46,
+  0xbf, 0x3f, 0xc9, 0x64, 0x4f, 0xc3, 0x3e, 0xc3, 0x4f, 0x68, 0xcb, 0x40,
+  0xc0, 0x48, 0xde, 0xd9, 0x48, 0xc2, 0x45, 0xcf, 0x7b, 0x55, 0xc9, 0x48,
+  0xcb, 0x5c, 0x75, 0xd3, 0x4f, 0xcd, 0x56, 0xe0, 0xed, 0x5e, 0xd7, 0x58,
+  0xdb, 0x63, 0xef, 0xf5, 0x65, 0xdf, 0x5a, 0xdb, 0x5b, 0xea, 0x7d, 0x5d,
+  0xd6, 0x4e, 0xd3, 0x59, 0x73, 0xd9, 0x4b, 0xca, 0x4b, 0xdc, 0xeb, 0x4d,
+  0xc6, 0x44, 0xcb, 0x61, 0x59, 0xc7, 0x41, 0xc2, 0x4e, 0xfb, 0xcc, 0x42,
+  0xbe, 0x46, 0xd5, 0xdb, 0x47, 0xbe, 0x40, 0xc9, 0x6e, 0x50, 0xc2, 0x3f,
+  0xc4, 0x52, 0x69, 0xcb, 0x42, 0xc3, 0x4a, 0xe2, 0xdc, 0x49, 0xc6, 0x48,
+  0xd4, 0x71, 0x56, 0xcd, 0x4a, 0xcf, 0x5b, 0x73, 0xdd, 0x53, 0xd3, 0x57,
+  0xe2, 0x78, 0x6a, 0xdf, 0x5d, 0xdb, 0x5e, 0xe1, 0x6f, 0x7a, 0xe0, 0x5b,
+  0xd4, 0x57, 0xdb, 0x79, 0x5f, 0xd0, 0x4d, 0xcd, 0x58, 0xfd, 0xd6, 0x4a,
+  0xc7, 0x4a, 0xd9, 0xe8, 0x4c, 0xc5, 0x42, 0xcb, 0x5f, 0x55, 0xc7, 0x3f,
+  0xc4, 0x4e, 0x71, 0xcd, 0x41, 0xbf, 0x46, 0xd9, 0xdb, 0x47, 0xbf, 0x41,
+  0xcb, 0x70, 0x51, 0xc4, 0x42, 0xc5, 0x57, 0x6b, 0xcc, 0x46, 0xc4, 0x4d,
+  0xe0, 0xdb, 0x4d, 0xc8, 0x4c, 0xd5, 0x78, 0x5d, 0xd1, 0x4f, 0xd3, 0x5d,
+  0xfb, 0xe6, 0x5a, 0xda, 0x59, 0xe1, 0x67, 0x7c, 0xf1, 0x5f, 0xde, 0x58,
+  0xdc, 0x5e, 0xf9, 0xe8, 0x56, 0xd1, 0x4f, 0xd7, 0x6d, 0x5e, 0xce, 0x4a,
+  0xcb, 0x55, 0xf7, 0xd3, 0x49, 0xc4, 0x4a, 0xd7, 0xe3, 0x4c, 0xc2, 0x43,
+  0xca, 0x66, 0x56, 0xc5, 0x40, 0xc3, 0x4f, 0x74, 0xcc, 0x42, 0xc0, 0x47,
+  0xdb, 0xdc, 0x47, 0xc1, 0x42, 0xce, 0x6e, 0x50, 0xc7, 0x43, 0xc9, 0x57,
+  0x67, 0xcf, 0x48, 0xc9, 0x4e, 0xe6, 0xe0, 0x50, 0xcd, 0x4e, 0xd8, 0x6f,
+  0x65, 0xd7, 0x55, 0xd7, 0x5f, 0xeb, 0xf3, 0x68, 0xde, 0x5e, 0xdc, 0x60,
+  0xe5, 0x7b, 0x6b, 0xdc, 0x56, 0xd4, 0x59, 0xe8, 0xe8, 0x55, 0xcd, 0x4c,
+  0xd3, 0x68, 0x5c, 0xcd, 0x47, 0xc9, 0x52, 0xfe, 0xd2, 0x47, 0xc4, 0x48,
+  0xd8, 0xe2, 0x4a, 0xc2, 0x42, 0xcb, 0x68, 0x54, 0xc5, 0x40, 0xc4, 0x51,
+  0x6e, 0xcc, 0x42, 0xc1, 0x49, 0xdd, 0xda, 0x49, 0xc3, 0x46, 0xcf, 0x7a,
+  0x53, 0xc8, 0x46, 0xcb, 0x5b, 0x6a, 0xd2, 0x4b, 0xcc, 0x52, 0xe7, 0xe7,
+  0x56, 0xd2, 0x53, 0xdc, 0x6a, 0x6d, 0xe2, 0x5b, 0xdc, 0x5e, 0xe5, 0x6d,
+  0x7a, 0xea, 0x5f, 0xda, 0x59, 0xdc, 0x68, 0x70, 0xdb, 0x52, 0xcf, 0x53,
+  0xe0, 0xe9, 0x53, 0xcb, 0x4a, 0xcf, 0x66, 0x5c, 0xcb, 0x46, 0xc7, 0x51,
+  0xfb, 0xcf, 0x46, 0xc2, 0x48, 0xd8, 0xdf, 0x4a, 0xc2, 0x42, 0xcb, 0x69,
+  0x53, 0xc5, 0x41, 0xc5, 0x53, 0x6b, 0xcc, 0x44, 0xc3, 0x4a, 0xe0, 0xdb,
+  0x4a, 0xc5, 0x48, 0xd2, 0x78, 0x55, 0xcb, 0x49, 0xce, 0x5c, 0x6e, 0xd7,
+  0x4f, 0xcf, 0x56, 0xe6, 0xef, 0x5d, 0xd8, 0x59, 0xdc, 0x67, 0xf6, 0xee,
+  0x65, 0xdf, 0x5d, 0xdd, 0x61, 0xec, 0xf4, 0x5f, 0xd8, 0x54, 0xd6, 0x5f,
+  0x78, 0xda, 0x4f, 0xcc, 0x4f, 0xde, 0xea, 0x50, 0xc9, 0x48, 0xce, 0x64,
+  0x5a, 0xca, 0x45, 0xc7, 0x50, 0x7b, 0xcf, 0x45, 0xc3, 0x48, 0xda, 0xde,
+  0x4a, 0xc2, 0x43, 0xcc, 0x6d, 0x53, 0xc6, 0x43, 0xc7, 0x56, 0x6a, 0xcd,
+  0x46, 0xc5, 0x4d, 0xe2, 0xdc, 0x4c, 0xc8, 0x4b, 0xd5, 0x7a, 0x59, 0xce,
+  0x4d, 0xd0, 0x5e, 0x76, 0xdc, 0x55, 0xd4, 0x5a, 0xe5, 0x7d, 0x68, 0xdf,
+  0x5d, 0xde, 0x60, 0xe9, 0x73, 0x70, 0xe4, 0x5c, 0xd9, 0x59, 0xe1, 0x7a,
+  0x60, 0xd5, 0x4f, 0xd1, 0x5b, 0x7e, 0xd9, 0x4d, 0xca, 0x4d, 0xdb, 0xe8,
+  0x4f, 0xc8, 0x47, 0xcd, 0x66, 0x5a, 0xc9, 0x44, 0xc6, 0x51, 0x78, 0xce,
+  0x45, 0xc3, 0x49, 0xdb, 0xdd, 0x49, 0xc3, 0x44, 0xce, 0x6f, 0x53, 0xc7,
+  0x44, 0xc9, 0x57, 0x69, 0xce, 0x48, 0xc8, 0x4e, 0xe6, 0xde, 0x4e, 0xcb,
+  0x4d, 0xd8, 0x78, 0x5d, 0xd2, 0x50, 0xd5, 0x5f, 0xfc, 0xe4, 0x5c, 0xda,
+  0x5c, 0xe2, 0x6e, 0x7d, 0xeb, 0x63, 0xde, 0x5d, 0xde, 0x65, 0xf9, 0xe7,
+  0x5a, 0xd5, 0x54, 0xdb, 0x6f, 0x5f, 0xd2, 0x4d, 0xce, 0x58, 0x7d, 0xd7,
+  0x4b, 0xc9, 0x4c, 0xdb, 0xe7, 0x4e, 0xc6, 0x46, 0xcd, 0x67, 0x58, 0xc8,
+  0x44, 0xc7, 0x53, 0x72, 0xce, 0x45, 0xc3, 0x4a, 0xdd, 0xdc, 0x4a, 0xc4,
+  0x46, 0xcf, 0x76, 0x54, 0xc9, 0x46, 0xcb, 0x5a, 0x69, 0xd0, 0x4a, 0xcb,
+  0x51, 0xe8, 0xe1, 0x52, 0xce, 0x50, 0xdb, 0x72, 0x63, 0xda, 0x56, 0xda,
+  0x5f, 0xf1, 0xf3, 0x65, 0xe0, 0x5e, 0xe0, 0x63, 0xec, 0x7c, 0x6a, 0xde,
+  0x59, 0xd8, 0x5c, 0xec, 0xe9, 0x58, 0xd0, 0x4f, 0xd7, 0x6c, 0x5f, 0xcf,
+  0x4b, 0xcc, 0x56, 0xfc, 0xd5, 0x4a, 0xc7, 0x4b, 0xdb, 0xe4, 0x4d, 0xc6,
+  0x46, 0xcd, 0x69, 0x57, 0xc8, 0x44, 0xc7, 0x54, 0x6e, 0xce, 0x46, 0xc5,
+  0x4b, 0xdf, 0xdc, 0x4b, 0xc6, 0x48, 0xd2, 0x79, 0x55, 0xcb, 0x49, 0xcd,
+  0x5d, 0x6c, 0xd4, 0x4d, 0xcd, 0x55, 0xe8, 0xe6, 0x58, 0xd3, 0x55, 0xdd,
+  0x6e, 0x6d, 0xe0, 0x5d, 0xdd, 0x5f, 0xe9, 0x73, 0x75, 0xe9, 0x60, 0xdd,
+  0x5c, 0xe0, 0x6c, 0x6e, 0xde, 0x55, 0xd4, 0x57, 0xe6, 0xeb, 0x56, 0xce,
+  0x4d, 0xd4, 0x6a, 0x5e, 0xce, 0x49, 0xcb, 0x55, 0xfe, 0xd3, 0x49, 0xc6,
+  0x4b, 0xdb, 0xe2, 0x4c, 0xc5, 0x46, 0xce, 0x6c, 0x56, 0xc8, 0x45, 0xc8,
+  0x56, 0x6c, 0xce, 0x47, 0xc6, 0x4d, 0xe3, 0xdd, 0x4c, 0xc8, 0x4a, 0xd5,
+  0x7a, 0x57, 0xcd, 0x4b, 0xcf, 0x5e, 0x6d, 0xd8, 0x50, 0xd1, 0x58, 0xe9,
+  0xee, 0x5d, 0xd9, 0x5a, 0xde, 0x6a, 0xfe, 0xec, 0x65, 0xe0, 0x5f, 0xe0,
+  0x67, 0xf0, 0xf1, 0x63, 0xda, 0x58, 0xda, 0x65, 0x78, 0xdc, 0x53, 0xcf,
+  0x54, 0xe1, 0xeb, 0x54, 0xcc, 0x4b, 0xd1, 0x68, 0x5d, 0xcd, 0x48, 0xca,
+  0x54, 0x7b, 0xd2, 0x48, 0xc6, 0x4b, 0xdc, 0xe0, 0x4c, 0xc6, 0x47, 0xcf,
+  0x6e, 0x55, 0xc9, 0x45, 0xca, 0x58, 0x6b, 0xcf, 0x48, 0xc8, 0x4e, 0xe5,
+  0xdd, 0x4e, 0xca, 0x4c, 0xd8, 0x7b, 0x5a, 0xcf, 0x4e, 0xd3, 0x60, 0x73,
+  0xdd, 0x56, 0xd6, 0x5b, 0xe8, 0xfc, 0x67, 0xdf, 0x5e, 0xdf, 0x65, 0xed,
+  0x7b, 0x6e, 0xe5, 0x5e, 0xdc, 0x5d, 0xe6, 0xff, 0x63, 0xd8, 0x53, 0xd5,
+  0x5e, 0x7c, 0xdb, 0x4f, 0xcd, 0x50, 0xde, 0xea, 0x52, 0xcb, 0x4a, 0xcf,
+  0x68, 0x5c, 0xcc, 0x47, 0xc9, 0x54, 0x79, 0xd1, 0x48, 0xc6, 0x4b, 0xdd,
+  0xdf, 0x4c, 0xc6, 0x47, 0xd0, 0x6f, 0x56, 0xca, 0x47, 0xcb, 0x5a, 0x6b,
+  0xd1, 0x4a, 0xca, 0x50, 0xe7, 0xdf, 0x50, 0xcd, 0x4f, 0xda, 0x79, 0x5e,
+  0xd5, 0x52, 0xd7, 0x62, 0xff, 0xe4, 0x5c, 0xdb, 0x5d, 0xe5, 0x73, 0x77,
+  0xea, 0x64, 0xe0, 0x5f, 0xe2, 0x6b, 0xfe, 0xe8, 0x5c, 0xd8, 0x58, 0xde,
+  0x76, 0x63, 0xd5, 0x50, 0xd1, 0x5b, 0xff, 0xda, 0x4e, 0xcb, 0x4f, 0xdd,
+  0xe9, 0x50, 0xca, 0x49, 0xcf, 0x69, 0x5b, 0xcb, 0x47, 0xc9, 0x55, 0x75,
+  0xd1, 0x48, 0xc7, 0x4c, 0xde, 0xde, 0x4c, 0xc7, 0x49, 0xd2, 0x76, 0x57,
+  0xcb, 0x49, 0xcd, 0x5c, 0x6b, 0xd3, 0x4c, 0xcd, 0x54, 0xe9, 0xe3, 0x54,
+  0xcf, 0x52, 0xdc, 0x75, 0x64, 0xda, 0x58, 0xdb, 0x63, 0xf4, 0xee, 0x65,
+  0xe0, 0x5f, 0xe3, 0x68, 0xf1, 0xf9, 0x6a, 0xe0, 0x5d, 0xdc, 0x60, 0xef,
+  0xeb, 0x5b, 0xd5, 0x54, 0xda, 0x6d, 0x62, 0xd3, 0x4e, 0xcf, 0x59, 0xfd,
+  0xd9, 0x4d, 0xca, 0x4e, 0xdd, 0xe8, 0x4f, 0xc9, 0x49, 0xcf, 0x69, 0x5a,
+  0xcb, 0x47, 0xca, 0x56, 0x73, 0xd1, 0x49, 0xc7, 0x4d, 0xdf, 0xdf, 0x4d,
+  0xc8, 0x4a, 0xd4, 0x77, 0x58, 0xcd, 0x4b, 0xcf, 0x5d, 0x6d, 0xd6, 0x4e,
+  0xcf, 0x56, 0xea, 0xe9, 0x59, 0xd5, 0x56, 0xde, 0x6f, 0x6d, 0xdf, 0x5d,
+  0xdd, 0x62, 0xeb, 0x7c, 0x71, 0xe8, 0x62, 0xdf, 0x60, 0xe5, 0x73, 0x6f,
+  0xdf, 0x5a, 0xd7, 0x5c, 0xe9, 0xec, 0x5a, 0xd1, 0x50, 0xd7, 0x6c, 0x61,
+  0xd1, 0x4c, 0xcd, 0x58, 0xfd, 0xd7, 0x4c, 0xc9, 0x4d, 0xdd, 0xe7, 0x4f,
+  0xc9, 0x49, 0xcf, 0x6b, 0x59, 0xcb, 0x47, 0xcb, 0x57, 0x6f, 0xd1, 0x49,
+  0xc9, 0x4e, 0xe2, 0xdf, 0x4e, 0xca, 0x4c, 0xd6, 0x78, 0x5a, 0xcf, 0x4d,
+  0xd1, 0x5f, 0x70, 0xda, 0x52, 0xd3, 0x59, 0xe9, 0xef, 0x5e, 0xda, 0x5a,
+  0xdf, 0x6b, 0x7b, 0xeb, 0x63, 0xe1, 0x61, 0xe5, 0x6b, 0xfa, 0xf0, 0x64,
+  0xdd, 0x5b, 0xde, 0x68, 0x75, 0xdf, 0x56, 0xd4, 0x58, 0xe4, 0xed, 0x58,
+  0xcf, 0x4e, 0xd5, 0x6a, 0x60, 0xcf, 0x4b, 0xcc, 0x57, 0xfd, 0xd6, 0x4b,
+  0xc9, 0x4d, 0xdd, 0xe5, 0x4f, 0xc9, 0x49, 0xd0, 0x6d, 0x5a, 0xcc, 0x48,
+  0xcc, 0x59, 0x6f, 0xd3, 0x4b, 0xca, 0x4f, 0xe5, 0xe1, 0x50, 0xcc, 0x4e,
+  0xd9, 0x77, 0x5d, 0xd2, 0x4f, 0xd5, 0x60, 0x77, 0xde, 0x57, 0xd7, 0x5c,
+  0xe8, 0xfa, 0x67, 0xdf, 0x5e, 0xe0, 0x67, 0xf0, 0xfc, 0x6d, 0xe5, 0x5f,
+  0xdf, 0x61, 0xeb, 0xfb, 0x65, 0xdb, 0x57, 0xd9, 0x61, 0x7c, 0xde, 0x54,
+  0xd0, 0x54, 0xe1, 0xed, 0x56, 0xcd, 0x4d, 0xd3, 0x69, 0x5f, 0xce, 0x4b,
+  0xcc, 0x57, 0x7d, 0xd5, 0x4b, 0xc9, 0x4e, 0xde, 0xe4, 0x4f, 0xc9, 0x4a,
+  0xd2, 0x6f, 0x5a, 0xcc, 0x4a, 0xcd, 0x5b, 0x6f, 0xd4, 0x4c, 0xcc, 0x52,
+  0xe7, 0xe4, 0x53, 0xce, 0x50, 0xdb, 0x76, 0x60, 0xd6, 0x54, 0xd8, 0x62,
+  0xff, 0xe5, 0x5d, 0xdc, 0x5e, 0xe7, 0x75, 0x73, 0xe9, 0x64, 0xe2, 0x63,
+  0xe7, 0x6f, 0x7b, 0xe9, 0x5f, 0xdb, 0x5c, 0xe2, 0x78, 0x65, 0xd9, 0x54,
+  0xd6, 0x5e, 0xfe, 0xdd, 0x51, 0xce, 0x52, 0xdf, 0xec, 0x54, 0xcd, 0x4c,
+  0xd2, 0x69, 0x5d, 0xce, 0x4a, 0xcc, 0x57, 0x7a, 0xd5, 0x4b, 0xc9, 0x4e,
+  0xdf, 0xe3, 0x4f, 0xca, 0x4b, 0xd4, 0x70, 0x5a, 0xcd, 0x4b, 0xce, 0x5c,
+  0x6f, 0xd6, 0x4e, 0xce, 0x55, 0xe8, 0xe7, 0x57, 0xd2, 0x54, 0xdd, 0x73,
+  0x66, 0xdb, 0x59, 0xdb, 0x63, 0xf5, 0xee, 0x65, 0xe0, 0x60, 0xe5, 0x6b,
+  0xf7, 0xf6, 0x69, 0xe2, 0x5f, 0xdf, 0x65, 0xf5, 0xeb, 0x5d, 0xd8, 0x58,
+  0xdd, 0x71, 0x65, 0xd7, 0x51, 0xd2, 0x5c, 0xfd, 0xdb, 0x4f, 0xcd, 0x50,
+  0xde, 0xea, 0x53, 0xcc, 0x4c, 0xd2, 0x6a, 0x5d, 0xce, 0x4a, 0xcc, 0x58,
+  0x78, 0xd4, 0x4b, 0xca, 0x4f, 0xe1, 0xe3, 0x4f, 0xcb, 0x4c, 0xd6, 0x74,
+  0x5b, 0xcf, 0x4d, 0xd0, 0x5e, 0x70, 0xd9, 0x50, 0xd0, 0x58, 0xea, 0xeb,
+  0x5b, 0xd6, 0x58, 0xdf, 0x6f, 0x6d, 0xe1, 0x5d, 0xde, 0x64, 0xed, 0xff,
+  0x6e, 0xe8, 0x63, 0xe2, 0x64, 0xe9, 0x77, 0x6e, 0xe2, 0x5c, 0xdb, 0x5e,
+  0xec, 0xed, 0x5c, 0xd5, 0x54, 0xda, 0x6d, 0x64, 0xd5, 0x4f, 0xd0, 0x5a,
+  0xfd, 0xda, 0x4e, 0xcc, 0x4f, 0xde, 0xe9, 0x52, 0xcb, 0x4b, 0xd3, 0x6c,
+  0x5c, 0xce, 0x4a, 0xcd, 0x5a, 0x74, 0xd5, 0x4c, 0xcb, 0x50, 0xe4, 0xe3,
+  0x51, 0xcc, 0x4e, 0xd8, 0x77, 0x5c, 0xd1, 0x4f, 0xd4, 0x60, 0x72, 0xdc,
+  0x55, 0xd5, 0x5b, 0xeb, 0xef, 0x5f, 0xdb, 0x5c, 0xe1, 0x6d, 0x7a, 0xeb,
+  0x65, 0xe3, 0x64, 0xe8, 0x6e, 0xfc, 0xef, 0x66, 0xdf, 0x5e, 0xe0, 0x6b,
+  0x76, 0xe0, 0x59, 0xd7, 0x5a, 0xe8, 0xee, 0x5a, 0xd2, 0x51, 0xd8, 0x6c,
+  0x62, 0xd3, 0x4e, 0xcf, 0x5a, 0xff, 0xd9, 0x4e, 0xcc, 0x4f, 0xdf, 0xe7,
+  0x51, 0xcb, 0x4c, 0xd4, 0x6e, 0x5c, 0xce, 0x4b, 0xce, 0x5b, 0x71, 0xd5,
+  0x4d, 0xcd, 0x53, 0xe7, 0xe3, 0x53, 0xce, 0x50, 0xdb, 0x78, 0x5e, 0xd5,
+  0x52, 0xd7, 0x63, 0x78, 0xdf, 0x5a, 0xd9, 0x5e, 0xea, 0xf9, 0x69, 0xe1,
+  0x60, 0xe3, 0x6a, 0xf2, 0xfa, 0x6e, 0xe7, 0x63, 0xe1, 0x65, 0xed, 0xfa,
+  0x67, 0xdd, 0x5a, 0xdc, 0x65, 0x7b, 0xdf, 0x57, 0xd4, 0x57, 0xe4, 0xee,
+  0x59, 0xd0, 0x4f, 0xd6, 0x6b, 0x60, 0xd2, 0x4d, 0xce, 0x59, 0x7d, 0xd8,
+  0x4d, 0xcc, 0x4f, 0xe0, 0xe7, 0x51, 0xcc, 0x4c, 0xd5, 0x6f, 0x5b, 0xce,
+  0x4c, 0xcf, 0x5c, 0x70, 0xd7, 0x4e, 0xce, 0x55, 0xe9, 0xe5, 0x56, 0xd1,
+  0x53, 0xdd, 0x78, 0x62, 0xd9, 0x57, 0xda, 0x66, 0x7e, 0xe6, 0x5e, 0xde,
+  0x60, 0xe9, 0x78, 0x73, 0xea, 0x66, 0xe4, 0x66, 0xea, 0x71, 0x7a, 0xea,
+  0x61, 0xde, 0x5e, 0xe5, 0x7a, 0x67, 0xdb, 0x57, 0xd8, 0x60, 0x7e, 0xde,
+  0x54, 0xd1, 0x55, 0xe2, 0xed, 0x57, 0xcf, 0x4e, 0xd6, 0x6b, 0x5f, 0xd0,
+  0x4c, 0xce, 0x5a, 0x7a, 0xd8, 0x4d, 0xcc, 0x50, 0xe3, 0xe5, 0x51, 0xcc,
+  0x4d, 0xd7, 0x74, 0x5c, 0xcf, 0x4d, 0xd1, 0x5e, 0x6f, 0xd9, 0x50, 0xd0,
+  0x58, 0xea, 0xe7, 0x59, 0xd4, 0x57, 0xde, 0x77, 0x68, 0xdd, 0x5b, 0xdd,
+  0x66, 0xf7, 0xee, 0x67, 0xe3, 0x64, 0xe7, 0x6d, 0xf9, 0xf5, 0x6b, 0xe5,
+  0x62, 0xe2, 0x68, 0xf6, 0xed, 0x5f, 0xdb, 0x5a, 0xdf, 0x72, 0x67, 0xd9,
+  0x54, 0xd5, 0x5e, 0xfd, 0xdd, 0x52, 0xcf, 0x53, 0xe1, 0xed, 0x56, 0xce,
+  0x4e, 0xd5, 0x6b, 0x5e, 0xd0, 0x4c, 0xce, 0x5a, 0x77, 0xd7, 0x4d, 0xcc,
+  0x52, 0xe4, 0xe5, 0x53, 0xcd, 0x4e, 0xd8, 0x76, 0x5d, 0xd1, 0x4f, 0xd3,
+  0x5f, 0x71, 0xdb, 0x53, 0xd4, 0x5a, 0xec, 0xec, 0x5d, 0xd9, 0x5a, 0xe1,
+  0x72, 0x6e, 0xe3, 0x5f, 0xe0, 0x66, 0xef, 0xfe, 0x6f, 0xe9, 0x66, 0xe5,
+  0x67, 0xec, 0x79, 0x70, 0xe5, 0x5e, 0xdd, 0x60, 0xee, 0xee, 0x5e, 0xd8,
+  0x57, 0xdc, 0x6f, 0x67, 0xd8, 0x52, 0xd3, 0x5d, 0xfd, 0xdc, 0x51, 0xce,
+  0x53, 0xe0, 0xeb, 0x55, 0xce, 0x4e, 0xd6, 0x6d, 0x5e, 0xd0, 0x4c, 0xcf,
+  0x5b, 0x75, 0xd8, 0x4e, 0xcd, 0x53, 0xe6, 0xe5, 0x54, 0xce, 0x50, 0xda,
+  0x78, 0x5e, 0xd4, 0x51, 0xd6, 0x63, 0x74, 0xdd, 0x57, 0xd7, 0x5d, 0xec,
+  0xf0, 0x61, 0xdd, 0x5e, 0xe4, 0x6f, 0x7a, 0xec, 0x67, 0xe5, 0x67, 0xea,
+  0x70, 0xfe, 0xf0, 0x68, 0xe2, 0x61, 0xe3, 0x6d, 0x77, 0xe4, 0x5c, 0xda,
+  0x5d, 0xea, 0xef, 0x5d, 0xd6, 0x54, 0xdb, 0x6c, 0x65, 0xd6, 0x50, 0xd2,
+  0x5c, 0xfe, 0xdc, 0x50, 0xce, 0x52, 0xe1, 0xea, 0x55, 0xcd, 0x4e, 0xd6,
+  0x6e, 0x5e, 0xd0, 0x4d, 0xcf, 0x5d, 0x74, 0xd8, 0x4f, 0xce, 0x55, 0xe8,
+  0xe6, 0x56, 0xd0, 0x53, 0xdc, 0x78, 0x60, 0xd7, 0x55, 0xd9, 0x65, 0x78,
+  0xe2, 0x5b, 0xdb, 0x5f, 0xec, 0xfa, 0x69, 0xe3, 0x62, 0xe6, 0x6b, 0xf6,
+  0xfa, 0x6e, 0xe9, 0x66, 0xe5, 0x68, 0xee, 0xfb, 0x69, 0xdf, 0x5d, 0xde,
+  0x68, 0x7c, 0xe3, 0x5a, 0xd7, 0x5a, 0xe6, 0xef, 0x5c, 0xd3, 0x52, 0xd9,
+  0x6c, 0x64, 0xd5, 0x4f, 0xd1, 0x5b, 0xff, 0xdb, 0x4f, 0xce, 0x53, 0xe2,
+  0xe9, 0x55, 0xce, 0x4e, 0xd7, 0x70, 0x5e, 0xd1, 0x4e, 0xd1, 0x5e, 0x73,
+  0xd9, 0x50, 0xd0, 0x58, 0xea, 0xe7, 0x58, 0xd3, 0x56, 0xde, 0x78, 0x64,
+  0xdb, 0x59, 0xdc, 0x67, 0x7d, 0xe8, 0x5f, 0xdf, 0x62, 0xeb, 0x79, 0x72,
+  0xeb, 0x67, 0xe7, 0x68, 0xec, 0x74, 0x79, 0xec, 0x64, 0xe0, 0x60, 0xe8,
+  0x7a, 0x6a, 0xde, 0x5a, 0xdb, 0x63, 0xfe, 0xe1, 0x58, 0xd4, 0x58, 0xe4,
+  0xef, 0x5a, 0xd2, 0x51, 0xd8, 0x6c, 0x63, 0xd4, 0x4f, 0xd0, 0x5c, 0x7d,
+  0xda, 0x4f, 0xce, 0x53, 0xe3, 0xe8, 0x55, 0xce, 0x4f, 0xd9, 0x73, 0x5e,
+  0xd2, 0x4f, 0xd4, 0x5f, 0x72, 0xdb, 0x53, 0xd3, 0x5a, 0xeb, 0xea, 0x5b,
+  0xd7, 0x59, 0xe0, 0x75, 0x6a, 0xde, 0x5d, 0xde, 0x68, 0xf9, 0xef, 0x67,
+  0xe5, 0x65, 0xe9, 0x6f, 0xfb, 0xf5, 0x6c, 0xe7, 0x64, 0xe5, 0x6a, 0xf8,
+  0xee, 0x62, 0xdd, 0x5d, 0xe2, 0x73, 0x6a, 0xdc, 0x57, 0xd8, 0x5f, 0xfb,
+  0xe0, 0x56, 0xd2, 0x57, 0xe2, 0xee, 0x59, 0xd1, 0x50, 0xd8, 0x6d, 0x62,
+  0xd3, 0x4e, 0xd0, 0x5c, 0x7a, 0xda, 0x4f, 0xce, 0x54, 0xe5, 0xe8, 0x56,
+  0xcf, 0x50, 0xda, 0x75, 0x5f, 0xd4, 0x51, 0xd6, 0x62, 0x74, 0xdd, 0x56,
+  0xd6, 0x5c, 0xec, 0xed, 0x5e, 0xdb, 0x5c, 0xe3, 0x73, 0x6e, 0xe5, 0x60,
+  0xe3, 0x68, 0xf1, 0xfd, 0x6f, 0xeb, 0x67, 0xe7, 0x69, 0xee, 0x7a, 0x71,
+  0xe7, 0x61, 0xdf, 0x64, 0xef, 0xef, 0x60, 0xda, 0x5a, 0xde, 0x70, 0x69,
+  0xda, 0x55, 0xd6, 0x5e, 0xfb, 0xde, 0x55, 0xd1, 0x56, 0xe2, 0xed, 0x59,
+  0xd0, 0x50, 0xd8, 0x6d, 0x60, 0xd3, 0x4f, 0xd1, 0x5d, 0x79, 0xda, 0x50,
+  0xcf, 0x56, 0xe7, 0xe8, 0x57, 0xd1, 0x53, 0xdc, 0x77, 0x60, 0xd7, 0x54,
+  0xd8, 0x64, 0x76, 0xdf, 0x59, 0xd9, 0x5e, 0xed, 0xf2, 0x64, 0xde, 0x5f,
+  0xe5, 0x6f, 0x79, 0xec, 0x68, 0xe7, 0x68, 0xec, 0x73, 0x7e, 0xf1, 0x6a,
+  0xe5, 0x63, 0xe7, 0x6f, 0x77, 0xe7, 0x5e, 0xdc, 0x5e, 0xeb, 0xf2, 0x5f,
+  0xd9, 0x57, 0xdc, 0x6d, 0x68, 0xd9, 0x53, 0xd5, 0x5d, 0xfc, 0xde, 0x53,
+  0xd0, 0x55, 0xe3, 0xed, 0x58, 0xd0, 0x50, 0xd8, 0x6e, 0x60, 0xd3, 0x4f,
+  0xd2, 0x5e, 0x77, 0xdb, 0x52, 0xd1, 0x57, 0xe8, 0xe9, 0x59, 0xd3, 0x55,
+  0xdd, 0x78, 0x64, 0xd9, 0x57, 0xdb, 0x66, 0x7b, 0xe4, 0x5d, 0xdc, 0x61,
+  0xec, 0xfa, 0x6b, 0xe4, 0x64, 0xe7, 0x6d, 0xf7, 0xf8, 0x6f, 0xea, 0x68,
+  0xe8, 0x6a, 0xf2, 0xfa, 0x6b, 0xe3, 0x5f, 0xe1, 0x69, 0x7c, 0xe6, 0x5c,
+  0xda, 0x5c, 0xe9, 0xf3, 0x5e, 0xd7, 0x55, 0xdb, 0x6c, 0x67, 0xd8, 0x52,
+  0xd4, 0x5d, 0xfd, 0xdd, 0x53, 0xd0, 0x55, 0xe3, 0xec, 0x58, 0xd0, 0x50,
+  0xd9, 0x6f, 0x61, 0xd4, 0x50, 0xd4, 0x5f, 0x77, 0xdc, 0x54, 0xd3, 0x59,
+  0xea, 0xea, 0x5b, 0xd6, 0x58, 0xdf, 0x77, 0x67, 0xdc, 0x5a, 0xdd, 0x68,
+  0xfe, 0xea, 0x62, 0xdf, 0x64, 0xeb, 0x7b, 0x73, 0xeb, 0x68, 0xe8, 0x6a,
+  0xee, 0x77, 0x79, 0xed, 0x67, 0xe3, 0x64, 0xeb, 0x7c, 0x6b, 0xe0, 0x5c,
+  0xdd, 0x65, 0xfe, 0xe5, 0x5a, 0xd8, 0x5a, 0xe6, 0xf3, 0x5d, 0xd5, 0x54,
+  0xda, 0x6d, 0x67, 0xd7, 0x51, 0xd3, 0x5d, 0xfe, 0xdd, 0x53, 0xd0, 0x56,
+  0xe5, 0xeb, 0x58, 0xd1, 0x52, 0xda, 0x71, 0x61, 0xd6, 0x52, 0xd6, 0x61,
+  0x77, 0xdd, 0x56, 0xd5, 0x5b, 0xeb, 0xec, 0x5d, 0xd9, 0x5a, 0xe2, 0x75,
+  0x6b, 0xe0, 0x5e, 0xe0, 0x68, 0xf9, 0xf0, 0x68, 0xe5, 0x66, 0xeb, 0x70,
+  0xfe, 0xf4, 0x6c, 0xe9, 0x67, 0xe8, 0x6d, 0xfa, 0xef, 0x65, 0xdf, 0x5f,
+  0xe6, 0x75, 0x6b, 0xde, 0x5a, 0xdb, 0x62, 0xfc, 0xe3, 0x59, 0xd6, 0x59,
+  0xe5, 0xf2, 0x5c, 0xd4, 0x53, 0xda, 0x6d, 0x65, 0xd7, 0x51, 0xd4, 0x5e,
+  0x7e, 0xdd, 0x53, 0xd1, 0x57, 0xe6, 0xeb, 0x59, 0xd2, 0x53, 0xdc, 0x75,
+  0x62, 0xd7, 0x54, 0xd8, 0x64, 0x78, 0xdf, 0x59, 0xd8, 0x5d, 0xec, 0xee,
+  0x60, 0xdc, 0x5d, 0xe4, 0x74, 0x70, 0xe6, 0x63, 0xe3, 0x6a, 0xf2, 0xfc,
+  0x70, 0xeb, 0x69, 0xe9, 0x6b, 0xf0, 0x7d, 0x72, 0xe9, 0x64, 0xe3, 0x67,
+  0xf2, 0xf2, 0x63, 0xdd, 0x5c, 0xe1, 0x70, 0x6b, 0xdd, 0x58, 0xd9, 0x5f,
+  0xfb, 0xe2, 0x58, 0xd4, 0x58, 0xe5, 0xf1, 0x5b, 0xd4, 0x53, 0xda, 0x6d,
+  0x64, 0xd6, 0x51, 0xd4, 0x5e, 0x7b, 0xdd, 0x54, 0xd2, 0x58, 0xe8, 0xeb,
+  0x5a, 0xd4, 0x55, 0xdd, 0x76, 0x64, 0xd9, 0x57, 0xda, 0x65, 0x79, 0xe2,
+  0x5b, 0xdb, 0x5f, 0xed, 0xf3, 0x66, 0xdf, 0x61, 0xe7, 0x71, 0x7a, 0xed,
+  0x69, 0xe8, 0x6a, 0xed, 0x76, 0x7e, 0xf1, 0x6b, 0xe7, 0x66, 0xea, 0x71,
+  0x77, 0xe9, 0x60, 0xde, 0x61, 0xed, 0xf5, 0x61, 0xdb, 0x5a, 0xde, 0x6e,
+  0x6b, 0xdc, 0x57, 0xd8, 0x5f, 0xfb, 0xe1, 0x57, 0xd4, 0x57, 0xe5, 0xef,
+  0x5b, 0xd3, 0x53, 0xda, 0x6e, 0x64, 0xd7, 0x52, 0xd5, 0x5f, 0x7b, 0xdd,
+  0x55, 0xd3, 0x59, 0xe9, 0xeb, 0x5b, 0xd6, 0x58, 0xde, 0x77, 0x67, 0xdb,
+  0x59, 0xdc, 0x68, 0x7d, 0xe6, 0x5f, 0xde, 0x63, 0xed, 0xfb, 0x6b, 0xe6,
+  0x65, 0xe8, 0x6e, 0xfa, 0xf8, 0x6e, 0xeb, 0x69, 0xea, 0x6c, 0xf5, 0xfa,
+  0x6c, 0xe5, 0x61, 0xe4, 0x6c, 0x7c, 0xe8, 0x5e, 0xdc, 0x5e, 0xea, 0xf4,
+  0x60, 0xd9, 0x58, 0xdd, 0x6e, 0x6a, 0xdb, 0x55, 0xd7, 0x5e, 0xfc, 0xdf,
+  0x56, 0xd3, 0x58, 0xe5, 0xee, 0x5b, 0xd3, 0x54, 0xdb, 0x6f, 0x64, 0xd7,
+  0x53, 0xd7, 0x60, 0x79, 0xde, 0x56, 0xd5, 0x5b, 0xeb, 0xed, 0x5d, 0xd8,
+  0x5a, 0xe1, 0x76, 0x69, 0xde, 0x5c, 0xde, 0x69, 0xfd, 0xeb, 0x64, 0xe2,
+  0x66, 0xed, 0x7c, 0x74, 0xec, 0x6a, 0xe9, 0x6c, 0xef, 0x7a, 0x78, 0xee,
+  0x68, 0xe6, 0x67, 0xed, 0x7c, 0x6d, 0xe3, 0x5e, 0xdf, 0x68, 0xfe, 0xe7,
+  0x5d, 0xda, 0x5d, 0xe8, 0xf4, 0x5f, 0xd8, 0x57, 0xdc, 0x6e, 0x69, 0xda,
+  0x55, 0xd6, 0x5e, 0xfd, 0xdf, 0x56, 0xd3, 0x58, 0xe6, 0xed, 0x5a, 0xd4,
+  0x54, 0xdc, 0x72, 0x64, 0xd8, 0x55, 0xd8, 0x63, 0x78, 0xdf, 0x58, 0xd8,
+  0x5d, 0xed, 0xee, 0x5f, 0xdb, 0x5c, 0xe4, 0x76, 0x6d, 0xe2, 0x5f, 0xe2,
+  0x6a, 0xfa, 0xf1, 0x6a, 0xe7, 0x68, 0xec, 0x72, 0x7e, 0xf5, 0x6e, 0xea,
+  0x69, 0xea, 0x6e, 0xfc, 0xf0, 0x68, 0xe2, 0x62, 0xe7, 0x77, 0x6d, 0xe0,
+  0x5d, 0xdd, 0x65, 0xfb, 0xe6, 0x5b, 0xd9, 0x5b, 0xe7, 0xf4, 0x5e, 0xd7,
+  0x56, 0xdc, 0x6e, 0x68, 0xd9, 0x54, 0xd6, 0x5f, 0xfe, 0xdf, 0x56, 0xd4,
+  0x59, 0xe7, 0xed, 0x5b, 0xd5, 0x56, 0xdd, 0x73, 0x65, 0xda, 0x57, 0xda,
+  0x65, 0x79, 0xe2, 0x5b, 0xda, 0x5f, 0xed, 0xf1, 0x63, 0xde, 0x5f, 0xe6,
+  0x75, 0x72, 0xe8, 0x64, 0xe6, 0x6b, 0xf4, 0xfb, 0x70, 0xec, 0x6a, 0xeb,
+  0x6d, 0xf3, 0x7e, 0x72, 0xeb, 0x66, 0xe5, 0x69, 0xf5, 0xf3, 0x66, 0xdf,
+  0x5f, 0xe4, 0x73, 0x6d, 0xdf, 0x5b, 0xdc, 0x63, 0xfb, 0xe5, 0x5a, 0xd7,
+  0x5a, 0xe6, 0xf3, 0x5d, 0xd7, 0x56, 0xdc, 0x6e, 0x67, 0xd9, 0x55, 0xd7,
+  0x5f, 0x7e, 0xdf, 0x57, 0xd5, 0x5a, 0xe9, 0xed, 0x5c, 0xd6, 0x58, 0xde,
+  0x76, 0x67, 0xdb, 0x59, 0xdc, 0x66, 0x7b, 0xe5, 0x5d, 0xdc, 0x61, 0xee,
+  0xf6, 0x67, 0xe2, 0x62, 0xe8, 0x71, 0x7a, 0xee, 0x69, 0xe9, 0x6b, 0xef,
+  0x78, 0x7c, 0xf0, 0x6c, 0xe9, 0x69, 0xec, 0x76, 0x77, 0xea, 0x63, 0xe1,
+  0x65, 0xee, 0xf4, 0x65, 0xdd, 0x5d, 0xe1, 0x70, 0x6d, 0xde, 0x59, 0xda,
+  0x61, 0xfb, 0xe4, 0x59, 0xd7, 0x5a, 0xe6, 0xf2, 0x5d, 0xd6, 0x56, 0xdc,
+  0x6e, 0x67, 0xd9, 0x55, 0xd8, 0x61, 0x7c, 0xdf, 0x58, 0xd6, 0x5b, 0xea,
+  0xee, 0x5d, 0xd8, 0x59, 0xe0, 0x76, 0x69, 0xdd, 0x5b, 0xde, 0x68, 0x7d,
+  0xe9, 0x5f, 0xdf, 0x63, 0xee, 0xfc, 0x6c, 0xe7, 0x66, 0xe9, 0x6f, 0xfb,
+  0xf7, 0x6e, 0xec, 0x6a, 0xec, 0x6e, 0xf8, 0xf9, 0x6d, 0xe7, 0x65, 0xe7,
+  0x6e, 0x7c, 0xea, 0x61, 0xde, 0x61, 0xec, 0xf6, 0x64, 0xdc, 0x5b, 0xdf,
+  0x6f, 0x6c, 0xdd, 0x58, 0xd9, 0x61, 0xfb, 0xe3, 0x59, 0xd6, 0x5a, 0xe7,
+  0xf1, 0x5d, 0xd6, 0x56, 0xdd, 0x6f, 0x67, 0xda, 0x56, 0xd9, 0x62, 0x7c,
+  0xe0, 0x59, 0xd8, 0x5d, 0xeb, 0xee, 0x5f, 0xda, 0x5b, 0xe2, 0x76, 0x6b,
+  0xe0, 0x5d, 0xe0, 0x6a, 0xfe, 0xed, 0x65, 0xe4, 0x67, 0xee, 0x7b, 0x73,
+  0xed, 0x6a, 0xeb, 0x6d, 0xf2, 0x7b, 0x77, 0xee, 0x6a, 0xe9, 0x6a, 0xef,
+  0x7e, 0x6e, 0xe6, 0x61, 0xe2, 0x6a, 0xfe, 0xe9, 0x5f, 0xdc, 0x5f, 0xea,
+  0xf7, 0x62, 0xdb, 0x5a, 0xde, 0x6e, 0x6c, 0xdc, 0x58, 0xd9, 0x60, 0xfc,
+  0xe2, 0x59, 0xd6, 0x5a, 0xe7, 0xef, 0x5d, 0xd7, 0x57, 0xde, 0x72, 0x67,
+  0xdb, 0x57, 0xda, 0x64, 0x7b, 0xe2, 0x5b, 0xda, 0x5e, 0xed, 0xf0, 0x61,
+  0xdd, 0x5e, 0xe5, 0x77, 0x6e, 0xe4, 0x61, 0xe3, 0x6c, 0xf9, 0xf1, 0x6b,
+  0xe8, 0x6a, 0xed, 0x75, 0x7e, 0xf5, 0x6e, 0xec, 0x6b, 0xec, 0x71, 0xfe,
+  0xf1, 0x69, 0xe5, 0x65, 0xea, 0x78, 0x6e, 0xe4, 0x5e, 0xdf, 0x67, 0xfc,
+  0xe9, 0x5e, 0xdb, 0x5d, 0xe9, 0xf6, 0x61, 0xda, 0x59, 0xde, 0x6e, 0x6b,
+  0xdc, 0x57, 0xd9, 0x61, 0xfd, 0xe2, 0x59, 0xd7, 0x5b, 0xe9, 0xef, 0x5d,
+  0xd8, 0x58, 0xdf, 0x73, 0x68, 0xdc, 0x59, 0xdc, 0x66, 0x7b, 0xe4, 0x5c,
+  0xdc, 0x60, 0xee, 0xf4, 0x65, 0xdf, 0x60, 0xe7, 0x75, 0x73, 0xe9, 0x66,
+  0xe7, 0x6c, 0xf5, 0xfa, 0x71, 0xed, 0x6b, 0xec, 0x6e, 0xf6, 0xfe, 0x73,
+  0xec, 0x68, 0xe9, 0x6b, 0xf7, 0xf5, 0x68, 0xe2, 0x61, 0xe7, 0x73, 0x6e,
+  0xe2, 0x5d, 0xde, 0x65, 0xfa, 0xe8, 0x5d, 0xda, 0x5d, 0xe8, 0xf6, 0x5f,
+  0xd9, 0x59, 0xdd, 0x6f, 0x6a, 0xdc, 0x58, 0xd9, 0x62, 0xfe, 0xe2, 0x59,
+  0xd7, 0x5c, 0xe9, 0xef, 0x5e, 0xd9, 0x5a, 0xdf, 0x75, 0x69, 0xdd, 0x5b,
+  0xdd, 0x68, 0x7d, 0xe7, 0x5f, 0xde, 0x63, 0xee, 0xf7, 0x69, 0xe4, 0x64,
+  0xe9, 0x72, 0x7b, 0xee, 0x6b, 0xea, 0x6c, 0xf0, 0x79, 0x7b, 0xf2, 0x6d,
+  0xeb, 0x6b, 0xee, 0x76, 0x78, 0xec, 0x66, 0xe4, 0x67, 0xf0, 0xf7, 0x67,
+  0xdf, 0x5e, 0xe4, 0x70, 0x6e, 0xe0, 0x5c, 0xdc, 0x63, 0xfa, 0xe7, 0x5c,
+  0xd9, 0x5c, 0xe8, 0xf5, 0x5f, 0xd9, 0x58, 0xde, 0x6f, 0x6a, 0xdc, 0x58,
+  0xda, 0x62, 0x7e, 0xe3, 0x5a, 0xd9, 0x5d, 0xeb, 0xf0, 0x5f, 0xdb, 0x5b,
+  0xe2, 0x74, 0x6b, 0xdf, 0x5d, 0xdf, 0x69, 0x7e, 0xea, 0x63, 0xe1, 0x65,
+  0xee, 0xfd, 0x6e, 0xe8, 0x68, 0xea, 0x6f, 0xfc, 0xf8, 0x6f, 0xed, 0x6c,
+  0xed, 0x70, 0xf9, 0xf9, 0x6e, 0xe9, 0x68, 0xe9, 0x6f, 0x7c, 0xec, 0x64,
+  0xe1, 0x64, 0xed, 0xf8, 0x66, 0xde, 0x5d, 0xe1, 0x6f, 0x6e, 0xdf, 0x5b,
+  0xdc, 0x63, 0xfb, 0xe6, 0x5b, 0xd9, 0x5c, 0xe8, 0xf4, 0x5f, 0xd9, 0x59,
+  0xde, 0x70, 0x6a, 0xdc, 0x59, 0xdb, 0x64, 0x7e, 0xe3, 0x5b, 0xda, 0x5e,
+  0xec, 0xf0, 0x61, 0xdc, 0x5d, 0xe4, 0x75, 0x6d, 0xe2, 0x5f, 0xe1, 0x6b,
+  0xfb, 0xee, 0x67, 0xe5, 0x68, 0xee, 0x7c, 0x75, 0xed, 0x6b, 0xec, 0x6e,
+  0xf4, 0x7d, 0x77, 0xef, 0x6c, 0xea, 0x6b, 0xf1, 0xff, 0x6f, 0xe8, 0x64,
+  0xe6, 0x6c, 0xff, 0xeb, 0x62, 0xdf, 0x61, 0xec, 0xf8, 0x65, 0xdd, 0x5c,
+  0xe0, 0x6f, 0x6d, 0xdf, 0x5a, 0xdb, 0x63, 0xfa, 0xe6, 0x5b, 0xd9, 0x5c,
+  0xe8, 0xf3, 0x5f, 0xda, 0x59, 0xdf, 0x71, 0x6a, 0xdd, 0x59, 0xdc, 0x65,
+  0x7d, 0xe5, 0x5d, 0xdc, 0x5f, 0xed, 0xf3, 0x64, 0xde, 0x5f, 0xe6, 0x75,
+  0x70, 0xe6, 0x63, 0xe5, 0x6c, 0xf8, 0xf3, 0x6c, 0xe9, 0x6a, 0xed, 0x76,
+  0x7e, 0xf5, 0x6e, 0xec, 0x6c, 0xee, 0x74, 0xfe, 0xf2, 0x6b, 0xe8, 0x68,
+  0xec, 0x7a, 0x6f, 0xe6, 0x61, 0xe2, 0x6a, 0xfc, 0xea, 0x60, 0xdd, 0x5f,
+  0xea, 0xf8, 0x64, 0xdc, 0x5b, 0xdf, 0x6e, 0x6d, 0xde, 0x5a, 0xdb, 0x63,
+  0xfb, 0xe5, 0x5b, 0xd9, 0x5d, 0xea, 0xf3, 0x5f, 0xda, 0x5a, 0xe0, 0x72,
+  0x6b, 0xde, 0x5b, 0xdd, 0x67, 0x7e, 0xe7, 0x5e, 0xdd, 0x62, 0xee, 0xf6,
+  0x68, 0xe1, 0x62, 0xe8, 0x74, 0x75, 0xeb, 0x68, 0xe8, 0x6c, 0xf6, 0xfb,
+  0x72, 0xed, 0x6d, 0xed, 0x70, 0xf7, 0xfe, 0x74, 0xed, 0x6a, 0xea, 0x6d,
+  0xf7, 0xf6, 0x6a, 0xe5, 0x64, 0xe9, 0x75, 0x70, 0xe5, 0x5f, 0xdf, 0x67,
+  0xfa, 0xea, 0x5f, 0xdc, 0x5e, 0xe9, 0xf8, 0x63, 0xdc, 0x5b, 0xdf, 0x6f,
+  0x6c, 0xde, 0x5a, 0xdb, 0x63, 0xfc, 0xe6, 0x5c, 0xda, 0x5e, 0xea, 0xf3,
+  0x61, 0xdb, 0x5c, 0xe2, 0x74, 0x6c, 0xdf, 0x5d, 0xdf, 0x69, 0x7e, 0xe9,
+  0x61, 0xdf, 0x64, 0xef, 0xfa, 0x6b, 0xe6, 0x65, 0xea, 0x72, 0x7b, 0xef,
+  0x6c, 0xeb, 0x6d, 0xf1, 0x7a, 0x7a, 0xf3, 0x6e, 0xec, 0x6c, 0xef, 0x78,
+  0x78, 0xed, 0x68, 0xe7, 0x6a, 0xf2, 0xf7, 0x6a, 0xe2, 0x61, 0xe6, 0x72,
+  0x70, 0xe3, 0x5e, 0xde, 0x66, 0xfa, 0xe9, 0x5e, 0xdc, 0x5e, 0xe9, 0xf8,
+  0x62, 0xdb, 0x5b, 0xdf, 0x6f, 0x6c, 0xde, 0x5a, 0xdc, 0x64, 0xfd, 0xe6,
+  0x5c, 0xdb, 0x5e, 0xeb, 0xf4, 0x62, 0xdd, 0x5d, 0xe3, 0x74, 0x6d, 0xe2,
+  0x5e, 0xe1, 0x69, 0xfd, 0xed, 0x65, 0xe3, 0x67, 0xee, 0xfe, 0x6f, 0xea,
+  0x69, 0xeb, 0x70, 0xfb, 0xf7, 0x70, 0xed, 0x6d, 0xee, 0x72, 0xfa, 0xf9,
+  0x6f, 0xeb, 0x6a, 0xeb, 0x71, 0x7c, 0xed, 0x67, 0xe3, 0x66, 0xee, 0xf9,
+  0x69, 0xe0, 0x5f, 0xe4, 0x71, 0x71, 0xe2, 0x5d, 0xdd, 0x65, 0xf8, 0xe9,
+  0x5e, 0xdb, 0x5e, 0xe8, 0xf7, 0x63, 0xdb, 0x5b, 0xdf, 0x70, 0x6d, 0xde,
+  0x5b, 0xdc, 0x65, 0xfc, 0xe7, 0x5d, 0xdc, 0x5f, 0xec, 0xf5, 0x64, 0xde,
+  0x5e, 0xe5, 0x73, 0x6f, 0xe5, 0x60, 0xe3, 0x6a, 0xfc, 0xf0, 0x68, 0xe7,
+  0x69, 0xef, 0x7b, 0x75, 0xee, 0x6c, 0xec, 0x6f, 0xf5, 0x7d, 0x77, 0xef,
+  0x6d, 0xec, 0x6d, 0xf3, 0xff, 0x70, 0xea, 0x66, 0xe8, 0x6e, 0xfe, 0xed,
+  0x65, 0xe1, 0x64, 0xed, 0xfa, 0x68, 0xdf, 0x5e, 0xe2, 0x6f, 0x6f, 0xe1,
+  0x5d, 0xdd, 0x65, 0xf9, 0xe9, 0x5e, 0xdb, 0x5e, 0xe9, 0xf6, 0x63, 0xdc,
+  0x5b, 0xe0, 0x71, 0x6d, 0xdf, 0x5c, 0xdd, 0x66, 0xfe, 0xe9, 0x5f, 0xdd,
+  0x61, 0xed, 0xf7, 0x67, 0xe0, 0x60, 0xe7, 0x74, 0x73, 0xe8, 0x64, 0xe6,
+  0x6c, 0xf8, 0xf5, 0x6c, 0xeb, 0x6b, 0xee, 0x76, 0x7d, 0xf4, 0x6f, 0xed,
+  0x6d, 0xef, 0x75, 0x7e, 0xf4, 0x6c, 0xe9, 0x6a, 0xee, 0x7a, 0x71, 0xe9,
+  0x64, 0xe5, 0x6b, 0xfc, 0xed, 0x63, 0xdf, 0x62, 0xeb, 0xfa, 0x67, 0xde,
+  0x5d, 0xe1, 0x6f, 0x6f, 0xe0, 0x5c, 0xdd, 0x64, 0xf9, 0xe8, 0x5e, 0xdb,
+  0x5e, 0xea, 0xf6, 0x64, 0xdc, 0x5c, 0xe1, 0x72, 0x6d, 0xe0, 0x5d, 0xde,
+  0x68, 0xfd, 0xea, 0x61, 0xdf, 0x63, 0xed, 0xf9, 0x6a, 0xe4, 0x63, 0xe9,
+  0x74, 0x77, 0xec, 0x69, 0xe9, 0x6d, 0xf6, 0xfd, 0x73, 0xee, 0x6d, 0xee,
+  0x72, 0xf9, 0xfd, 0x74, 0xee, 0x6c, 0xec, 0x6f, 0xf9, 0xf6, 0x6c, 0xe8,
+  0x66, 0xeb, 0x76, 0x72, 0xe8, 0x62, 0xe2, 0x69, 0xfa, 0xec, 0x62, 0xde,
+  0x60, 0xea, 0xfb, 0x66, 0xde, 0x5d, 0xe1, 0x6f, 0x6f, 0xe0, 0x5c, 0xdd,
+  0x65, 0xfa, 0xe9, 0x5e, 0xdc, 0x5f, 0xeb, 0xf7, 0x64, 0xdd, 0x5d, 0xe3,
+  0x72, 0x6e, 0xe2, 0x5e, 0xe0, 0x69, 0xfc, 0xec, 0x64, 0xe1, 0x65, 0xef,
+  0xfc, 0x6c, 0xe7, 0x66, 0xeb, 0x72, 0x7c, 0xf1, 0x6c, 0xec, 0x6d, 0xf2,
+  0x7b, 0x7a, 0xf2, 0x6e, 0xed, 0x6e, 0xf1, 0x7a, 0x79, 0xee, 0x6a, 0xe9,
+  0x6c, 0xf4, 0xf9, 0x6b, 0xe5, 0x64, 0xe8, 0x73, 0x73, 0xe6, 0x60, 0xe0,
+  0x68, 0xf9, 0xec, 0x61, 0xde, 0x5f, 0xea, 0xfa, 0x66, 0xde, 0x5d, 0xe1,
+  0x6f, 0x6f, 0xe1, 0x5c, 0xdd, 0x66, 0xfb, 0xe9, 0x5e, 0xdd, 0x5f, 0xec,
+  0xf7, 0x65, 0xde, 0x5e, 0xe5, 0x74, 0x6f, 0xe5, 0x60, 0xe2, 0x6a, 0xfc,
+  0xee, 0x67, 0xe5, 0x68, 0xef, 0xfe, 0x71, 0xeb, 0x6a, 0xec, 0x72, 0xfc,
+  0xf8, 0x71, 0xee, 0x6e, 0xef, 0x74, 0xfb, 0xf9, 0x70, 0xed, 0x6c, 0xed,
+  0x73, 0x7d, 0xef, 0x69, 0xe6, 0x68, 0xef, 0xfa, 0x6b, 0xe3, 0x62, 0xe6,
+  0x72, 0x73, 0xe5, 0x5f, 0xdf, 0x67, 0xf8, 0xeb, 0x60, 0xdd, 0x5f, 0xea,
+  0xfb, 0x65, 0xde, 0x5d, 0xe2, 0x6f, 0x6e, 0xe1, 0x5d, 0xde, 0x66, 0xfd,
+  0xea, 0x5f, 0xde, 0x61, 0xed, 0xf8, 0x67, 0xe0, 0x60, 0xe7, 0x74, 0x71,
+  0xe8, 0x63, 0xe5, 0x6c, 0xfa, 0xf1, 0x6b, 0xe8, 0x6a, 0xef, 0x7c, 0x77,
+  0xef, 0x6d, 0xed, 0x71, 0xf5, 0x7e, 0x79, 0xf0, 0x6e, 0xed, 0x6f, 0xf4,
+  0xfe, 0x73, 0xeb, 0x69, 0xe9, 0x6f, 0xfd, 0xee, 0x67, 0xe4, 0x66, 0xed,
+  0xfb, 0x6a, 0xe2, 0x60, 0xe5, 0x70, 0x72, 0xe5, 0x5e, 0xdf, 0x66, 0xf8,
+  0xeb, 0x5f, 0xdd, 0x5f, 0xea, 0xfa, 0x65, 0xde, 0x5d, 0xe3, 0x6f, 0x6e,
+  0xe2, 0x5d, 0xdf, 0x67, 0xfc, 0xeb, 0x60, 0xdf, 0x63, 0xed, 0xf9, 0x69,
+  0xe3, 0x63, 0xe9, 0x74, 0x75, 0xea, 0x67, 0xe8, 0x6d, 0xf9, 0xf7, 0x6e,
+  0xec, 0x6c, 0xef, 0x78, 0x7e, 0xf5, 0x71, 0xee, 0x6e, 0xf0, 0x77, 0xfe,
+  0xf4, 0x6e, 0xeb, 0x6c, 0xef, 0x7b, 0x74, 0xea, 0x67, 0xe7, 0x6d, 0xfb,
+  0xee, 0x66, 0xe1, 0x64, 0xec, 0xfc, 0x69, 0xe1, 0x5f, 0xe4, 0x6f, 0x72,
+  0xe4, 0x5e, 0xdf, 0x66, 0xf9, 0xeb, 0x5f, 0xdd, 0x5f, 0xeb, 0xf9, 0x66,
+  0xde, 0x5e, 0xe3, 0x71, 0x6f, 0xe4, 0x5f, 0xe0, 0x69, 0xfc, 0xec, 0x63,
+  0xe1, 0x65, 0xef, 0xfb, 0x6b, 0xe6, 0x65, 0xea, 0x74, 0x79, 0xed, 0x6a,
+  0xea, 0x6e, 0xf6, 0xfc, 0x73, 0xef, 0x6e, 0xef, 0x73, 0xf9, 0xfd, 0x75,
+  0xef, 0x6d, 0xed, 0x70, 0xfa, 0xf7, 0x6e, 0xe9, 0x69, 0xec, 0x77, 0x74,
+  0xe9, 0x65, 0xe5, 0x6b, 0xfa, 0xee, 0x64, 0xe0, 0x63, 0xec, 0xfc, 0x69,
+  0xe0, 0x5f, 0xe3, 0x6f, 0x72, 0xe4, 0x5e, 0xdf, 0x66, 0xfa, 0xeb, 0x60,
+  0xde, 0x60, 0xeb, 0xf9, 0x67, 0xdf, 0x5f, 0xe5, 0x72, 0x70, 0xe5, 0x60,
+  0xe2, 0x6a, 0xfc, 0xee, 0x66, 0xe4, 0x67, 0xef, 0xfd, 0x6e, 0xe9, 0x68,
+  0xec, 0x73, 0x7d, 0xf3, 0x6e, 0xed, 0x6e, 0xf3, 0x7b, 0x7b, 0xf5, 0x70,
+  0xee, 0x6f, 0xf3, 0x7a, 0x7a, 0xef, 0x6c, 0xeb, 0x6d, 0xf5, 0xf9, 0x6d,
+  0xe7, 0x67, 0xea, 0x75, 0x75, 0xe8, 0x63, 0xe3, 0x69, 0xf8, 0xed, 0x64,
+  0xdf, 0x62, 0xeb, 0xfc, 0x69, 0xdf, 0x5f, 0xe3, 0x70, 0x71, 0xe3, 0x5e,
+  0xdf, 0x67, 0xfa, 0xeb, 0x61, 0xdf, 0x61, 0xed, 0xfa, 0x68, 0xe1, 0x60,
+  0xe7, 0x73, 0x71, 0xe7, 0x62, 0xe5, 0x6b, 0xfb, 0xf0, 0x69, 0xe7, 0x69,
+  0xef, 0x7e, 0x72, 0xed, 0x6b, 0xed, 0x72, 0xfc, 0xf9, 0x72, 0xef, 0x6e,
+  0xf1, 0x74, 0xfc, 0xf9, 0x71, 0xee, 0x6d, 0xee, 0x75, 0x7d, 0xf0, 0x6b,
+  0xe8, 0x6b, 0xf0, 0xfa, 0x6d, 0xe6, 0x64, 0xe8, 0x72, 0x75, 0xe8, 0x62,
+  0xe2, 0x69, 0xf8, 0xed, 0x63, 0xdf, 0x61, 0xeb, 0xfc, 0x68, 0xdf, 0x5f,
+  0xe4, 0x70, 0x71, 0xe4, 0x5f, 0xe0, 0x68, 0xfb, 0xec, 0x62, 0xdf, 0x63,
+  0xed, 0xfa, 0x69, 0xe3, 0x62, 0xe8, 0x74, 0x74, 0xea, 0x65, 0xe7, 0x6c,
+  0xfa, 0xf4, 0x6c, 0xea, 0x6b, 0xef, 0x7a, 0x78, 0xf0, 0x6e, 0xee, 0x71,
+  0xf6, 0xff, 0x79, 0xf2, 0x6f, 0xee, 0x70, 0xf5, 0xfd, 0x74, 0xed, 0x6b,
+  0xec, 0x70, 0xfe, 0xf0, 0x69, 0xe7, 0x68, 0xef, 0xfd, 0x6c, 0xe5, 0x62,
+  0xe7, 0x70, 0x74, 0xe7, 0x60, 0xe1, 0x67, 0xf9, 0xee, 0x62, 0xdf, 0x61,
+  0xeb, 0xfb, 0x68, 0xe0, 0x5f, 0xe4, 0x70, 0x71, 0xe5, 0x5f, 0xe1, 0x69,
+  0xfb, 0xed, 0x64, 0xe1, 0x65, 0xee, 0xfb, 0x6b, 0xe5, 0x65, 0xe9, 0x74,
+  0x78, 0xec, 0x69, 0xe9, 0x6e, 0xf8, 0xf8, 0x6f, 0xec, 0x6d, 0xef, 0x77,
+  0x7e, 0xf6, 0x72, 0xef, 0x70, 0xf2, 0x78, 0x7e, 0xf6, 0x6f, 0xed, 0x6d,
+  0xf0, 0x7b, 0x75, 0xed, 0x69, 0xe9, 0x6e, 0xfb, 0xf0, 0x68, 0xe5, 0x66,
+  0xee, 0xfd, 0x6b, 0xe4, 0x61, 0xe6, 0x70, 0x74, 0xe7, 0x60, 0xe1, 0x67,
+  0xf8, 0xed, 0x63, 0xdf, 0x62, 0xeb, 0xfb, 0x69, 0xe0, 0x5f, 0xe5, 0x71,
+  0x72, 0xe6, 0x61, 0xe2, 0x6a, 0xfa, 0xee, 0x66, 0xe3, 0x67, 0xee, 0xfc,
+  0x6e, 0xe8, 0x67, 0xeb, 0x74, 0x7c, 0xef, 0x6c, 0xeb, 0x6e, 0xf6, 0xfd,
+  0x75, 0xf0, 0x6f, 0xef, 0x74, 0xfa, 0xfd, 0x75, 0xf0, 0x6e, 0xef, 0x73,
+  0xfb, 0xf8, 0x6e, 0xeb, 0x6a, 0xee, 0x78, 0x76, 0xeb, 0x67, 0xe7, 0x6c,
+  0xfa, 0xf0, 0x67, 0xe3, 0x65, 0xed, 0xfe, 0x6b, 0xe3, 0x61, 0xe5, 0x6f,
+  0x74, 0xe7, 0x60, 0xe1, 0x67, 0xf9, 0xee, 0x63, 0xdf, 0x62, 0xec, 0xfc,
+  0x69, 0xe2, 0x60, 0xe6, 0x72, 0x73, 0xe8, 0x63, 0xe4, 0x6b, 0xfa, 0xef,
+  0x68, 0xe5, 0x69, 0xef, 0xfe, 0x70, 0xea, 0x6a, 0xec, 0x74, 0x7e, 0xf3,
+  0x6f, 0xed, 0x6f, 0xf3, 0x7c, 0x7b, 0xf4, 0x71, 0xef, 0x71, 0xf4, 0x7c,
+  0x7a, 0xf0, 0x6e, 0xec, 0x6f, 0xf6, 0xfa, 0x6f, 0xea, 0x69, 0xeb, 0x76,
+  0x77, 0xeb, 0x66, 0xe5, 0x6b, 0xf8, 0xef, 0x67, 0xe2, 0x64, 0xed, 0xfe,
+  0x6b, 0xe3, 0x60, 0xe5, 0x6f, 0x74, 0xe7, 0x60, 0xe1, 0x68, 0xf9, 0xee,
+  0x63, 0xe1, 0x63, 0xed, 0xfd, 0x6a, 0xe4, 0x62, 0xe8, 0x72, 0x75, 0xe9,
+  0x65, 0xe6, 0x6c, 0xfa, 0xf2, 0x6b, 0xe8, 0x6a, 0xef, 0x7d, 0x74, 0xed,
+  0x6d, 0xed, 0x73, 0xfb, 0xf9, 0x74, 0xef, 0x70, 0xf1, 0x77, 0xfd, 0xfa,
+  0x73, 0xef, 0x6e, 0xef, 0x76, 0x7d, 0xf2, 0x6c, 0xeb, 0x6c, 0xf2, 0xfc,
+  0x6e, 0xe9, 0x67, 0xea, 0x72, 0x77, 0xea, 0x65, 0xe4, 0x6a, 0xf8, 0xef,
+  0x66, 0xe2, 0x63, 0xec, 0xff, 0x6b, 0xe3, 0x60, 0xe5, 0x70, 0x75, 0xe7,
+  0x61, 0xe2, 0x69, 0xf9, 0xee, 0x65, 0xe2, 0x64, 0xed, 0xfd, 0x6c, 0xe5,
+  0x64, 0xe9, 0x73, 0x77, 0xeb, 0x67, 0xe8, 0x6d, 0xf9, 0xf6, 0x6d, 0xeb,
+  0x6c, 0xef, 0x7a, 0x7a, 0xf1, 0x6f, 0xee, 0x72, 0xf7, 0xff, 0x78, 0xf3,
+  0x70, 0xef, 0x72, 0xf7, 0xfe, 0x75, 0xee, 0x6c, 0xed, 0x72, 0xfd, 0xf2,
+  0x6b, 0xe8, 0x6a, 0xef, 0xfd, 0x6e, 0xe7, 0x65, 0xe9, 0x71, 0x78, 0xea,
+  0x63, 0xe4, 0x69, 0xf7, 0xef, 0x66, 0xe1, 0x64, 0xec, 0xff, 0x6b, 0xe3,
+  0x61, 0xe6, 0x70, 0x75, 0xe8, 0x62, 0xe3, 0x6a, 0xf9, 0xef, 0x67, 0xe3,
+  0x66, 0xee, 0xfe, 0x6d, 0xe7, 0x66, 0xea, 0x74, 0x7a, 0xee, 0x6a, 0xea,
+  0x6e, 0xf7, 0xfa, 0x70, 0xee, 0x6e, 0xf1, 0x77, 0x7e, 0xf7, 0x72, 0xf0,
+  0x71, 0xf4, 0x79, 0x7d, 0xf6, 0x71, 0xee, 0x6f, 0xf2, 0x7c, 0x77, 0xee,
+  0x6b, 0xeb, 0x6f, 0xfb, 0xf2, 0x6b, 0xe7, 0x68, 0xee, 0xff, 0x6e, 0xe6,
+  0x64, 0xe7, 0x70, 0x78, 0xea, 0x63, 0xe3, 0x69, 0xf7, 0xef, 0x66, 0xe2,
+  0x64, 0xec, 0xfe, 0x6b, 0xe4, 0x62, 0xe6, 0x71, 0x76, 0xe9, 0x63, 0xe4,
+  0x6a, 0xf9, 0xf1, 0x68, 0xe5, 0x67, 0xef, 0x7e, 0x6f, 0xea, 0x68, 0xec,
+  0x74, 0x7c, 0xf1, 0x6c, 0xec, 0x6f, 0xf6, 0xfe, 0x76, 0xf1, 0x6f, 0xf0,
+  0x75, 0xfb, 0xfd, 0x76, 0xf1, 0x6f, 0xef, 0x74, 0xfb, 0xf9, 0x70, 0xec,
+  0x6d, 0xee, 0x79, 0x78, 0xed, 0x6a, 0xe9, 0x6d, 0xf9, 0xf2, 0x6a, 0xe6,
+  0x67, 0xed, 0x7e, 0x6d, 0xe6, 0x63, 0xe7, 0x70, 0x78, 0xe9, 0x63, 0xe3,
+  0x69, 0xf7, 0xf0, 0x66, 0xe2, 0x64, 0xec, 0xfe, 0x6c, 0xe5, 0x63, 0xe7,
+  0x71, 0x77, 0xea, 0x65, 0xe6, 0x6b, 0xf8, 0xf3, 0x6a, 0xe8, 0x69, 0xef,
+  0x7e, 0x72, 0xec, 0x6b, 0xed, 0x74, 0xfe, 0xf5, 0x6f, 0xee, 0x6f, 0xf4,
+  0x7c, 0x7b, 0xf5, 0x71, 0xf0, 0x72, 0xf6, 0x7b, 0x79, 0xf3, 0x6e, 0xee,
+  0x6f, 0xf7, 0xfb, 0x6f, 0xec, 0x6b, 0xed, 0x76, 0x79, 0xed, 0x68, 0xe7,
+  0x6c, 0xf7, 0xf2, 0x69, 0xe5, 0x66, 0xec, 0x7e, 0x6e, 0xe5, 0x63, 0xe7,
+  0x6f, 0x78, 0xe9, 0x63, 0xe4, 0x69, 0xf7, 0xf0, 0x67, 0xe3, 0x65, 0xed,
+  0xff, 0x6d, 0xe6, 0x65, 0xe9, 0x73, 0x78, 0xeb, 0x67, 0xe7, 0x6c, 0xf8,
+  0xf5, 0x6d, 0xea, 0x6b, 0xf0, 0x7c, 0x76, 0xef, 0x6d, 0xee, 0x72, 0xfc,
+  0xfb, 0x74, 0xf2, 0x70, 0xf3, 0x76, 0xfe, 0xfb, 0x75, 0xf0, 0x6f, 0xf1,
+  0x77, 0x7d, 0xf4, 0x6e, 0xec, 0x6d, 0xf3, 0xfe, 0x70, 0xeb, 0x69, 0xeb,
+  0x73, 0x7a, 0xed, 0x67, 0xe7, 0x6b, 0xf7, 0xf4, 0x69, 0xe5, 0x66, 0xec,
+  0x7d, 0x6d, 0xe5, 0x63, 0xe6, 0x6f, 0x79, 0xea, 0x64, 0xe3, 0x6a, 0xf6,
+  0xf0, 0x68, 0xe4, 0x66, 0xed, 0xff, 0x6e, 0xe7, 0x66, 0xea, 0x73, 0x7a,
+  0xed, 0x69, 0xe9, 0x6d, 0xf8, 0xf8, 0x6f, 0xec, 0x6d, 0xf1, 0x7a, 0x7a,
+  0xf3, 0x6f, 0xf0, 0x72, 0xf8, 0x7e, 0x78, 0xf5, 0x70, 0xf1, 0x72, 0xf9,
+  0xff, 0x75, 0xf0, 0x6d, 0xef, 0x72, 0xfd, 0xf4, 0x6d, 0xeb, 0x6b, 0xf1,
+  0xff, 0x70, 0xea, 0x67, 0xea, 0x72, 0x7a, 0xec, 0x66, 0xe5, 0x6a, 0xf6,
+  0xf3, 0x68, 0xe4, 0x66, 0xec, 0x7d, 0x6e, 0xe6, 0x64, 0xe7, 0x70, 0x79,
+  0xea, 0x65, 0xe4, 0x6b, 0xf6, 0xf2, 0x69, 0xe5, 0x68, 0xed, 0xff, 0x6f,
+  0xe9, 0x68, 0xeb, 0x73, 0x7c, 0xef, 0x6c, 0xeb, 0x6e, 0xf6, 0xfc, 0x72,
+  0xee, 0x6e, 0xf1, 0x77, 0xfe, 0xf8, 0x73, 0xf2, 0x71, 0xf5, 0x79, 0x7d,
+  0xf7, 0x72, 0xef, 0x6f, 0xf4, 0x7b, 0x77, 0xf0, 0x6c, 0xec, 0x6f, 0xfb,
+  0xf5, 0x6c, 0xe9, 0x6a, 0xef, 0x7d, 0x6f, 0xe9, 0x66, 0xe9, 0x70, 0x7a,
+  0xec, 0x66, 0xe5, 0x6a, 0xf6, 0xf3, 0x69, 0xe4, 0x65, 0xec, 0x7d, 0x6e,
+  0xe7, 0x64, 0xe8, 0x70, 0x79, 0xeb, 0x66, 0xe6, 0x6b, 0xf7, 0xf4, 0x6b,
+  0xe7, 0x69, 0xee, 0x7d, 0x72, 0xeb, 0x6a, 0xec, 0x73, 0xff, 0xf2, 0x6e,
+  0xed, 0x6f, 0xf5, 0xfe, 0x78, 0xf2, 0x70, 0xf1, 0x75, 0xfb, 0xfd, 0x77,
+  0xf3, 0x70, 0xf1, 0x75, 0xfb, 0xfa, 0x73, 0xee, 0x6e, 0xf0, 0x78, 0x79,
+  0xef, 0x6b, 0xeb, 0x6e, 0xf8, 0xf5, 0x6c, 0xe8, 0x69, 0xee, 0x7c, 0x6f,
+  0xe9, 0x66, 0xe8, 0x6f, 0x7a, 0xec, 0x66, 0xe5, 0x6a, 0xf6, 0xf4, 0x69,
+  0xe5, 0x66, 0xed, 0x7c, 0x6e, 0xe7, 0x65, 0xe9, 0x71, 0x7a, 0xec, 0x68,
+  0xe7, 0x6c, 0xf7, 0xf6, 0x6c, 0xe9, 0x6b, 0xef, 0x7c, 0x75, 0xed, 0x6c,
+  0xed, 0x74, 0xfd, 0xf7, 0x71, 0xef, 0x70, 0xf4, 0x7c, 0x7c, 0xf6, 0x74,
+  0xf1, 0x73, 0xf6, 0x7d, 0x7b, 0xf3, 0x70, 0xef, 0x71, 0xf7, 0xfd, 0x72,
+  0xee, 0x6c, 0xee, 0x75, 0x7a, 0xef, 0x6a, 0xea, 0x6d, 0xf7, 0xf6, 0x6b,
+  0xe7, 0x68, 0xed, 0x7b, 0x6f, 0xe8, 0x66, 0xe9, 0x6f, 0x7b, 0xec, 0x66,
+  0xe5, 0x6a, 0xf6, 0xf4, 0x6a, 0xe6, 0x67, 0xed, 0x7c, 0x6f, 0xe9, 0x66,
+  0xe9, 0x72, 0x7a, 0xee, 0x69, 0xe9, 0x6d, 0xf8, 0xf8, 0x6e, 0xeb, 0x6c,
+  0xf0, 0x7b, 0x78, 0xf0, 0x6e, 0xef, 0x74, 0xfa, 0xfb, 0x76, 0xf1, 0x71,
+  0xf3, 0x78, 0xfd, 0xfa, 0x76, 0xf1, 0x71, 0xf3, 0x78, 0x7e, 0xf5, 0x6f,
+  0xed, 0x6f, 0xf4, 0xfe, 0x72, 0xec, 0x6b, 0xec, 0x74, 0x7c, 0xee, 0x6a,
+  0xe8, 0x6c, 0xf6, 0xf5, 0x6b, 0xe7, 0x67, 0xed, 0x7c, 0x6f, 0xe9, 0x65,
+  0xe9, 0x6f, 0x7a, 0xed, 0x66, 0xe6, 0x6a, 0xf6, 0xf5, 0x6a, 0xe7, 0x68,
+  0xee, 0x7c, 0x70, 0xea, 0x68, 0xeb, 0x72, 0x7c, 0xef, 0x6b, 0xeb, 0x6e,
+  0xf7, 0xfa, 0x70, 0xee, 0x6d, 0xf1, 0x79, 0x7b, 0xf5, 0x70, 0xf0, 0x74,
+  0xf7, 0x7e, 0x7a, 0xf4, 0x73, 0xf1, 0x75, 0xf9, 0xfe, 0x77, 0xf1, 0x6f,
+  0xef, 0x74, 0xfc, 0xf6, 0x6f, 0xec, 0x6d, 0xf1, 0x7e, 0x73, 0xeb, 0x6a,
+  0xeb, 0x73, 0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf5, 0xf6, 0x6b, 0xe7, 0x67,
+  0xed, 0x7b, 0x6f, 0xe9, 0x66, 0xe9, 0x6f, 0x7b, 0xed, 0x67, 0xe7, 0x6b,
+  0xf7, 0xf6, 0x6b, 0xe8, 0x68, 0xee, 0x7c, 0x72, 0xeb, 0x69, 0xec, 0x73,
+  0x7d, 0xf2, 0x6d, 0xec, 0x6f, 0xf7, 0xfc, 0x75, 0xef, 0x6f, 0xf1, 0x78,
+  0xfe, 0xf9, 0x75, 0xf2, 0x73, 0xf5, 0x7a, 0x7e, 0xf8, 0x74, 0xf1, 0x71,
+  0xf4, 0x7c, 0x79, 0xf1, 0x6e, 0xee, 0x71, 0xfb, 0xf7, 0x6e, 0xeb, 0x6b,
+  0xf0, 0x7c, 0x72, 0xeb, 0x69, 0xea, 0x71, 0x7d, 0xee, 0x68, 0xe7, 0x6b,
+  0xf5, 0xf6, 0x6b, 0xe7, 0x68, 0xed, 0x7c, 0x70, 0xe9, 0x67, 0xe9, 0x70,
+  0x7b, 0xed, 0x68, 0xe8, 0x6c, 0xf7, 0xf6, 0x6c, 0xe9, 0x6a, 0xef, 0x7b,
+  0x74, 0xed, 0x6b, 0xed, 0x73, 0xff, 0xf5, 0x6f, 0xee, 0x6f, 0xf6, 0x7e,
+  0x79, 0xf3, 0x71, 0xf2, 0x76, 0xfb, 0xfd, 0x78, 0xf5, 0x72, 0xf3, 0x76,
+  0xfc, 0xfa, 0x74, 0xef, 0x6f, 0xf2, 0x79, 0x7a, 0xf1, 0x6d, 0xec, 0x6f,
+  0xf9, 0xf7, 0x6d, 0xea, 0x6b, 0xee, 0x7c, 0x72, 0xeb, 0x68, 0xea, 0x70,
+  0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf4, 0xf6, 0x6c, 0xe7, 0x68, 0xed, 0x7b,
+  0x71, 0xea, 0x67, 0xea, 0x71, 0x7c, 0xee, 0x69, 0xe9, 0x6c, 0xf6, 0xf8,
+  0x6e, 0xeb, 0x6b, 0xf0, 0x7a, 0x76, 0xef, 0x6d, 0xee, 0x73, 0xfd, 0xf9,
+  0x72, 0xf0, 0x70, 0xf5, 0x7b, 0x7c, 0xf7, 0x73, 0xf3, 0x74, 0xf8, 0x7c,
+  0x7b, 0xf5, 0x71, 0xf1, 0x72, 0xf9, 0xfd, 0x74, 0xee, 0x6d, 0xef, 0x77,
+  0x7c, 0xf1, 0x6c, 0xeb, 0x6e, 0xf7, 0xf7, 0x6d, 0xe9, 0x6a, 0xee, 0x7c,
+  0x72, 0xea, 0x68, 0xea, 0x70, 0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf5, 0xf6,
+  0x6c, 0xe7, 0x69, 0xed, 0x7c, 0x72, 0xea, 0x68, 0xeb, 0x71, 0x7d, 0xef,
+  0x6b, 0xea, 0x6d, 0xf7, 0xfa, 0x6f, 0xec, 0x6d, 0xf1, 0x7a, 0x7a, 0xf2,
+  0x6f, 0xef, 0x74, 0xfa, 0xfc, 0x76, 0xf3, 0x72, 0xf4, 0x78, 0xfe, 0xfb,
+  0x75, 0xf3, 0x72, 0xf4, 0x79, 0x7e, 0xf7, 0x70, 0xef, 0x70, 0xf5, 0xfe,
+  0x75, 0xee, 0x6c, 0xee, 0x75, 0x7d, 0xf0, 0x6c, 0xea, 0x6d, 0xf7, 0xf8,
+  0x6d, 0xe9, 0x69, 0xee, 0x7b, 0x73, 0xea, 0x68, 0xea, 0x70, 0x7d, 0xee,
+  0x69, 0xe8, 0x6b, 0xf6, 0xf7, 0x6c, 0xe8, 0x69, 0xee, 0x7b, 0x73, 0xec,
+  0x6a, 0xec, 0x71, 0x7d, 0xf2, 0x6c, 0xec, 0x6e, 0xf7, 0xfc, 0x73, 0xee,
+  0x6e, 0xf1, 0x79, 0x7c, 0xf6, 0x72, 0xf1, 0x74, 0xf9, 0x7e, 0x7a, 0xf6,
+  0x73, 0xf3, 0x75, 0xfa, 0xfe, 0x78, 0xf3, 0x70, 0xf1, 0x76, 0xfd, 0xf7,
+  0x70, 0xed, 0x6e, 0xf3, 0x7e, 0x75, 0xed, 0x6c, 0xed, 0x73, 0x7e, 0xf0,
+  0x6b, 0xea, 0x6c, 0xf6, 0xf8, 0x6d, 0xe9, 0x69, 0xee, 0x7b, 0x72, 0xeb,
+  0x68, 0xea, 0x70, 0x7d, 0xee, 0x69, 0xe9, 0x6c, 0xf5, 0xf8, 0x6d, 0xea,
+  0x6a, 0xef, 0x7b, 0x75, 0xed, 0x6b, 0xed, 0x72, 0xff, 0xf4, 0x6e, 0xed,
+  0x6f, 0xf6, 0xfe, 0x75, 0xf1, 0x70, 0xf3, 0x78, 0xfe, 0xf9, 0x75, 0xf3,
+  0x73, 0xf6, 0x7b, 0x7d, 0xf9, 0x74, 0xf2, 0x72, 0xf7, 0x7c, 0x7a, 0xf3,
+  0x6f, 0xef, 0x73, 0xfb, 0xf8, 0x6f, 0xed, 0x6d, 0xf1, 0x7c, 0x75, 0xed,
+  0x6b, 0xec, 0x73, 0x7d, 0xef, 0x6b, 0xe9, 0x6c, 0xf6, 0xf8, 0x6d, 0xe9,
+  0x69, 0xee, 0x7a, 0x73, 0xeb, 0x69, 0xeb, 0x71, 0x7e, 0xef, 0x6b, 0xe9,
+  0x6d, 0xf6, 0xf8, 0x6e, 0xea, 0x6c, 0xef, 0x7c, 0x77, 0xee, 0x6d, 0xee,
+  0x74, 0xfe, 0xf7, 0x70, 0xef, 0x70, 0xf6, 0x7d, 0x78, 0xf5, 0x71, 0xf4,
+  0x76, 0xfb, 0xfe, 0x78, 0xf6, 0x73, 0xf5, 0x76, 0xfd, 0xfb, 0x75, 0xf2,
+  0x70, 0xf3, 0x7a, 0x7b, 0xf3, 0x6e, 0xed, 0x71, 0xf9, 0xf8, 0x6f, 0xec,
+  0x6c, 0xef, 0x7c, 0x75, 0xec, 0x6a, 0xec, 0x71, 0x7e, 0xf0, 0x6a, 0xe9,
+  0x6c, 0xf6, 0xf9, 0x6d, 0xe9, 0x69, 0xee, 0x7a, 0x73, 0xec, 0x69, 0xeb,
+  0x71, 0x7e, 0xf0, 0x6b, 0xea, 0x6d, 0xf6, 0xfa, 0x6f, 0xec, 0x6d, 0xf0,
+  0x7b, 0x79, 0xf0, 0x6e, 0xef, 0x75, 0xfc, 0xf9, 0x74, 0xf1, 0x72, 0xf6,
+  0x7b, 0x7c, 0xf8, 0x75, 0xf4, 0x74, 0xf8, 0x7d, 0x7c, 0xf7, 0x73, 0xf2,
+  0x74, 0xf9, 0xfd, 0x76, 0xf0, 0x6f, 0xf0, 0x77, 0x7d, 0xf3, 0x6d, 0xed,
+  0x6f, 0xf8, 0xfa, 0x6f, 0xeb, 0x6b, 0xef, 0x7b, 0x75, 0xec, 0x6a, 0xeb,
+  0x71, 0x7e, 0xf0, 0x6a, 0xe9, 0x6c, 0xf5, 0xf9, 0x6d, 0xea, 0x6a, 0xee,
+  0x7a, 0x74, 0xed, 0x6a, 0xec, 0x71, 0x7e, 0xf2, 0x6c, 0xec, 0x6e, 0xf5,
+  0xfc, 0x72, 0xed, 0x6e, 0xf1, 0x79, 0x7b, 0xf3, 0x70, 0xef, 0x74, 0xfa,
+  0xfc, 0x78, 0xf3, 0x73, 0xf4, 0x79, 0xfc, 0xfa, 0x77, 0xf3, 0x74, 0xf4,
+  0x7a, 0xfe, 0xf7, 0x73, 0xef, 0x71, 0xf7, 0x7e, 0x76, 0xef, 0x6e, 0xef,
+  0x75, 0x7d, 0xf3, 0x6d, 0xec, 0x6e, 0xf7, 0xfa, 0x6e, 0xeb, 0x6b, 0xef,
+  0x79, 0x74, 0xed, 0x69, 0xeb, 0x6f, 0x7e, 0xf1, 0x6a, 0xea, 0x6c, 0xf6,
+  0xfa, 0x6e, 0xeb, 0x6a, 0xef, 0x7a, 0x76, 0xee, 0x6b, 0xed, 0x72, 0xfe,
+  0xf4, 0x6e, 0xed, 0x6f, 0xf6, 0xfd, 0x75, 0xef, 0x6f, 0xf1, 0x79, 0x7e,
+  0xf6, 0x75, 0xf1, 0x76, 0xf8, 0xfd, 0x7a, 0xf5, 0x74, 0xf2, 0x76, 0xf8,
+  0x7e, 0x7a, 0xf6, 0x71, 0xec, 0x76, 0xfa, 0x7e, 0x7b, 0xf9, 0x72, 0xf5,
+  0x7d, 0xfb, 0xfb, 0x76, 0xf9, 0x75, 0x7b, 0xf1, 0x77, 0xfe, 0x7a, 0xfb,
+  0x7e, 0x73, 0xfa, 0x7e, 0xfb, 0x7d, 0x77, 0xf7, 0x7e, 0x7c, 0xfe, 0x78,
+  0xfa, 0x7e, 0xff, 0x7e, 0x76, 0xf9, 0xfd, 0xf9, 0xfe, 0x75, 0xf1, 0x76,
+  0x7c, 0xfc, 0x7a, 0xf3, 0x70, 0xfd, 0x7e, 0x7d, 0xf3, 0x70, 0xf7, 0x78,
+  0x7e, 0xf9, 0x72, 0xef, 0x77, 0xfc, 0xfd, 0x75, 0xee, 0x72, 0xf7, 0xfb
+#endif
+};
 
-void
-ctx_font_size (Ctx *ctx, float x)
-{
-  CTX_PROCESS_F1 (CTX_FONT_SIZE, x);
-}
+static short MuLawDecompressTable[256] =
+{
+     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+      -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
+       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
+       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
+       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
+       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
+       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
+        -56,   -48,   -40,   -32,   -24,   -16,    -8,     -1,
+      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
+       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
+       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
+       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
+       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
+       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
+       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
+        876,   844,   812,   780,   748,   716,   684,   652,
+        620,   588,   556,   524,   492,   460,   428,   396,
+        372,   356,   340,   324,   308,   292,   276,   260,
+        244,   228,   212,   196,   180,   164,   148,   132,
+        120,   112,   104,    96,    88,    80,    72,    64,
+         56,    48,    40,    32,    24,    16,     8,     0
+};
 
-float ctx_get_font_size  (Ctx *ctx)
-{
-  return ctx->state.gstate.font_size;
-}
 
-void
-ctx_miter_limit (Ctx *ctx, float limit)
+void vt_bell (VT *vt)
 {
-  CTX_PROCESS_F1 (CTX_MITER_LIMIT, limit);
+  if (vt->bell < 2)
+    return;
+  for (int i = 0; i < (int)sizeof (vt_bell_audio); i++)
+  {
+    int16_t val = MuLawDecompressTable[vt_bell_audio[i]] * vt->bell / 8;
+    terminal_queue_pcm (val, val);
+  }
 }
 
-float ctx_get_line_width (Ctx *ctx)
-{
-  return ctx->state.gstate.line_width;
-}
 
-void
-_ctx_font (Ctx *ctx, const char *name)
-{
-  ctx->state.gstate.font = ctx_resolve_font (name);
-}
+void terminal_queue_pcm (int16_t sample_left, int16_t sample_right);
 
-#if 0
-void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len)
+void vt_audio (VT *vt, const char *command)
 {
-  if (len <= 0) len = strlen (string);
-  ctx_process_cmd_str (ctx, CTX_SET, string, key_hash, len);
-}
+  AudioState *audio = &vt->audio;
+  // the simplest form of audio is raw audio
+  // _As=8000,c=2,b=8,e=u
+  //
+  // multiple voices:
+  //   ids to queue - store samples as images...
+  //
+  // reusing samples
+  //   .. pitch bend and be able to do a mod player?
+  const char *payload = NULL;
+  char key = 0;
+  int  value;
+  int  pos = 1;
+
+  audio->frames=0;
+  audio->action='t';
+
+  int configure = 0;
+  while (command[pos] != ';')
+  {
+    pos ++; // G or ,
+    if (command[pos] == ';') break;
+    key = command[pos]; pos++;
+    if (command[pos] == ';') break;
+    pos ++; // =
+    if (command[pos] == ';') break;
+
+    if (command[pos] >= '0' && command[pos] <= '9')
+      value = atoi(&command[pos]);
+    else
+      value = command[pos];
+    while (command[pos] &&
+           command[pos] != ',' &&
+           command[pos] != ';') pos++;
+    
+    if (value=='?')
+    {
+      char buf[256];
+      const char *range="";
+      switch (key)
+      {
+        case 's':range="8000,16000,24000,48000";break;
+        case 'b':range="8,16";break;
+        case 'B':range="512-65536";break;
+        case 'c':range="1";break;
+        case 'T':range="u,s,f";break;
+        case 'e':range="b,a";break;
+        case 'o':range="z,0";break;
+        case 'a':range="t,q";break;
+        default:range="unknown";break;
+      }
+      sprintf (buf, "\033_A%c=?;%s\033\\", key, range);
+      vt_write (vt, buf, strlen(buf));
+      return;
+    }
 
-const char *
-ctx_get (Ctx *ctx, const char *key)
-{
-  static char retbuf[32];
-  int len = 0;
-  CTX_PROCESS_U32(CTX_GET, ctx_strhash (key, 0), 0);
-  while (read (STDIN_FILENO, &retbuf[len], 1) != -1)
+    switch (key)
     {
-      if(retbuf[len]=='\n')
+      case 's': audio->samplerate = value; configure = 1; break;
+      case 'b': audio->bits = value; configure = 1; break;
+      case 'B': audio->buffer_size = value; configure = 1; break;
+      case 'c': audio->channels = value; configure = 1; break;
+      case 'a': audio->action = value; configure = 1; break;
+      case 'T': audio->type = value; configure = 1; break;
+      case 'f': audio->frames = value; configure = 1; break;
+      case 'e': audio->encoding = value; configure = 1; break;
+      case 'o': audio->compression = value; configure = 1; break;
+      case 'm': 
+        audio->mic = value?1:0;
         break;
-      retbuf[++len]=0;
     }
-  return retbuf;
-}
-#endif
 
-void
-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);
+    if (configure)
+    {
+      /* these are the specific sample rates supported by opus,
+       * instead of enabling anything SDL supports, the initial
+       * implementation limits itself to the opus sample rates
+       */
+      if (audio->samplerate <= 8000)
+      {
+        audio->samplerate = 8000;
+      }
+      else if (audio->samplerate <= 16000)
+      {
+        audio->samplerate = 16000;
+      }
+      else if (audio->samplerate <= 24000)
+      {
+        audio->samplerate = 24000;
+      }
+      else
+      {
+        audio->samplerate = 48000;
+      }
+
+      if (audio->bits != 8 && audio->bits != 16)
+        audio->bits = 8;
+
+      if (audio->buffer_size > 2048)
+        audio->buffer_size = 2048;
+      else if (audio->buffer_size < 512)
+        audio->buffer_size = 512;
+
+      switch (audio->type)
+      {
+        case 'u':
+        case 's':
+        case 'f':
+          break;
+        default:
+          audio->type = 's';
+      }
+
+      /* only 1 and 2 channels supported */
+      if (audio->channels <= 0 || audio->channels > 2)
+      {
+        audio->channels = 1;
+      }
+    }
+  }
+  
+  if (audio->frames ||  audio->action != 'd')
+  {
+  payload = &command[pos+1];
+
+  // accumulate incoming data
+  {
+     int chunk_size = strlen (payload);
+     int old_size = audio->data_size;
+     if (audio->data == NULL)
+     {
+       audio->data_size = chunk_size;
+       audio->data = malloc (audio->data_size + 1);
+     }
+     else
+     {
+       audio->data_size += chunk_size;
+       audio->data = realloc (audio->data, audio->data_size + 1);
+     }
+     memcpy (audio->data + old_size, payload, chunk_size);
+     audio->data[audio->data_size]=0;
+  }
+
+    if (audio->frames)
+    switch (audio->encoding)
+    {
+      case 'y':
+        audio->data_size = ydec (audio->data, audio->data, audio->data_size);
+      break;
+      case 'a':
+      {
+        int bin_length = audio->data_size;
+        if (bin_length)
+        {
+        uint8_t *data2 = malloc ((unsigned int)ctx_a85len ((char*)audio->data, audio->data_size) + 1);
+        // a85len is inaccurate but gives an upper bound,
+        // should be fixed.
+        bin_length = ctx_a85dec ((char*)audio->data,
+                                 (void*)data2,
+                                 bin_length);
+        free (audio->data);
+        audio->data = data2;
+        audio->data_size = bin_length;
+        }
+      }
+      break;
+
+      case 'b':
+      {
+        int bin_length = audio->data_size;
+        uint8_t *data2 = malloc (audio->data_size);
+        bin_length = ctx_base642bin ((char*)audio->data,
+                                     &bin_length,
+                                     data2);
+        memcpy (audio->data, data2, bin_length + 1);
+        audio->data_size = bin_length;
+        free (data2);
+      }
+      break;
+    }
+
+    if (audio->frames)
+    switch (audio->compression)
+    {
+      case 'z':
+    {
+      unsigned long actual_uncompressed_size = audio->frames * audio->bits/8 * audio->channels + 512;
+      unsigned char *data2 = malloc (actual_uncompressed_size);
+      /* if a buf size is set (rather compression, but
+       * this works first..) then */
+      int z_result = uncompress (data2, &actual_uncompressed_size,
+                                 audio->data,
+                                 audio->data_size);
+      if (z_result != Z_OK)
+      {
+       // fprintf (stderr, "[z error %i %i]", __LINE__, z_result);
+      }
+
+#if 0
+      // XXX : we seem to get buf-error (-5) here, which indicates not enough
+      //       space in output buffer, which is odd
+      //
+      //       it is non fatal though so we ignore it and use the validly
+      //       decompressed bits.
+      {
+        char buf[256];
+        sprintf (buf, "\e_Ao=z;zlib error1 %i\e\\", z_result);
+        vt_write (vt, buf, strlen(buf));
+        //goto cleanup;
+      }
 #endif
-}
+      free (audio->data);
+      audio->data = data2;
+      audio->data_size = actual_uncompressed_size;
+    }
 
-void
-ctx_font (Ctx *ctx, const char *family_name)
-{
-  // should also parse size
-  ctx_font_family (ctx, family_name);
-}
+        break;
+      case 'o':
+        break;
+      default:
+        break;
+    }
 
-const char *
-ctx_get_font (Ctx *ctx)
-{
-  return ctx_fonts[ctx->state.gstate.font].name;
-}
+    if (audio->frames == 0)
+    {
+      /* implicit frame count */
+      audio->frames = audio->data_size /
+                                (audio->bits/8) /
+                                   audio->channels;
+    }
 
-void ctx_line_to (Ctx *ctx, float x, float y)
-{
-  if (!ctx->state.has_moved)
-    { CTX_PROCESS_F (CTX_MOVE_TO, x, y); }
-  else
-    { CTX_PROCESS_F (CTX_LINE_TO, x, y); }
-}
 
-void ctx_move_to (Ctx *ctx, float x, float y)
-{
-  CTX_PROCESS_F (CTX_MOVE_TO,x,y);
-}
+#if 0
+    if (audio->format == 100/* opus */)
+    {
+      int channels;
+      uint8_t *new_data = NULL;//stbi_load_from_memory (audio->data, audio->data_size, &audio->buf_width, 
&audio->buf_height, &channels, 4);
 
-void ctx_curve_to (Ctx *ctx, float x0, float y0,
-                   float x1, float y1,
-                   float x2, float y2)
-{
-  CtxEntry command[3]=
+      if (!new_data)
+      {
+        char buf[256]= "\e_Gf=100;audio decode error\e\\";
+        vt_write (vt, buf, strlen(buf));
+        goto cleanup;
+      }
+      audio->format = 32;
+      free (audio->data);
+      audio->data = new_data;
+      audio->data_size = audio->buf_width * audio->buf_height * 4;
+    }
+#endif
+
+  switch (audio->action)
   {
-    ctx_f (CTX_CURVE_TO, x0, y0),
-    ctx_f (CTX_CONT,     x1, y1),
-    ctx_f (CTX_CONT,     x2, y2)
-  };
-  ctx_process (ctx, command);
-}
+    case 't': // transfer
+       if (audio->type == 'u') // implied 8bit
+       {
+         if (audio->channels == 2)
+         {
+           for (int i = 0; i < audio->frames; i++)
+           {
+             int val_left = MuLawDecompressTable[audio->data[i*2]];
+             int val_right = MuLawDecompressTable[audio->data[i*2+1]];
+             terminal_queue_pcm (val_left, val_right);
+           }
+         }
+         else
+         {
+           for (int i = 0; i < audio->frames; i++)
+           {
+             int val = MuLawDecompressTable[audio->data[i]];
+             terminal_queue_pcm (val, val);
+           }
+         }
+       }
+       else if (audio->type == 's')
+       {
+         if (audio->bits == 8)
+         {
+           if (audio->channels == 2)
+           {
+             for (int i = 0; i < audio->frames; i++)
+             {
+               int val_left = 256*((int8_t*)(audio->data))[i*2];
+               int val_right = 256*((int8_t*)(audio->data))[i*2+1];
+               terminal_queue_pcm (val_left, val_right);
+             }
+           }
+           else
+           {
+             for (int i = 0; i < audio->frames; i++)
+             {
+               int val = 256*((int8_t*)(audio->data))[i];
+               terminal_queue_pcm (val, val);
+             }
+           }
+         }
+         else
+         {
+           if (audio->channels == 2)
+           {
+             for (int i = 0; i < audio->frames; i++)
+             {
+               int val_left = ((int16_t*)(audio->data))[i*2];
+               int val_right = ((int16_t*)(audio->data))[i*2+1];
+               terminal_queue_pcm (val_left, val_right);
+             }
+           }
+           else
+           {
+             for (int i = 0; i < audio->frames; i++)
+             {
+               int val = ((int16_t*)(audio->data))[i];
+               terminal_queue_pcm (val, val);
+             }
+           }
+         }
+       }
+       free (audio->data);
+       audio->data = NULL;
+       audio->data_size=0;
+       break;
+    case 'q': // query
+       {
+         char buf[512];
+         sprintf (buf, "\033_As=%i,b=%i,c=%i,T=%c,B=%i,e=%c,o=%c;OK\033\\",
+      audio->samplerate, audio->bits, audio->channels, audio->type,
+      audio->buffer_size,
+      audio->encoding?audio->encoding:'0',
+      audio->compression?audio->compression:'0'
+      /*audio->transmission*/);
+
+         vt_write (vt, buf, strlen(buf));
+       }
+      break;
+  }
+  }
 
-void ctx_round_rectangle (Ctx *ctx,
-                          float x0, float y0,
-                          float w, float h,
-                          float radius)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_ROUND_RECTANGLE, x0, y0),
-    ctx_f (CTX_CONT,            w, h),
-    ctx_f (CTX_CONT,            radius, 0)
-  };
-  ctx_process (ctx, command);
+//cleanup:
+    if (audio->data)
+      free (audio->data);
+    audio->data = NULL;
+    audio->data_size=0;
 }
+#endif
+/* DEC terminals/xterm family terminal with ANSI, utf8, vector graphics and
+ * audio.
+ *
+ * Copyright (c) 2014, 2016, 2018, 2020 Øyvind Kolås <pippin gimp org>
+ *
+ * Adhering to the standards with modern extensions.
+ *
+ * Features:
+ *     vt100 - 101 points on scoresheet
+ *     UTF8, cp437
+ *     dim, bold, strikethrough, underline, italic, reverse
+ *     ANSI colors, 256 colors (non-redefineable), 24bit color
+ *     realtime audio transmission
+ *     raster sprites (kitty spec)
+ *     vector graphics
+ *     vt320 - horizontal margins
+ *     proportional fonts
+ *
+ *     BBS/ANSI-art mode
+ *
+ * 8bit clean
+ *
+ *
+ * Todo:
+ *     DECCIR - cursor state report https://vt100.net/docs/vt510-rm/DECCIR.html
+ *     make absolute positioning take proportional into account
+ *     HTML / PNG / SVG / PDF export of scrollback / screen
+ *     sixels
+ *
+ */
 
-void ctx_view_box (Ctx *ctx,
-                   float x0, float y0,
-                   float w, float h)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_VIEW_BOX, x0, y0),
-    ctx_f (CTX_CONT,     w, h)
-  };
-  ctx_process (ctx, command);
-}
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 // for posix_openpt
+#endif
 
-void ctx_rectangle (Ctx *ctx,
-                    float x0, float y0,
-                    float w, float h)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_RECTANGLE, x0, y0),
-    ctx_f (CTX_CONT,      w, h)
-  };
-  ctx_process (ctx, command);
-}
+#if !__COSMOPOLITAN__
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <assert.h>
 
-void ctx_rel_line_to (Ctx *ctx, float x, float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CTX_PROCESS_F (CTX_REL_LINE_TO,x,y);
-}
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <zlib.h>
+#endif
 
-void ctx_rel_move_to (Ctx *ctx, float x, float y)
-{
-  if (!ctx->state.has_moved)
-    {
-      CTX_PROCESS_F (CTX_MOVE_TO,x,y);
-      return;
-    }
-  CTX_PROCESS_F (CTX_REL_MOVE_TO,x,y);
-}
+#include "ctx.h"
 
-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;
-}
+#define CTX_VT_USE_FRAMEDIFF 0  // is a larger drain than neccesary when everything is per-byte?
+                                // is anyways currently disabled also in ctx
 
-CtxBlend ctx_get_blend_mode (Ctx *ctx)
-{
-  return ctx->state.gstate.blend_mode;
-}
+//#define STB_IMAGE_IMPLEMENTATION
+//#include "stb_image.h"
 
-CtxTextAlign ctx_get_text_align  (Ctx *ctx)
-{
-  return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_text_align);
-}
+//#include "vt-line.h"
+//#include "vt.h"
+//#include "ctx-clients.h"
 
-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;
-}
+#define VT_LOG_INFO     (1<<0)
+#define VT_LOG_CURSOR   (1<<1)
+#define VT_LOG_COMMAND  (1<<2)
+#define VT_LOG_WARNING  (1<<3)
+#define VT_LOG_ERROR    (1<<4)
+#define VT_LOG_INPUT    (1<<5)
+#define VT_LOG_ALL       0xff
 
-void ctx_line_cap (Ctx *ctx, CtxLineCap cap)
-{
-  if (ctx->state.gstate.line_cap != cap)
-    CTX_PROCESS_U8 (CTX_LINE_CAP, cap);
-}
+static int vt_log_mask = VT_LOG_INPUT;
+//static int vt_log_mask = VT_LOG_WARNING | VT_LOG_ERROR;// | VT_LOG_COMMAND;// | VT_LOG_INFO | 
VT_LOG_COMMAND;
+//static int vt_log_mask = VT_LOG_WARNING | VT_LOG_ERROR | VT_LOG_INFO | VT_LOG_COMMAND | VT_LOG_INPUT;
+//static int vt_log_mask = VT_LOG_ALL;
 
-void ctx_fill_rule (Ctx *ctx, CtxFillRule fill_rule)
-{
-  if (ctx->state.gstate.fill_rule != fill_rule)
-    CTX_PROCESS_U8 (CTX_FILL_RULE, fill_rule);
-}
-void ctx_line_join (Ctx *ctx, CtxLineJoin join)
-{
-  if (ctx->state.gstate.line_join != join)
-    CTX_PROCESS_U8 (CTX_LINE_JOIN, join);
-}
-void ctx_blend_mode (Ctx *ctx, CtxBlend mode)
-{
-  if (ctx->state.gstate.blend_mode != mode)
-    CTX_PROCESS_U8 (CTX_BLEND_MODE, mode);
-}
-void ctx_compositing_mode (Ctx *ctx, CtxCompositingMode mode)
-{
-  if (ctx->state.gstate.compositing_mode != mode)
-    CTX_PROCESS_U8 (CTX_COMPOSITING_MODE, mode);
-}
-void ctx_text_align (Ctx *ctx, CtxTextAlign text_align)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_ALIGN, text_align);
-}
-void ctx_text_baseline (Ctx *ctx, CtxTextBaseline text_baseline)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_BASELINE, text_baseline);
-}
-void ctx_text_direction (Ctx *ctx, CtxTextDirection text_direction)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_DIRECTION, text_direction);
-}
+#if 0
+#define vt_log(domain, fmt, ...)
+
+#define VT_input(str, ...)
+#define VT_info(str, ...)
+#define VT_command(str, ...)
+#define VT_cursor(str, ...)
+#define VT_warning(str, ...)
+#define VT_error(str, ...)
+#else
+#define vt_log(domain, line, a...) \
+        do {fprintf (stderr, "%i %s ", line, domain);fprintf(stderr, ##a);fprintf(stderr, "\n");}while(0)
+#define VT_info(a...) if (vt_log_mask & VT_LOG_INFO) vt_log ("INFO  ", __LINE__, ##a)
+#define VT_input(a...) if (vt_log_mask & VT_LOG_INPUT) vt_log ("INPUT ", __LINE__, ##a)
+#define VT_command(a...) if (vt_log_mask & VT_LOG_COMMAND) vt_log ("CMD   ", __LINE__, ##a)
+#define VT_cursor(a...) if (vt_log_mask & VT_LOG_CURSOR) vt_log ("CURSOR",__LINE__, ##a)
+#define VT_warning(a...) if (vt_log_mask & VT_LOG_WARNING) vt_log ("WARN  ",__LINE__, ##a)
+#define VT_error(a...) if (vt_log_mask & VT_LOG_ERROR) vt_log ("ERROR",__LINE__, ##a)
 
-void
-ctx_rel_curve_to (Ctx *ctx,
-                  float x0, float y0,
-                  float x1, float y1,
-                  float x2, float y2)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_REL_CURVE_TO, x0, y0),
-    ctx_f (CTX_CONT, x1, y1),
-    ctx_f (CTX_CONT, x2, y2)
-  };
-  ctx_process (ctx, command);
-}
+#endif
 
-void
-ctx_rel_quad_to (Ctx *ctx,
-                 float cx, float cy,
-                 float x,  float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_REL_QUAD_TO, cx, cy),
-    ctx_f (CTX_CONT, x, y)
-  };
-  ctx_process (ctx, command);
-}
+#ifndef MIN
+#define MIN(a,b)  ((a)<(b)?(a):(b))
+#endif
 
-void
-ctx_quad_to (Ctx *ctx,
-             float cx, float cy,
-             float x,  float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_QUAD_TO, cx, cy),
-    ctx_f (CTX_CONT, x, y)
-  };
-  ctx_process (ctx, command);
-}
+static void vt_state_neutral      (VT *vt, int byte);
+static void vt_state_esc          (VT *vt, int byte);
+static void vt_state_osc          (VT *vt, int byte);
+static void vt_state_apc          (VT *vt, int byte);
+static void vt_state_apc_generic  (VT *vt, int byte);
+static void vt_state_sixel        (VT *vt, int byte);
+static void vt_state_esc_sequence (VT *vt, int byte);
+static void vt_state_esc_foo      (VT *vt, int byte);
+static void vt_state_swallow      (VT *vt, int byte);
+static void vt_state_ctx          (VT *vt, int byte);
+static void vt_state_vt52         (VT *vt, int byte);
 
-void ctx_arc (Ctx  *ctx,
-              float x0, float y0,
-              float radius,
-              float angle1, float angle2,
-              int   direction)
+#if 0
+/* barebones linked list */
+
+typedef struct _CtxList CtxList;
+struct _CtxList
 {
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_ARC, x0, y0),
-    ctx_f (CTX_CONT, radius, angle1),
-    ctx_f (CTX_CONT, angle2, direction)
-  };
-  ctx_process (ctx, command);
-}
+  void *data;
+  CtxList *next;
+};
 
-static int ctx_coords_equal (float x1, float y1, float x2, float y2, float tol)
+static inline int ctx_list_length (CtxList *list)
 {
-  float dx = x2 - x1;
-  float dy = y2 - y1;
-  return dx*dx + dy*dy < tol*tol;
+  int length = 0;
+  for (CtxList *l = list; l; l = l->next, length++);
+  return length;
 }
 
-static float
-ctx_point_seg_dist_sq (float x, float y,
-                       float vx, float vy, float wx, float wy)
+static inline void ctx_list_prepend (CtxList **list, void *data)
 {
-  float l2 = ctx_pow2 (vx-wx) + ctx_pow2 (vy-wy);
-  if (l2 < 0.0001)
-    { return ctx_pow2 (x-vx) + ctx_pow2 (y-vy); }
-  float t = ( (x - vx) * (wx - vx) + (y - vy) * (wy - vy) ) / l2;
-  t = ctx_maxf (0, ctx_minf (1, t) );
-  float ix = vx + t * (wx - vx);
-  float iy = vy + t * (wy - vy);
-  return ctx_pow2 (x-ix) + ctx_pow2 (y-iy);
+  CtxList *new_=calloc (sizeof (CtxList), 1);
+  new_->next = *list;
+  new_->data = data;
+  *list = new_;
 }
 
-static void
-ctx_normalize (float *x, float *y)
+static inline void *ctx_list_last (CtxList *list)
 {
-  float length = ctx_hypotf ( (*x), (*y) );
-  if (length > 1e-6f)
+  if (list)
     {
-      float r = 1.0f / length;
-      *x *= r;
-      *y *= r;
+      CtxList *last;
+      for (last = list; last->next; last=last->next);
+      return last->data;
     }
+  return NULL;
 }
 
-void
-ctx_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
+static inline void ctx_list_append (CtxList **list, void *data)
 {
-  // XXX : should partially move into rasterizer to preserve comand
-  //       even if an arc preserves all geometry, just to ensure roundtripping
-  //       of data
-  /* from nanovg - but not quite working ; uncertain if arc or wrong
-   * transfusion is the cause.
-   */
-  float x0 = ctx->state.x;
-  float y0 = ctx->state.y;
-  float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
-  int dir;
-  if (!ctx->state.has_moved)
-    { return; }
-  if (1)
-    {
-      // Handle degenerate cases.
-      if (ctx_coords_equal (x0,y0, x1,y1, 0.5f) ||
-          ctx_coords_equal (x1,y1, x2,y2, 0.5f) ||
-          ctx_point_seg_dist_sq (x1,y1, x0,y0, x2,y2) < 0.5 ||
-          radius < 0.5)
-        {
-          ctx_line_to (ctx, x1,y1);
-          return;
-        }
-    }
-  // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
-  dx0 = x0-x1;
-  dy0 = y0-y1;
-  dx1 = x2-x1;
-  dy1 = y2-y1;
-  ctx_normalize (&dx0,&dy0);
-  ctx_normalize (&dx1,&dy1);
-  a = ctx_acosf (dx0*dx1 + dy0*dy1);
-  d = radius / ctx_tanf (a/2.0f);
-#if 0
-  if (d > 10000.0f)
+  CtxList *new_= calloc (sizeof (CtxList), 1);
+  new_->data=data;
+  if (*list)
     {
-      ctx_line_to (ctx, x1, y1);
+      CtxList *last;
+      for (last = *list; last->next; last=last->next);
+      last->next = new_;
       return;
     }
-#endif
-  if ( (dx1*dy0 - dx0*dy1) > 0.0f)
-    {
-      cx = x1 + dx0*d + dy0*radius;
-      cy = y1 + dy0*d + -dx0*radius;
-      a0 = ctx_atan2f (dx0, -dy0);
-      a1 = ctx_atan2f (-dx1, dy1);
-      dir = 0;
-    }
-  else
-    {
-      cx = x1 + dx0*d + -dy0*radius;
-      cy = y1 + dy0*d + dx0*radius;
-      a0 = ctx_atan2f (-dx0, dy0);
-      a1 = ctx_atan2f (dx1, -dy1);
-      dir = 1;
-    }
-  ctx_arc (ctx, cx, cy, radius, a0, a1, dir);
-}
-
-void
-ctx_rel_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
-{
-  x1 += ctx->state.x;
-  y1 += ctx->state.y;
-  x2 += ctx->state.x;
-  y2 += ctx->state.y;
-  ctx_arc_to (ctx, x1, y1, x2, y2, radius);
-}
-
-void
-ctx_exit (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_EXIT);
+  *list = new_;
+  return;
 }
 
-void
-ctx_flush (Ctx *ctx)
+static inline void ctx_list_remove (CtxList **list, void *data)
 {
-  /* XXX: should be fully moved into the renderers
-   *      to permit different behavior and get rid
-   *      of the extranous flush() vfunc.
-   */
-  ctx->rev++;
-//  CTX_PROCESS_VOID (CTX_FLUSH);
-#if 0
-  //printf (" \e[?2222h");
-  ctx_drawlist_compact (&ctx->drawlist);
-  for (int i = 0; i < ctx->drawlist.count - 1; i++)
+  CtxList *iter, *prev = NULL;
+  if ( (*list)->data == data)
     {
-      CtxEntry *entry = &ctx->drawlist.entries[i];
-      fwrite (entry, 9, 1, stdout);
-#if 0
-      uint8_t  *buf = (void *) entry;
-      for (int j = 0; j < 9; j++)
-        { printf ("%c", buf[j]); }
-#endif
+      prev = (void *) (*list)->next;
+      free (*list);
+      *list = prev;
+      return;
     }
-  printf ("Xx.Xx.Xx.");
-  fflush (NULL);
-#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);
+  for (iter = *list; iter; iter = iter->next)
+    if (iter->data == data)
+      {
+        prev->next = iter->next;
+        free (iter);
+        break;
+      }
+    else
+      { prev = iter; }
 }
 
-////////////////////////////////////////
-
-void
-ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
+static inline void
+ctx_list_insert_before (CtxList **list, CtxList *sibling,
+                       void *data)
 {
-  CtxCommand *c = (CtxCommand *) entry;
-  switch (entry->code)
+  if (*list == NULL || *list == sibling)
     {
-      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;
-#if CTX_ENABLE_SHADOW_BLUR
-      case CTX_SHADOW_BLUR:
-        state->gstate.shadow_blur = ctx_arg_float (0);
-        break;
-      case CTX_SHADOW_OFFSET_X:
-        state->gstate.shadow_offset_x = ctx_arg_float (0);
-        break;
-      case CTX_SHADOW_OFFSET_Y:
-        state->gstate.shadow_offset_y = ctx_arg_float (0);
-        break;
-#endif
-      case CTX_LINE_CAP:
-        state->gstate.line_cap = (CtxLineCap) ctx_arg_u8 (0);
-        break;
-      case CTX_FILL_RULE:
-        state->gstate.fill_rule = (CtxFillRule) ctx_arg_u8 (0);
-        break;
-      case CTX_LINE_JOIN:
-        state->gstate.line_join = (CtxLineJoin) ctx_arg_u8 (0);
-        break;
-      case CTX_COMPOSITING_MODE:
-        state->gstate.compositing_mode = (CtxCompositingMode) ctx_arg_u8 (0);
-        break;
-      case CTX_BLEND_MODE:
-        state->gstate.blend_mode = (CtxBlend) ctx_arg_u8 (0);
-        break;
-      case CTX_TEXT_ALIGN:
-        ctx_state_set (state, CTX_text_align, ctx_arg_u8 (0) );
-        break;
-      case CTX_TEXT_BASELINE:
-        ctx_state_set (state, CTX_text_baseline, ctx_arg_u8 (0) );
-        break;
-      case CTX_TEXT_DIRECTION:
-        ctx_state_set (state, CTX_text_direction, ctx_arg_u8 (0) );
-        break;
-      case CTX_GLOBAL_ALPHA:
-        state->gstate.global_alpha_u8 = ctx_float_to_u8 (ctx_arg_float (0) );
-        state->gstate.global_alpha_f = ctx_arg_float (0);
-        break;
-      case CTX_FONT_SIZE:
-        state->gstate.font_size = ctx_arg_float (0);
-        break;
-      case CTX_MITER_LIMIT:
-        state->gstate.miter_limit = ctx_arg_float (0);
-        break;
-      case CTX_COLOR_SPACE:
-        /* move this out of this function and only do it in rasterizer? XXX */
-        ctx_rasterizer_colorspace_icc (state, (CtxColorSpace)c->colorspace.space_slot,
-                                              (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_STROKE_SOURCE:
-        state->source = 1;
-        break;
-
-      case CTX_COLOR:
+      ctx_list_prepend (list, data);
+    }
+  else
+    {
+      CtxList *prev = NULL;
+      for (CtxList *l = *list; l; l=l->next)
         {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = 0;
-
-          source->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) // XXX remove 511 after stroke source is complete
-            {
-              case CTX_RGB:
-                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
-                break;
-              case CTX_RGBA:
-                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-              case CTX_DRGBA:
-                ctx_color_set_drgba (state, &source->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, &source->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, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
-                break;
-              case CTX_DCMYKA:
-                ctx_color_set_dcmyka (state, &source->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, &source->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, &source->color, c->graya.g, c->graya.a);
-                break;
-              case CTX_GRAY:
-                ctx_color_set_graya (state, &source->color, c->graya.g, 1.0f);
-                break;
-            }
+          if (l == sibling)
+            { break; }
+          prev = l;
         }
-        break;
-      case CTX_SET_RGBA_U8:
-        //ctx_source_deinit (&state->gstate.source);
-        //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+      if (prev)
         {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = 0;
+          CtxList *new_=calloc (sizeof (CtxList), 1);
+          new_->next = sibling;
+          new_->data = data;
+          prev->next=new_;
+        }
+    }
+}
+#endif
 
-          source->type = CTX_SOURCE_COLOR;
 
-          ctx_color_set_RGBA8 (state, &source->color,
-                               ctx_arg_u8 (0),
-                               ctx_arg_u8 (1),
-                               ctx_arg_u8 (2),
-                               ctx_arg_u8 (3) );
-        }
-        //for (int i = 0; i < 4; i ++)
-        //  state->gstate.source.color.rgba[i] = ctx_arg_u8(i);
-        break;
-      //case CTX_TEXTURE:
-      //  state->gstate.source.type = CTX_SOURCE_
-      //  break;
-      case CTX_LINEAR_GRADIENT:
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = is_stroke ? 2 : 0;
+typedef enum
+{
+  STYLE_REVERSE         = 1 << 0,
+  STYLE_BOLD            = 1 << 1,
+  STYLE_BLINK           = 1 << 2,
+  STYLE_UNDERLINE       = 1 << 3,
+  STYLE_DIM             = 1 << 4,
+  STYLE_HIDDEN          = 1 << 5,
+  STYLE_ITALIC          = 1 << 6,
+  STYLE_UNDERLINE_VAR   = 1 << 7,
+  STYLE_STRIKETHROUGH   = 1 << 8,
+  STYLE_OVERLINE        = 1 << 9,
+  STYLE_BLINK_FAST      = 1 << 10,
+  STYLE_PROPORTIONAL    = 1 << 11,
+  STYLE_FG_COLOR_SET    = 1 << 12,
+  STYLE_BG_COLOR_SET    = 1 << 13,
+  STYLE_FG24_COLOR_SET  = 1 << 14,
+  STYLE_BG24_COLOR_SET  = 1 << 15,
+  //STYLE_NONERASABLE     = 1 << 16  // needed for selective erase
+} TerminalStyle;
+
+typedef struct Image
+{
+  int kitty_format;
+  int width;
+  int height;
+  int id;
+  int eid_no;
+  int size;
+  uint8_t *data;
+} Image;
 
-          float x0 = ctx_arg_float (0);
-          float y0 = ctx_arg_float (1);
-          float x1 = ctx_arg_float (2);
-          float y1 = ctx_arg_float (3);
-          float dx, dy, length, start, end;
+#define MAX_IMAGES 128
 
-          length = ctx_hypotf (x1-x0,y1-y0);
-          dx = (x1-x0) / length;
-          dy = (y1-y0) / length;
-          start = (x0 * dx + y0 * dy) / length;
-          end =   (x1 * dx + y1 * dy) / length;
-          source->linear_gradient.length = length;
-          source->linear_gradient.dx = dx;
-          source->linear_gradient.dy = dy;
-          source->linear_gradient.start = start;
-          source->linear_gradient.end = end;
-          source->linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
-          source->type = CTX_SOURCE_LINEAR_GRADIENT;
-          source->transform = state->gstate.transform;
-          ctx_matrix_invert (&source->transform);
-        }
-        break;
-      case CTX_RADIAL_GRADIENT:
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = is_stroke ? 2 : 0;
+static Image image_db[MAX_IMAGES]= {{0,},};
 
-          float x0 = ctx_arg_float (0);
-          float y0 = ctx_arg_float (1);
-          float r0 = ctx_arg_float (2);
-          float x1 = ctx_arg_float (3);
-          float y1 = ctx_arg_float (4);
-          float r1 = ctx_arg_float (5);
-          source->radial_gradient.x0 = x0;
-          source->radial_gradient.y0 = y0;
-          source->radial_gradient.r0 = r0;
-          source->radial_gradient.x1 = x1;
-          source->radial_gradient.y1 = y1;
-          source->radial_gradient.r1 = r1;
-          source->radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
-          source->type      = CTX_SOURCE_RADIAL_GRADIENT;
-          source->transform = state->gstate.transform;
-          ctx_matrix_invert (&source->transform);
-        }
-        break;
+static Image *image_query (int id)
+{
+  for (int i = 0; i < MAX_IMAGES; i++)
+    {
+      Image *image = &image_db[i];
+      if (image->id == id)
+        { return image; }
     }
+  return NULL;
 }
 
-void
-ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data)
+static int image_eid_no = 0;
+
+static Image *image_add (int width,
+                         int height,
+                         int id,
+                         int format,
+                         int size,
+                         uint8_t *data)
 {
-  switch (entry->code)
+  // look for id if id is not 0
+  Image *image;
+  for (int i = 0; i < MAX_IMAGES; i++)
     {
-      case CTX_SAVE:
-        ctx_gstate_push (state);
-        break;
-      case CTX_RESTORE:
-        ctx_gstate_pop (state);
-        break;
-      case CTX_IDENTITY:
-        ctx_matrix_identity (&state->gstate.transform);
-        break;
-      case CTX_TRANSLATE:
-        ctx_matrix_translate (&state->gstate.transform,
-                              ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_SCALE:
-        ctx_matrix_scale (&state->gstate.transform,
-                          ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_ROTATE:
-        ctx_matrix_rotate (&state->gstate.transform, ctx_arg_float (0) );
-        break;
-      case CTX_APPLY_TRANSFORM:
-        {
-          CtxMatrix m;
-          ctx_matrix_set (&m,
-                          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_matrix_multiply (&state->gstate.transform,
-                               &state->gstate.transform, &m); // XXX verify order
-        }
-#if 0
-        ctx_matrix_set (&state->gstate.transform,
-                        ctx_arg_float (0), ctx_arg_float (1),
-                        ctx_arg_float (2), ctx_arg_float (3),
-                        ctx_arg_float (4), ctx_arg_float (5) );
-#endif
-        break;
+      image = &image_db[i];
+      if (image->data == NULL)
+        { break; }
+    }
+  if (image->data)
+    {
+      // not a good eviction strategy
+      image = &image_db[random() %MAX_IMAGES];
     }
+  if (image->data)
+    { free (image->data); }
+  image->kitty_format = format;
+  image->width  = width;
+  image->height = height;
+  image->id     = id;
+  image->size   = size;
+  image->data   = data;
+  image->eid_no = image_eid_no++;
+  return image;
 }
 
-/*
- * this transforms the contents of entry according to ctx->transformation
- */
-void
-ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data)
+
+
+void vtpty_resize (void *data, int cols, int rows, int px_width, int px_height)
 {
-  CtxCommand *c = (CtxCommand *) entry;
-  float start_x = state->x;
-  float start_y = state->y;
-  int had_moved = state->has_moved;
-  switch (entry->code)
+  VtPty *vtpty = data;
+  struct winsize ws;
+  ws.ws_row = rows;
+  ws.ws_col = cols;
+  ws.ws_xpixel = px_width;
+  ws.ws_ypixel = px_height;
+  ioctl (vtpty->pty, TIOCSWINSZ, &ws);
+}
+
+ssize_t vtpty_write (void *data, const void *buf, size_t count)
+{
+  VtPty *vtpty = data;
+  return write (vtpty->pty, buf, count);
+}
+
+ssize_t vtpty_read (void  *data, void *buf, size_t count)
+{
+  VtPty *vtpty = data;
+  return read (vtpty->pty, buf, count);
+}
+
+int vtpty_waitdata (void  *data, int timeout)
+{
+  VtPty *vtpty = data;
+  struct timeval tv;
+  fd_set fdset;
+  FD_ZERO (&fdset);
+  FD_SET (vtpty->pty, &fdset);
+  tv.tv_sec = 0;
+  tv.tv_usec = timeout;
+  tv.tv_sec  = timeout / 1000000;
+  tv.tv_usec = timeout % 1000000;
+  if (select (vtpty->pty+1, &fdset, NULL, NULL, &tv) == -1)
     {
-      case CTX_MOVE_TO:
-      case CTX_LINE_TO:
+      perror ("select");
+      return 0;
+    }
+  if (FD_ISSET (vtpty->pty, &fdset) )
+    {
+      return 1;
+    }
+  return 0;
+}
+
+
+/* on current line */
+static int vt_col_to_pos (VT *vt, int col)
+{
+  int pos = col;
+  if (vt->current_line->contains_proportional)
+    {
+      Ctx *ctx = ctx_new ();
+      ctx_font (ctx, "regular");
+      ctx_font_size (ctx, vt->font_size);
+      int x = 0;
+      pos = 0;
+      int prev_prop = 0;
+      while (x <= col * vt->cw)
         {
-          float x = c->c.x0;
-          float y = c->c.y0;
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          if (vt_line_get_style (vt->current_line, pos) & STYLE_PROPORTIONAL)
             {
-              _ctx_user_to_device (state, &x, &y);
-              ctx_arg_float (0) = x;
-              ctx_arg_float (1) = y;
+              x += ctx_glyph_width (ctx, vt_line_get_unichar (vt->current_line, pos) );
+              prev_prop = 1;
             }
-        }
-        break;
-      case CTX_ARC:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            float temp;
-            _ctx_user_to_device (state, &c->arc.x, &c->arc.y);
-            temp = 0;
-            _ctx_user_to_device_distance (state, &c->arc.radius, &temp);
-          }
-        break;
-      case CTX_LINEAR_GRADIENT:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-        {
-        _ctx_user_to_device (state, &c->linear_gradient.x1, &c->linear_gradient.y1);
-        _ctx_user_to_device (state, &c->linear_gradient.x2, &c->linear_gradient.y2);
-        }
-        break;
-      case CTX_RADIAL_GRADIENT:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-        {
-          float temp;
-          _ctx_user_to_device (state, &c->radial_gradient.x1, &c->radial_gradient.y1);
-          temp = 0;
-          _ctx_user_to_device_distance (state, &c->radial_gradient.r1, &temp);
-          _ctx_user_to_device (state, &c->radial_gradient.x2, &c->radial_gradient.y2);
-          temp = 0;
-          _ctx_user_to_device_distance (state, &c->radial_gradient.r2, &temp);
-        }
-        break;
-      case CTX_CURVE_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 3; c ++)
-              {
-                float x = entry[c].data.f[0];
-                float y = entry[c].data.f[1];
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-          }
-        break;
-      case CTX_QUAD_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 2; c ++)
-              {
-                float x = entry[c].data.f[0];
-                float y = entry[c].data.f[1];
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-          }
-        break;
-      case CTX_REL_MOVE_TO:
-      case CTX_REL_LINE_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 1; c ++)
-              {
-                float x = state->x;
-                float y = state->y;
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-            if (entry->code == CTX_REL_MOVE_TO)
-              { entry->code = CTX_MOVE_TO; }
-            else
-              { entry->code = CTX_LINE_TO; }
-          }
-        break;
-      case CTX_REL_CURVE_TO:
-        {
-          float nx = state->x + ctx_arg_float (4);
-          float ny = state->y + ctx_arg_float (5);
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          else
             {
-              for (int c = 0; c < 3; c ++)
+              if (prev_prop)
                 {
-                  float x = nx + entry[c].data.f[0];
-                  float y = ny + entry[c].data.f[1];
-                  _ctx_user_to_device (state, &x, &y);
-                  entry[c].data.f[0] = x;
-                  entry[c].data.f[1] = y;
+                  int new_cw = vt->cw - ( (x % vt->cw) );
+                  if (new_cw < vt->cw*3/2)
+                    { new_cw += vt->cw; }
+                  x += new_cw;
                 }
-              entry->code = CTX_CURVE_TO;
-            }
-        }
-        break;
-      case CTX_REL_QUAD_TO:
-        {
-          float nx = state->x + ctx_arg_float (2);
-          float ny = state->y + ctx_arg_float (3);
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-            {
-              for (int c = 0; c < 2; c ++)
+              else
                 {
-                  float x = nx + entry[c].data.f[0];
-                  float y = ny + entry[c].data.f[1];
-                  _ctx_user_to_device (state, &x, &y);
-                  entry[c].data.f[0] = x;
-                  entry[c].data.f[1] = y;
+                  x += vt->cw;
                 }
-              entry->code = CTX_QUAD_TO;
+              prev_prop = 0;
             }
+          pos ++;
         }
-        break;
+      pos --;
+      ctx_free (ctx);
     }
-  if ((((Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE))
+  return pos;
+}
+
+static int vt_margin_left (VT *vt)
+{
+  int left = vt->left_right_margin_mode?vt->margin_left:1;
+  return vt_col_to_pos (vt, left);
+}
+
+#define VT_MARGIN_LEFT vt_margin_left(vt)
+
+static int vt_margin_right (VT *vt)
+{
+  int right = vt->left_right_margin_mode?vt->margin_right:vt->cols;
+  return vt_col_to_pos (vt, right);
+}
+
+#define VT_MARGIN_RIGHT vt_margin_right(vt)
+
+
+void vt_rev_inc (VT *vt)
+{
+  if (vt)
+    vt->rev++;
+}
+
+long vt_rev (VT *vt)
+{
+  return vt?vt->rev:0;
+}
+
+static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence);
+int vt_set_prop (VT *vt, uint32_t key_hash, const char *val);
+uint64_t ctx_strhash (const char *utf8);
+
+static void vt_set_title (VT *vt, const char *new_title)
+{
+  if (vt->inert) return;
+
+  if (vt->title)
+    { free (vt->title); }
+  vt->title = strdup (new_title);
+  vt_set_prop (vt, ctx_strhash ("title"), (char*)new_title);
+}
+
+const char *vt_get_title (VT *vt)
+{
+  return vt->title;
+}
+
+static void vt_run_command (VT *vt, const char *command, const char *term);
+static void vtcmd_set_top_and_bottom_margins (VT *vt, const char *sequence);
+static void vtcmd_set_left_and_right_margins (VT *vt, const char *sequence);
+static void _vt_move_to (VT *vt, int y, int x);
+
+static void vtcmd_clear (VT *vt, const char *sequence)
+{
+  while (vt->lines)
     {
-      int components = 0;
-      _ctx_user_to_device (state, &start_x, &start_y);
-      switch (entry->code)
+      vt_line_free (vt->lines->data, 1);
+      ctx_list_remove (&vt->lines, vt->lines->data);
+    }
+  vt->lines = NULL;
+  vt->line_count = 0;
+
+  if (1)
+  { /* TODO: detect if this is neccesary.. due to images present
+             in lines in scrollback */
+    for (int i=0; i<vt->rows; i++)
+    {
+      vt->current_line = vt_line_new_with_size ("", vt->cols);
+      ctx_list_prepend (&vt->scrollback, vt->current_line);
+      vt->scrollback_count++;
+    }
+  }
+
+  /* populate lines */
+  for (int i=0; i<vt->rows; i++)
+    {
+      vt->current_line = vt_line_new_with_size ("", vt->cols);
+      ctx_list_prepend (&vt->lines, vt->current_line);
+      vt->line_count++;
+    }
+}
+
+#define set_fg_rgb(r, g, b) \
+    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<16));\
+    vt->cstyle |=  ((uint64_t)(r)<<16);\
+    vt->cstyle |=  ((uint64_t)(g)<<(16+8));\
+    vt->cstyle |=  ((uint64_t)(b)<<(16+8+8));\
+    vt->cstyle |= STYLE_FG_COLOR_SET;\
+    vt->cstyle |= STYLE_FG24_COLOR_SET;\
+
+#define set_bg_rgb(r, g, b) \
+    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<40));\
+    vt->cstyle |=  ((uint64_t)(r)<<40);\
+    vt->cstyle |=  ((uint64_t)(g)<<(40+8));\
+    vt->cstyle |=  ((uint64_t)(b)<<(40+8+8));\
+    vt->cstyle |= STYLE_BG_COLOR_SET;\
+    vt->cstyle |= STYLE_BG24_COLOR_SET;\
+
+#define set_fg_idx(idx) \
+    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<16));\
+    vt->cstyle ^= (vt->cstyle & STYLE_FG24_COLOR_SET);\
+    vt->cstyle |=  ((idx)<<16);\
+    vt->cstyle |= STYLE_FG_COLOR_SET;
+
+#define set_bg_idx(idx) \
+    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<40));\
+    vt->cstyle ^= (vt->cstyle & STYLE_BG24_COLOR_SET);\
+    vt->cstyle |= ((int64_t)(idx)<<40) ;\
+    vt->cstyle |= STYLE_BG_COLOR_SET;
+
+
+static void _vt_compute_cw_ch (VT *vt)
+{
+  vt->cw = (vt->font_size / vt->line_spacing * vt->scale_x) + 0.99;
+  vt->ch = vt->font_size;
+}
+
+static void vtcmd_set_132_col (VT  *vt, int set)
+{
+  // this should probably force the window as well
+  if (set == 0 && vt->scale_x == 1.0f) return;
+  if (set == 1 && vt->scale_x != 1.0f) return;
+  if (set) // 132 col
+    {
+      vt->scale_x = 74.0/132.0; // show all - po
+      //vt->scale_x = 80.0/132.0;
+      vt->scale_y = 1.0;
+      _vt_compute_cw_ch (vt);
+      vt_set_term_size (vt, vt->cols * 132/80.0, vt->rows);
+    }
+  else // 80 col
+    {
+      vt->scale_x = 1.0;
+      vt->scale_y = 1.0;
+      _vt_compute_cw_ch (vt);
+      vt_set_term_size (vt, vt->cols * 80/132.0, vt->rows);
+    }
+}
+
+static void vt_line_feed (VT *vt);
+static void vt_carriage_return (VT *vt);
+
+static int vt_trimlines (VT *vt, int max);
+static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence)
+{
+  VT_info ("reset %s", sequence);
+  if (getenv ("VT_DEBUG") )
+    { vt->debug = 1; }
+  vtcmd_clear (vt, sequence);
+  vt->encoding = 0;
+  vt->bracket_paste = 0;
+  vt->ctx_events = 0;
+  vt->cr_on_lf = 0;
+  vtcmd_set_top_and_bottom_margins (vt, "[r");
+  vtcmd_set_left_and_right_margins (vt, "[s");
+  vt->autowrap               = 1;
+  vt->justify                = 0;
+  vt->cursor_visible         = 1;
+  vt->charset[0]             = 0;
+  vt->charset[1]             = 0;
+  vt->charset[2]             = 0;
+  vt->charset[3]             = 0;
+  vt->bell                   = 3;
+  vt->scale_x                = 1.0;
+  vt->scale_y                = 1.0;
+  vt->saved_x                = 1;
+  vt->saved_y                = 1;
+  vt->saved_style            = 1;
+  vt->reverse_video          = 0;
+  vt->cstyle                 = 0;
+  vt->keyrepeat              = 1;
+  vt->cursor_key_application = 0;
+  vt->argument_buf_len       = 0;
+  vt->argument_buf[0]        = 0;
+  vt->vtpty.done             = 0;
+  vt->result                 = -1;
+  vt->state                  = vt_state_neutral;
+  vt->scroll_on_output       = 0;
+  vt->scroll_on_input        = 1;
+  vt->unit_pixels            = 0;
+  vt->mouse                  = 0;
+  vt->mouse_drag             = 0;
+  vt->mouse_all              = 0;
+  vt->mouse_decimal          = 0;
+  _vt_compute_cw_ch (vt);
+  for (int i = 0; i < MAX_COLS; i++)
+    { vt->tabs[i] = i % 8 == 0? 1 : 0; }
+  _vt_move_to (vt, vt->margin_top, vt->cursor_x);
+  vt_carriage_return (vt);
+  //if (vt->ctx)
+  //  { ctx_reset (vt->ctx); }
+  vt->audio.bits = 8;
+  vt->audio.channels = 1;
+  vt->audio.type = 'u';
+  vt->audio.samplerate = 8000;
+  vt->audio.buffer_size = 1024;
+  vt->audio.encoding = 'a';
+  vt->audio.compression = '0';
+  vt->audio.mic = 0;
+  while (vt->scrollback)
+    {
+      vt_line_free (vt->scrollback->data, 1);
+      ctx_list_remove (&vt->scrollback, vt->scrollback->data);
+    }
+  vt->scrollback_count = 0;
+}
+
+void vt_set_font_size (VT *vt, float font_size)
+{
+  vt->font_size = font_size;
+  _vt_compute_cw_ch (vt);
+}
+
+float       vt_get_font_size      (VT *vt)
+{
+  return vt->font_size;
+}
+
+void vt_set_line_spacing (VT *vt, float line_spacing)
+{
+  vt->line_spacing = line_spacing;
+  _vt_compute_cw_ch (vt);
+}
+
+VT *vt_new (const char *command, int width, int height, float font_size, float line_spacing, int id, int 
can_launch)
+{
+  VT *vt                 = calloc (sizeof (VT), 1);
+  vt->id                 = id;
+  vt->lastx              = -1;
+  vt->lasty              = -1;
+  vt->state              = vt_state_neutral;
+  vt->smooth_scroll      = 0;
+  vt->can_launch         = can_launch;
+  vt->scroll_offset      = 0.0;
+  vt->waitdata           = vtpty_waitdata;
+  vt->read               = vtpty_read;
+  vt->write              = vtpty_write;
+  vt->resize             = vtpty_resize;
+  vt->font_to_cell_scale = 0.98;
+  vt->cursor_visible     = 1;
+  vt->lines              = NULL;
+  vt->line_count         = 0;
+  vt->current_line       = NULL;
+  vt->cols               = 0;
+  vt->rows               = 0;
+
+  vt->scrollback_limit   = DEFAULT_SCROLLBACK;
+  vt->argument_buf_len   = 0;
+  vt->argument_buf_cap   = 64;
+  vt->argument_buf       = malloc (vt->argument_buf_cap);
+  vt->argument_buf[0]    = 0;
+  vt->vtpty.done         = 0;
+  vt->result             = -1;
+  vt->line_spacing       = 1.0;
+  vt->scale_x            = 1.0;
+  vt->scale_y            = 1.0;
+  vt_set_font_size (vt, font_size);
+  vt_set_line_spacing (vt, line_spacing);
+  if (command)
+    {
+      vt_run_command (vt, command, NULL);
+    }
+  if (width <= 0) width = 640;
+  if (height <= 0) width = 480;
+  vt_set_px_size (vt, width, height);
+
+  vt->fg_color[0] = 216;
+  vt->fg_color[1] = 216;
+  vt->fg_color[2] = 216;
+  vt->bg_color[0] = 0;
+  vt->bg_color[1] = 0;
+  vt->bg_color[2] = 0;
+  vtcmd_reset_to_initial_state (vt, NULL);
+  //vt->ctx = ctx_new ();
+  ctx_list_prepend (&vts, vt);
+  return vt;
+}
+
+int vt_cw (VT *vt)
+{
+  return vt->cw;
+}
+
+int vt_ch (VT *vt)
+{
+  return vt->ch;
+}
+
+static int vt_trimlines (VT *vt, int max)
+{
+  CtxList *chop_point = NULL;
+  CtxList *l;
+  int i;
+  if (vt->line_count < max)
+    { 
+      return 0;
+    }
+  for (l = vt->lines, i = 0; l && i < max-1; l = l->next, i++);
+  if (l)
+    {
+      chop_point = l->next;
+      l->next = NULL;
+    }
+  while (chop_point)
+    {
+      if (vt->in_alt_screen)
         {
-          case CTX_MOVE_TO:
-            if (had_moved) { components = 1; }
-            break;
-          case CTX_LINE_TO:
-            components = 1;
-            break;
-          case CTX_CURVE_TO:
-            components = 3;
-            break;
-          case CTX_QUAD_TO:
-            components = 2;
-            break;
+          vt_line_free (chop_point->data, 1);
         }
-      if (components)
+      else
         {
-          for (int c = 0; c < components; c++)
-            {
-              entry[c].data.f[0] -= start_x;
-              entry[c].data.f[1] -= start_y;
-            }
-          switch (entry->code)
-            {
-              case CTX_MOVE_TO:
-                entry[0].code = CTX_REL_MOVE_TO;
-                break;
-              case CTX_LINE_TO:
-                entry[0].code = CTX_REL_LINE_TO;
-                break;
-                break;
-              case CTX_CURVE_TO:
-                entry[0].code = CTX_REL_CURVE_TO;
-                break;
-              case CTX_QUAD_TO:
-                entry[0].code = CTX_REL_QUAD_TO;
-                break;
-            }
+          ctx_list_prepend (&vt->scrollback, chop_point->data);
+          vt->scrollback_count ++;
         }
+      ctx_list_remove (&chop_point, chop_point->data);
+      vt->line_count--;
     }
-}
-
-void
-ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data)
-{
-  switch (entry->code)
+  if (vt->scrollback_count > vt->scrollback_limit + 1024)
     {
-      case CTX_RESET:
-        ctx_state_init (state);
-        break;
-      case CTX_CLIP:
-      case CTX_BEGIN_PATH:
-      case CTX_FILL:
-      case CTX_STROKE:
-        state->has_moved = 0;
-        break;
-      case CTX_MOVE_TO:
-      case CTX_LINE_TO:
+      CtxList *l = vt->scrollback;
+      int no = 0;
+      while (l && no < vt->scrollback_limit)
         {
-          float x = ctx_arg_float (0);
-          float y = ctx_arg_float (1);
-          state->x = x;
-          state->y = y;
-          if (!state->has_moved)
-            {
-              state->has_moved = 1;
-            }
+          l = l->next;
+          no++;
+        }
+      chop_point = NULL;
+      if (l)
+        {
+          chop_point = l->next;
+          l->next = NULL;
+        }
+      while (chop_point)
+        {
+          vt_line_free (chop_point->data, 1);
+          ctx_list_remove (&chop_point, chop_point->data);
+          vt->scrollback_count --;
         }
-        break;
-      case CTX_CURVE_TO:
-        state->x = ctx_arg_float (4);
-        state->y = ctx_arg_float (5);
-        if (!state->has_moved)
-          {
-            state->has_moved = 1;
-          }
-        break;
-      case CTX_QUAD_TO:
-        state->x = ctx_arg_float (2);
-        state->y = ctx_arg_float (3);
-        if (!state->has_moved)
-          {
-            state->has_moved = 1;
-          }
-        break;
-      case CTX_ARC:
-        state->x = ctx_arg_float (0) + ctx_cosf (ctx_arg_float (4) ) * ctx_arg_float (2);
-        state->y = ctx_arg_float (1) + ctx_sinf (ctx_arg_float (4) ) * ctx_arg_float (2);
-        break;
-      case CTX_REL_MOVE_TO:
-      case CTX_REL_LINE_TO:
-        state->x += ctx_arg_float (0);
-        state->y += ctx_arg_float (1);
-        break;
-      case CTX_REL_CURVE_TO:
-        state->x += ctx_arg_float (4);
-        state->y += ctx_arg_float (5);
-        break;
-      case CTX_REL_QUAD_TO:
-        state->x += ctx_arg_float (2);
-        state->y += ctx_arg_float (3);
-        break;
-        // XXX missing some smooths
     }
+  return 0;
 }
 
-void
-ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data)
+static void vt_rewrap_pair (VT *vt, VtLine *topline, VtLine *bottomline, int max_col)
 {
-  if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ||
-       ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE) )
-    {
-      ctx_interpret_pos_transform (state, entry, data);
-    }
-  ctx_interpret_pos_bare (state, entry, data);
-}
+  int toplen = 0;
 
-#if CTX_BABL
-void ctx_colorspace_babl (CtxState   *state,
-                          CtxColorSpace  icc_slot,
-                          const Babl *space);
-#endif
+  while ((toplen = vt_line_get_utf8length (topline)) > max_col)
+  {
+     uint32_t unichar = vt_line_get_unichar (topline, toplen-1);
+     uint32_t style =  vt_line_get_style (topline, toplen-1);
+     vt_line_insert_unichar (bottomline, 0, unichar);
+     vt_line_remove (topline, toplen-1);
+     vt_line_set_style (bottomline, 0, style);
+  }
 
-static void
-ctx_state_init (CtxState *state)
-{
-  ctx_memset (state, 0, sizeof (CtxState) );
-  state->gstate.global_alpha_u8 = 255;
-  state->gstate.global_alpha_f  = 1.0;
-  state->gstate.font_size       = 12;
-  state->gstate.line_width      = 2.0;
-  state->gstate.image_smoothing = 1;
-  state->gstate.source_stroke.type = CTX_SOURCE_INHERIT_FILL;
-  ctx_state_set (state, CTX_line_spacing, 1.0f);
-  state->min_x                  = 8192;
-  state->min_y                  = 8192;
-  state->max_x                  = -8192;
-  state->max_y                  = -8192;
-  ctx_matrix_identity (&state->gstate.transform);
-#if CTX_CM
-#if CTX_BABL
-  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_USER_RGB,   babl_space ("sRGB"));
-  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_DEVICE_RGB, babl_space ("ACEScg"));
-#endif
-#endif
+  while (vt_line_get_length (bottomline) &&
+         (toplen = vt_line_get_utf8length (topline)) < max_col)
+  {
+     uint32_t unichar = vt_line_get_unichar (bottomline, 0);
+     uint32_t style =  vt_line_get_style (bottomline, 0);
+     vt_line_append_unichar (topline, unichar);
+     vt_line_set_style (topline, toplen, style);
+     vt_line_remove (bottomline, 0);
+  }
 }
 
-void _ctx_set_transformation (Ctx *ctx, int transformation)
+static void vt_rewrap (VT *vt, int max_col)
 {
-  ctx->transformation = transformation;
+  if (max_col < 8) max_col = 8;
+  CtxList *list = NULL;
+
+  for (CtxList *l = vt->lines; l;)
+  {
+    CtxList *next = l->next;
+    ctx_list_prepend (&list, l->data);
+    ctx_list_remove (&vt->lines, l->data);
+    l = next;
+  }
+  for (CtxList *l = vt->scrollback; l;)
+  {
+    CtxList *next = l->next;
+    ctx_list_prepend (&list, l->data);
+    ctx_list_remove (&vt->scrollback, l->data);
+    l = next;
+  }
+
+  for (CtxList *l = list; l; l = l->next)
+    {
+      VtLine *line = l->data;
+      VtLine *next = l->next ?l->next->data:NULL;
+
+      if (vt_line_get_utf8length (line) >= max_col || (next && next->wrapped))
+      {
+        if (!next)
+        {
+          ctx_list_append (&list, vt_line_new (""));
+          next = l->next->data;
+          next->wrapped = 1;
+        }
+        else if (!next->wrapped)
+        {
+          ctx_list_insert_before (&list, l->next, vt_line_new (""));
+          next = l->next->data;
+          next->wrapped = 1;
+        } 
+        vt_rewrap_pair (vt, line, next, max_col);
+        if (vt_line_get_utf8length (next) == 0)
+          ctx_list_remove (&list, l->next->data);
+      }
+    }
+
+  int rows = vt->rows;
+  int total_rows = ctx_list_length (list);
+
+  int scrollback_rows = total_rows - rows;
+
+  int c = 0;
+  CtxList *l;
+  for (l = list; l && c < scrollback_rows;)
+  {
+    CtxList *next = l->next;
+    ctx_list_prepend (&vt->scrollback, l->data);
+    ctx_list_remove (&list, l->data);
+    l = next;
+    c++;
+  }
+  for (; l ;)
+  {
+    CtxList *next = l->next;
+    ctx_list_prepend (&vt->lines, l->data);
+    ctx_list_remove (&list, l->data);
+    l = next;
+    c++;
+  }
 }
 
-static void
-_ctx_init (Ctx *ctx)
+void vt_set_term_size (VT *vt, int icols, int irows)
 {
-  for (int i = 0; i <256;i++)
-    ctx_u8_float[i] = i/255.0f;
+  if (vt->rows == irows && vt->cols == icols)
+    return;
 
-  ctx_state_init (&ctx->state);
 
-  ctx->renderer = NULL;
-#if CTX_CURRENT_PATH
-  ctx->current_path.flags |= CTX_DRAWLIST_CURRENT_PATH;
-#endif
-  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_SCREEN_SPACE;
-  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_RELATIVE;
-#if CTX_BITPACK
-  ctx->drawlist.flags |= CTX_TRANSFORMATION_BITPACK;
-#endif
-  ctx->texture_cache = ctx;
+  if (vt->state == vt_state_ctx)
+  {
+    // we should queue a pending resize instead,
+    // .. or set a flag indicating that the last
+    // rendered frame is discarded?
+    return;
+  }
+
+  if(1)vt_rewrap (vt, icols);
+
+  while (irows > vt->rows)
+    {
+      if (vt->scrollback_count)
+        {
+          vt->scrollback_count--;
+          ctx_list_append (&vt->lines, vt->scrollback->data);
+          ctx_list_remove (&vt->scrollback, vt->scrollback->data);
+          vt->cursor_y++;
+        }
+      else
+        {
+          ctx_list_prepend (&vt->lines, vt_line_new_with_size ("", vt->cols) );
+        }
+      vt->line_count++;
+      vt->rows++;
+    }
+  while (irows < vt->rows)
+    {
+      vt->cursor_y--;
+      vt->rows--;
+    }
+  vt->rows = irows;
+  vt->cols = icols;
+  vt_resize (vt, vt->cols, vt->rows, vt->width, vt->height);
+  vt_trimlines (vt, vt->rows);
+  vt->margin_top     = 1;
+  vt->margin_left    = 1;
+  vt->margin_bottom  = vt->rows;
+  vt->margin_right   = vt->cols;
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  vt->rev++;
+  VT_info ("resize %i %i", irows, icols);
+  if (vt->ctxp)
+    ctx_parser_free (vt->ctxp);
+  vt->ctxp = NULL;
 }
 
-static void ctx_setup ();
+void vt_set_px_size (VT *vt, int width, int height)
+{
+  int cols = width / vt->cw;
+  int rows = height / vt->ch;
+  vt->width = width;
+  vt->height = height;
+  vt_set_term_size (vt, cols, rows);
+}
 
-#if CTX_DRAWLIST_STATIC
-static Ctx ctx_state;
-#endif
 
-void ctx_set_renderer (Ctx  *ctx,
-                       void *renderer)
+
+static void vt_argument_buf_reset (VT *vt, const char *start)
+{
+  if (start)
+    {
+      strcpy (vt->argument_buf, start);
+      vt->argument_buf_len = strlen (start);
+    }
+  else
+    { vt->argument_buf[vt->argument_buf_len=0]=0; }
+}
+
+static inline void vt_argument_buf_add (VT *vt, int ch)
 {
-  if (ctx->renderer && ctx->renderer->free)
-    ctx->renderer->free (ctx->renderer);
-  ctx->renderer = (CtxImplementation*)renderer;
+  if (vt->argument_buf_len + 1 >= 1024 * 1024 * 16)
+    return; 
+  //
+  if (vt->argument_buf_len + 1 >=
+      vt->argument_buf_cap)
+    {
+      vt->argument_buf_cap = vt->argument_buf_cap * 2;
+      vt->argument_buf = realloc (vt->argument_buf, vt->argument_buf_cap);
+    }
+  vt->argument_buf[vt->argument_buf_len] = ch;
+  vt->argument_buf[++vt->argument_buf_len] = 0;
 }
 
-void *ctx_get_renderer (Ctx *ctx)
+static void
+_vt_move_to (VT *vt, int y, int x)
 {
-  return ctx->renderer;
+  int i;
+  x = x < 1 ? 1 : (x > vt->cols ? vt->cols : x);
+  y = y < 1 ? 1 : (y > vt->rows ? vt->rows : y);
+  vt->at_line_home = 0;
+  vt->cursor_x = x;
+  vt->cursor_y = y;
+  i = vt->rows - y;
+  CtxList *l;
+  for (l = vt->lines; l && i >= 1; l = l->next, i--);
+  if (l)
+    {
+      vt->current_line = l->data;
+    }
+  else
+    {
+      for (; i > 0; i--)
+        {
+          vt->current_line = vt_line_new_with_size ("", vt->cols);
+          ctx_list_append (&vt->lines, vt->current_line);
+          vt->line_count++;
+        }
+    }
+  VT_cursor ("%i,%i (_vt_move_to)", y, x);
+  vt->rev++;
 }
 
-Ctx *
-ctx_new (void)
+static void vt_scroll (VT *vt, int amount);
+
+static void _vt_add_str (VT *vt, const char *str)
 {
-  ctx_setup ();
-#if CTX_DRAWLIST_STATIC
-  Ctx *ctx = &ctx_state;
-#else
-  Ctx *ctx = (Ctx *) malloc (sizeof (Ctx) );
-#endif
-  ctx_memset (ctx, 0, sizeof (Ctx) );
-  _ctx_init (ctx);
-  return ctx;
+  int logical_margin_right = VT_MARGIN_RIGHT;
+  if (vt->cstyle & STYLE_PROPORTIONAL)
+    { vt->current_line->contains_proportional = 1; }
+  if (vt->cursor_x > logical_margin_right)
+    {
+      if (vt->autowrap)
+        {
+          int chars = 0;
+          int old_x = vt->cursor_x;
+          VtLine *old_line = vt->current_line;
+          if (vt->justify && str[0] != ' ')
+            {
+              while (old_x-1-chars >1 && vt_line_get_unichar (vt->current_line,
+                     old_x-1-chars) !=' ')
+                {
+                  chars++;
+                }
+              chars--;
+              if (chars > (vt->margin_right - vt->margin_left) * 3 / 2)
+                { chars = 0; }
+            }
+          if (vt->cursor_y == vt->margin_bottom)
+            {
+              vt_scroll (vt, -1);
+            }
+          else
+            {
+              _vt_move_to (vt, vt->cursor_y+1, 1);
+            }
+          vt->current_line->wrapped=1;
+          vt_carriage_return (vt);
+          for (int i = 0; i < chars; i++)
+            {
+              vt_line_set_style (vt->current_line, vt->cursor_x-1, vt->cstyle);
+              vt_line_replace_unichar (vt->current_line, vt->cursor_x - 1,
+                                         vt_line_get_unichar (old_line, old_x-1-chars+i) );
+              vt->cursor_x++;
+            }
+          for (int i = 0; i < chars; i++)
+            {
+              vt_line_replace_unichar (old_line, old_x-1-chars+i, ' ');
+            }
+          if (str[0] == ' ')
+            return;
+        }
+      else
+        {
+          vt->cursor_x = logical_margin_right;
+        }
+    }
+  if (vt->insert_mode)
+    {
+      vt_line_insert_utf8 (vt->current_line, vt->cursor_x - 1, str);
+      while (vt->current_line->string.utf8_length > logical_margin_right)
+        { vt_line_remove (vt->current_line, logical_margin_right); }
+    }
+  else
+    {
+      vt_line_replace_utf8 (vt->current_line, vt->cursor_x - 1, str);
+    }
+  vt_line_set_style (vt->current_line, vt->cursor_x-1, vt->cstyle);
+  vt->cursor_x += 1;
+  vt->at_line_home = 0;
+  vt->rev++;
 }
 
-void
-ctx_drawlist_deinit (CtxDrawlist *drawlist)
+static void _vt_backspace (VT *vt)
 {
-#if !CTX_DRAWLIST_STATIC
-  if (drawlist->entries && ! (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES) )
+  if (vt->current_line)
     {
-      free (drawlist->entries); 
+      vt->cursor_x --;
+      if (vt->cursor_x == VT_MARGIN_RIGHT) { vt->cursor_x--; }
+      if (vt->cursor_x < VT_MARGIN_LEFT)
+        {
+          vt->cursor_x = VT_MARGIN_LEFT;
+          vt->at_line_home = 1;
+        }
+      VT_cursor ("backspace");
     }
-#endif
-  drawlist->entries = NULL;
-  drawlist->size = 0;
+  vt->rev++;
 }
 
-static void ctx_deinit (Ctx *ctx)
+static void vtcmd_set_top_and_bottom_margins (VT *vt, const char *sequence)
 {
-  if (ctx->renderer)
+  int top = 1, bottom = vt->rows;
+  /* w3m issues this; causing reset of cursor position, why it is issued
+   * is unknown 
+   */
+  if (!strcmp (sequence, "[?1001r"))
+    return;
+  if (strlen (sequence) > 2)
     {
-      if (ctx->renderer->free)
-        ctx->renderer->free (ctx->renderer);
-      ctx->renderer    = NULL;
+      sscanf (sequence, "[%i;%ir", &top, &bottom);
     }
-  ctx_drawlist_deinit (&ctx->drawlist);
-#if CTX_CURRENT_PATH
-  ctx_drawlist_deinit (&ctx->current_path);
+  VT_info ("margins: %i %i", top, bottom);
+  if (top <1) { top = 1; }
+  if (top > vt->rows) { top = vt->rows; }
+  if (bottom > vt->rows) { bottom = vt->rows; }
+  if (bottom < top) { bottom = top; }
+  vt->margin_top = top;
+  vt->margin_bottom = bottom;
+#if 0
+  _vt_move_to (vt, top, 1);
 #endif
+  vt_carriage_return (vt);
+  VT_cursor ("%i, %i (home)", top, 1);
 }
+static void vtcmd_save_cursor_position (VT *vt, const char *sequence);
 
-void ctx_free (Ctx *ctx)
+static void vtcmd_set_left_and_right_margins (VT *vt, const char *sequence)
 {
-  if (!ctx)
-    { return; }
-#if CTX_EVENTS
-  ctx_clear_bindings (ctx);
-#endif
-  ctx_deinit (ctx);
-#if !CTX_DRAWLIST_STATIC
-  free (ctx);
-#endif
+  int left = 1, right = vt->cols;
+  if (!vt->left_right_margin_mode)
+    {
+      vtcmd_save_cursor_position (vt, sequence);
+      return;
+    }
+  if (strlen (sequence) > 2)
+    {
+      sscanf (sequence, "[%i;%is", &left, &right);
+    }
+  VT_info ("hor margins: %i %i", left, right);
+  if (left <1) { left = 1; }
+  if (left > vt->cols) { left = vt->cols; }
+  if (right > vt->cols) { right = vt->cols; }
+  if (right < left) { right = left; }
+  vt->margin_left = left;
+  vt->margin_right = right;
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  vt_carriage_return (vt);
+  //VT_cursor ("%i, %i (home)", left, 1);
 }
 
-Ctx *ctx_new_for_drawlist (void *data, size_t length)
+static inline int parse_int (const char *arg, int def_val)
 {
-  Ctx *ctx = ctx_new ();
-  ctx->drawlist.flags   |= CTX_DRAWLIST_DOESNT_OWN_ENTRIES;
-  ctx->drawlist.entries  = (CtxEntry *) data;
-  ctx->drawlist.count    = length / sizeof (CtxEntry);
-  return ctx;
+  if (!isdigit (arg[1]) || strlen (arg) == 2)
+    { return def_val; }
+  return atoi (arg+1);
 }
 
-#ifdef CTX_HAVE_SIMD
-void ctx_simd_setup ();
-#endif
 
-static void ctx_setup ()
+static void vtcmd_set_line_home (VT *vt, const char *sequence)
 {
-#ifdef CTX_HAVE_SIMD
-  ctx_simd_setup ();
-#endif
-  ctx_font_setup ();
+  int val = parse_int (sequence, 1);
+  char buf[256];
+  vt->left_right_margin_mode = 1;
+  sprintf (buf, "[%i;%it", val, vt->margin_right);
+  vtcmd_set_left_and_right_margins (vt, buf);
 }
 
-void
-ctx_render_ctx (Ctx *ctx, Ctx *d_ctx)
+static void vtcmd_set_line_limit (VT *vt, const char *sequence)
 {
-  CtxIterator iterator;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
+  int val = parse_int (sequence, 0);
+  char buf[256];
+  vt->left_right_margin_mode = 1;
+  if (val < vt->margin_left) { val = vt->margin_left; }
+  sprintf (buf, "[%i;%it", vt->margin_left, val);
+  vtcmd_set_left_and_right_margins (vt, buf);
+}
+
+static void vt_scroll (VT *vt, int amount)
+{
+  int remove_no, insert_before;
+  VtLine *string = NULL;
+  if (amount == 0) { amount = 1; }
+  if (amount < 0)
     {
-       ctx_process (d_ctx, &command->entry);
+      remove_no = vt->margin_top;
+      insert_before = vt->margin_bottom;
+    }
+  else
+    {
+      remove_no = vt->margin_bottom;
+      insert_before = vt->margin_top;
+    }
+  CtxList *l;
+  int i;
+  for (i=vt->rows, l = vt->lines; i > 0 && l; l=l->next, i--)
+    {
+      if (i == remove_no)
+        {
+          string = l->data;
+          ctx_list_remove (&vt->lines, string);
+          break;
+        }
+    }
+  if (string)
+    {
+      if (!vt->in_alt_screen &&
+          (vt->margin_top == 1 && vt->margin_bottom == vt->rows) )
+        {
+          ctx_list_prepend (&vt->scrollback, string);
+          vt->scrollback_count ++;
+        }
+      else
+        {
+          vt_line_free (string, 1);
+        }
+    }
+  string = vt_line_new_with_size ("", vt->cols/4);
+  if (amount > 0 && vt->margin_top == 1)
+    {
+      ctx_list_append (&vt->lines, string);
+    }
+  else
+    {
+      for (i=vt->rows, l = vt->lines; l; l=l->next, i--)
+        {
+          if (i == insert_before)
+            {
+              ctx_list_insert_before (&vt->lines, l, string);
+              break;
+            }
+        }
+      if (i != insert_before)
+        {
+          ctx_list_append (&vt->lines, string);
+        }
+    }
+  vt->current_line = string;
+  /* not updating line count since we should always remove one and add one */
+  if (vt->smooth_scroll)
+    {
+      if (amount < 0)
+        {
+          vt->scroll_offset = -1.0;
+          vt->in_smooth_scroll = -1;
+        }
+      else
+        {
+          vt->scroll_offset = 1.0;
+          vt->in_smooth_scroll = 1;
+        }
     }
+
+  {
+    vt->select_begin_row += amount;
+    vt->select_end_row += amount;
+    vt->select_start_row += amount;
+  }
 }
 
-void
-ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx)
+typedef struct Sequence
 {
-  CtxIterator iterator;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
+  const char *prefix;
+  char        suffix;
+  void (*vtcmd) (VT *vt, const char *sequence);
+  uint32_t    compat;
+} Sequence;
+
+static void vtcmd_cursor_position (VT *vt, const char *sequence)
+{
+  int y = 1, x = 1;
+  const char *semi;
+  if (sequence[0] != 'H' && sequence[0] != 'f')
     {
-       switch (command->code)
-       {
-         default:
-                 //fprintf (stderr, "[%c]", command->code);
-                 break;
-         case CTX_TEXTURE:
-             fprintf (stderr, "t:%s\n", command->texture.eid);
-             ctx_process (d_ctx, &command->entry);
-             break;
-         case CTX_DEFINE_TEXTURE:
-             fprintf (stderr, "d:%s\n", command->define_texture.eid);
-             ctx_process (d_ctx, &command->entry);
-           break;
-       }
+      y = parse_int (sequence, 1);
+      if ( (semi = strchr (sequence, ';') ) )
+        {
+          x = parse_int (semi, 1);
+        }
+    }
+  if (x == 0) { x = 1; }
+  if (y == 0) { y = 1; }
+  if (vt->origin)
+    {
+      y += vt->margin_top - 1;
+      _vt_move_to (vt, y, vt->cursor_x);
+      x += VT_MARGIN_LEFT - 1;
     }
+  VT_cursor ("%i %i CUP", y, x);
+  _vt_move_to (vt, y, x);
 }
 
-void ctx_quit (Ctx *ctx)
+
+static void vtcmd_horizontal_position_absolute (VT *vt, const char *sequence)
 {
-#if CTX_EVENTS
-  ctx->quit ++;
-#endif
+  int x = parse_int (sequence, 1);
+  if (x<=0) { x = 1; }
+  _vt_move_to (vt, vt->cursor_y, x);
 }
 
-int  ctx_has_quit (Ctx *ctx)
+static void vtcmd_goto_row (VT *vt, const char *sequence)
 {
-#if CTX_EVENTS
-  return (ctx->quit);
-#else
-  return 1; 
-#endif
+  int y = parse_int (sequence, 1);
+  _vt_move_to (vt, y, vt->cursor_x);
 }
 
-int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format)
+static void vtcmd_cursor_forward (VT *vt, const char *sequence)
 {
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->bpp;
-  return -1;
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
+    {
+      vt->cursor_x++;
+    }
+  if (vt->cursor_x > VT_MARGIN_RIGHT)
+    { vt->cursor_x = VT_MARGIN_RIGHT; }
 }
 
-int ctx_pixel_format_get_stride (CtxPixelFormat format, int width)
+static void vtcmd_cursor_backward (VT *vt, const char *sequence)
 {
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-  {
-    switch (info->bpp)
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
     {
-      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);
+      vt->cursor_x--;
+    }
+  if (vt->cursor_x < VT_MARGIN_LEFT)
+    {
+      vt->cursor_x = VT_MARGIN_LEFT; // should this wrap??
+      vt->at_line_home = 1;
     }
-  }
-  return width;
 }
 
-int ctx_pixel_format_ebpp (CtxPixelFormat format)
+static void vtcmd_reverse_index (VT *vt, const char *sequence)
 {
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->ebpp;
-  return -1;
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
+    {
+      if (vt->cursor_y == vt->margin_top)
+        {
+          vt_scroll (vt, 1);
+          _vt_move_to (vt, vt->margin_top, vt->cursor_x);
+        }
+      else
+        {
+          _vt_move_to (vt, vt->cursor_y-1, vt->cursor_x);
+        }
+    }
 }
 
-int ctx_pixel_format_components (CtxPixelFormat format)
+static void vtcmd_cursor_up (VT *vt, const char *sequence)
 {
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->components;
-  return -1;
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
+    {
+      if (vt->cursor_y == vt->margin_top)
+        {
+          //_vt_move_to (vt, 1, vt->cursor_x);
+        }
+      else
+        {
+          _vt_move_to (vt, vt->cursor_y-1, vt->cursor_x);
+        }
+    }
 }
 
-#if CTX_EVENTS
-void         ctx_set_cursor (Ctx *ctx, CtxCursor cursor)
+static void vtcmd_back_index (VT *vt, const char *sequence)
 {
-  if (ctx->cursor != cursor)
-  {
-    ctx_set_dirty (ctx, 1);
-    ctx->cursor = cursor;
-  }
+  // XXX implement
 }
-CtxCursor    ctx_get_cursor (Ctx *ctx)
+
+static void vtcmd_forward_index (VT *vt, const char *sequence)
 {
-  return ctx->cursor;
+  // XXX implement
 }
 
-void ctx_set_clipboard (Ctx *ctx, const char *text)
+static void vtcmd_index (VT *vt, const char *sequence)
 {
-  if (ctx->renderer && ctx->renderer->set_clipboard)
-  {
-    ctx->renderer->set_clipboard (ctx->renderer, text);
-    return;
-  }
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
+    {
+      if (vt->cursor_y == vt->margin_bottom)
+        {
+          vt_scroll (vt, -1);
+          _vt_move_to (vt, vt->margin_bottom, vt->cursor_x);
+        }
+      else
+        {
+          _vt_move_to (vt, vt->cursor_y + 1, vt->cursor_x);
+        }
+    }
 }
 
-char *ctx_get_clipboard (Ctx *ctx)
+static void vtcmd_cursor_down (VT *vt, const char *sequence)
 {
-  if (ctx->renderer && ctx->renderer->get_clipboard)
-  {
-    return ctx->renderer->get_clipboard (ctx->renderer);
-  }
-  return strdup ("");
+  int n = parse_int (sequence, 1);
+  if (n==0) { n = 1; }
+  for (int i = 0; i < n; i++)
+    {
+      if (vt->cursor_y >= vt->margin_bottom)
+        {
+          _vt_move_to (vt, vt->margin_bottom, vt->cursor_x);
+        }
+      else
+        {
+          _vt_move_to (vt, vt->cursor_y + 1, vt->cursor_x);
+        }
+    }
 }
 
-void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source)
+static void vtcmd_next_line (VT *vt, const char *sequence)
 {
-  ((CtxRasterizer*)ctx->renderer)->texture_source = texture_source;
+  vtcmd_index (vt, sequence);
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  vt_carriage_return (vt);
+  vt->cursor_x = VT_MARGIN_LEFT;
 }
 
-void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache)
+static void vtcmd_cursor_preceding_line (VT *vt, const char *sequence)
 {
-  ctx->texture_cache = texture_cache;
+  vtcmd_cursor_up (vt, sequence);
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  vt->cursor_x = VT_MARGIN_LEFT;
 }
 
-void ctx_set_transform (Ctx *ctx, float a, float b, float c, float d, float e, float f)
+static void vtcmd_erase_in_line (VT *vt, const char *sequence)
 {
-  ctx_identity (ctx);
-  ctx_apply_transform (ctx, a, b, c, d, e, f);
+  int n = parse_int (sequence, 0);
+  switch (n)
+    {
+      case 0: // clear to end of line
+        {
+          char *p = (char *) mrg_utf8_skip (vt->current_line->string.str, vt->cursor_x-1);
+          if (p) { *p = 0; }
+          // XXX : this is chopping lines
+          for (int col = vt->cursor_x; col <= VT_MARGIN_RIGHT; col++)
+            { vt_line_set_style (vt->current_line, col - 1, vt->cstyle); }
+          vt->current_line->string.length = strlen (vt->current_line->string.str);
+          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str);
+        }
+        break;
+      case 1: // clear from beginning to cursor
+        {
+          for (int col = VT_MARGIN_LEFT; col <= vt->cursor_x; col++)
+            {
+              vt_line_replace_utf8 (vt->current_line, col-1, " ");
+            }
+          for (int col = VT_MARGIN_LEFT; col <= vt->cursor_x; col++)
+            { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
+          vt->current_line->string.length = strlen (vt->current_line->string.str);
+          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str); // should 
be a nop
+        }
+        break;
+      case 2: // clear entire line
+        for (int col = VT_MARGIN_LEFT; col <= VT_MARGIN_RIGHT; col++)
+          { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
+        vt_line_set (vt->current_line, ""); // XXX not all
+        break;
+    }
 }
-#ifndef NO_LIBCURL
-#include <curl/curl.h>
-static size_t
-ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *userp)
+
+static void vtcmd_erase_in_display (VT *vt, const char *sequence)
 {
-  CtxString *string = (CtxString*)userp;
-  ctx_string_append_data ((CtxString*)string, contents, size * nmemb);
-  return size * nmemb;
+  int n = parse_int (sequence, 0);
+  switch (n)
+    {
+      case 0: // clear to end of screen
+        {
+          char *p = (char *) mrg_utf8_skip (vt->current_line->string.str, vt->cursor_x-1);
+          if (p) { *p = 0; }
+          vt->current_line->string.length = strlen (vt->current_line->string.str);
+          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str);
+        }
+        for (int col = vt->cursor_x; col <= VT_MARGIN_RIGHT; col++)
+          { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
+        {
+          CtxList *l;
+          int no = vt->rows;
+          for (l = vt->lines; l->data != vt->current_line; l = l->next, no--)
+            {
+              VtLine *buf = l->data;
+              buf->string.str[0] = 0;
+              buf->string.length = 0;
+              buf->string.utf8_length = 0;
+              for (int col = 1; col <= vt->cols; col++)
+                { vt_line_set_style (buf, col-1, vt->cstyle); }
+            }
+        }
+        break;
+      case 1: // clear from beginning to cursor
+        {
+          for (int col = 1; col <= vt->cursor_x; col++)
+            {
+              vt_line_replace_utf8 (vt->current_line, col-1, " ");
+              vt_line_set_style (vt->current_line, col-1, vt->cstyle);
+            }
+        }
+        {
+          CtxList *l;
+          int there_yet = 0;
+          int no = vt->rows;
+          for (l = vt->lines; l; l = l->next, no--)
+            {
+              VtLine *buf = l->data;
+              if (there_yet)
+                {
+                  buf->string.str[0] = 0;
+                  buf->string.length = 0;
+                  buf->string.utf8_length = 0;
+                  for (int col = 1; col <= vt->cols; col++)
+                    { vt_line_set_style (buf, col-1, vt->cstyle); }
+                }
+              if (buf == vt->current_line)
+                {
+                  there_yet = 1;
+                }
+            }
+        }
+        break;
+      case 3: // also clear scrollback
+        while (vt->scrollback)
+        {
+           vt_line_free (vt->scrollback->data, 1);
+           ctx_list_remove (&vt->scrollback, vt->scrollback->data);
+         }
+        vt->scrollback_count = 0;
+        /* FALLTHROUGH */
+      case 2: // clear entire screen but keep cursor;
+        {
+          int tx = vt->cursor_x;
+          int ty = vt->cursor_y;
+          vtcmd_clear (vt, "");
+          _vt_move_to (vt, ty, tx);
+          for (CtxList *l = vt->lines; l; l = l->next)
+            {
+              VtLine *line = l->data;
+              for (int col = 1; col <= vt->cols; col++)
+                { vt_line_set_style (line, col-1, vt->cstyle); }
+            }
+        }
+        break;
+    }
 }
 
-#endif
-
-int
-ctx_get_contents (const char     *uri,
-                  unsigned char **contents,
-                  long           *length)
+static void vtcmd_screen_alignment_display (VT *vt, const char *sequence)
 {
-  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 = (CtxFileContent*)l->data;
-    if (!strcmp (c->path, uri))
+  for (int y = 1; y <= vt->rows; y++)
     {
-      contents = malloc (c->length+1);
-      contents[c->length]=0;
-      if (length) *length = c->length;
-      return 0;
+      _vt_move_to (vt, y, 1);
+      for (int x = 1; x <= vt->cols; x++)
+        {
+          _vt_add_str (vt, "E");
+        }
     }
-  }
-
-  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;
 }
 
-
+#if 0
+static int find_idx (int r, int g, int b)
+{
+  r = r / 255.0 * 5;
+  g = g / 255.0 * 5;
+  b = b / 255.0 * 5;
+  return 16 + r * 6 * 6 + g * 6 + b;
+}
 #endif
 
-#endif // CTX_IMPLEMENTATION
-
-#if CTX_COMPOSITE
-
+static void vtcmd_set_graphics_rendition (VT *vt, const char *sequence)
+{
+  const char *s = sequence;
+  if (s[0]) { s++; }
+  while (s && *s)
+    {
+      int n = parse_int (s - 1, 0); // works until color
+      // both fg and bg could be set in 256 color mode FIXME
+      //
+      /* S_GR@38@Set forground color@foo bar baz@ */
+      if (n == 38) // set foreground
+        {
+          s = strchr (s, ';');
+          if (!s)
+            {
+              VT_warning ("incomplete [38m expected ;  %s", sequence);
+              return;
+            }
+          n = parse_int (s, 0);
+          if (n == 5)
+            {
+              s++;
+              if (strchr (s, ';') )
+                { s = strchr (s, ';'); }
+              else
+                { s = strchr (s, ':'); }
+              if (s)
+                {
+                  n = parse_int (s, 0);
+                  set_fg_idx (n);
+                  s++;
+                  while (*s && *s >= '0' && *s <='9') { s++; }
+                }
+            }
+          else if (n == 2)
+            {
+              int r = 0, g = 0, b = 0;
+              s++;
+              if (strchr (s, ';') )
+                {
+                  s = strchr (s, ';');
+                  if (s)
+                    { sscanf (s, ";%i;%i;%i", &r, &g, &b); }
+                }
+              else
+                {
+                  s = strchr (s, ':');
+                  if (s)
+                    { sscanf (s, ":%i:%i:%i", &r, &g, &b); }
+                }
+              if (s)
+              for (int i = 0; i < 3; i++)
+                {
+                  if (*s)
+                    {
+                      s++;
+                      while (*s && *s >= '0' && *s <='9') { s++; }
+                    }
+                }
+              set_fg_rgb (r,g,b);
+            }
+          else
+            {
+              VT_warning ("unhandled %s %i", sequence, n);
+              return;
+            }
+          //return; // XXX we should continue, and allow further style set after complex color
+        }
+      else if (n == 48) // set background
+        {
+          s = strchr (s, ';');
+          if (!s)
+            {
+              VT_warning ("incomplete [38m expected ;  %s", sequence);
+              return;
+            }
+          n = parse_int (s, 0);
+          if (n == 5)
+            {
+              s++;
+              if (strchr (s, ';') )
+                { s = strchr (s, ';'); }
+              else
+                { s = strchr (s, ':'); }
+              if (s)
+                { n = parse_int (s, 0); }
+              set_bg_idx (n);
+              if (s)
+                {
+                  s++;
+                  while (*s && *s >= '0' && *s <='9') { s++; }
+                }
+            }
+          else if (n == 2)
+            {
+              int r = 0, g = 0, b = 0;
+              s++;
+              if (strchr (s, ';') )
+                {
+                  s = strchr (s, ';');
+                  if (s)
+                    { sscanf (s, ";%i;%i;%i", &r, &g, &b); }
+                }
+              else
+                {
+                  s = strchr (s, ':');
+                  if (s)
+                    { sscanf (s, ":%i:%i:%i", &r, &g, &b); }
+                }
+              if (s)
+              for (int i = 0; i < 3; i++)
+                {
+                  s++;
+                  while (*s >= '0' && *s <='9') { s++; }
+                }
+              set_bg_rgb (r,g,b);
+            }
+          else
+            {
+              VT_warning ("unhandled %s %i", sequence, n);
+              return;
+            }
+          //return; // we XXX should continue, and allow further style set after complex color
+        }
+      else
+        switch (n)
+          {
+            case 0: /* SGR@0@Style reset@@ */
+              if (vt->cstyle & STYLE_PROPORTIONAL)
+                { vt->cstyle = STYLE_PROPORTIONAL; }
+              else
+                { vt->cstyle = 0; }
+              break;
+            case 1: /* SGR@@Bold@@ */
+              vt->cstyle |= STYLE_BOLD;
+              break;
+            case 2: /* SGR@@Dim@@ */
+              vt->cstyle |= STYLE_DIM;
+              break;
+            case 3: /* SGR@@Italic@@ */
+              vt->cstyle |= STYLE_ITALIC;
+              break;
+            case 4: /* SGR@@Underscore@@ */
+                    /* SGR@4:2@Double underscore@@ */
+                    /* SGR@4:3@Curvy underscore@@ */
+              if (s[1] == ':')
+                {
+                  switch (s[2])
+                    {
+                      case '0':
+                        break;
+                      case '1':
+                        vt->cstyle |= STYLE_UNDERLINE;
+                        break;
+                      case '2':
+                        vt->cstyle |= STYLE_UNDERLINE|
+                                      STYLE_UNDERLINE_VAR;
+                        break;
+                      default:
+                      case '3':
+                        vt->cstyle |= STYLE_UNDERLINE_VAR;
+                        break;
+                    }
+                }
+              else
+                {
+                  vt->cstyle |= STYLE_UNDERLINE;
+                }
+              break;
+            case 5: /* SGR@@Blink@@ */
+              vt->cstyle |= STYLE_BLINK;
+              break;
+            case 6: /* SGR@@Blink Fast@@ */
+              vt->cstyle |= STYLE_BLINK_FAST;
+              break;
+            case 7: /* SGR@@Reverse@@ */
+              vt->cstyle |= STYLE_REVERSE;
+              break;
+            case 8: /* SGR@@Hidden@@ */
+              vt->cstyle |= STYLE_HIDDEN;
+              break;
+            case 9: /* SGR@@Strikethrough@@ */
+              vt->cstyle |= STYLE_STRIKETHROUGH;
+              break;
+            case 10: /* SGR@@Font 0@@ */
+              break;
+            case 11: /* SGR@@Font 1@@ */
+              break;
+            case 12: /* SGR@@Font 2(ignored)@@ */
+            case 13: /* SGR@@Font 3(ignored)@@ */
+            case 14: /* SGR@@Font 4(ignored)@@ */
+              break;
+            case 22: /* SGR@@Bold off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_BOLD);
+              vt->cstyle ^= (vt->cstyle & STYLE_DIM);
+              break;
+            case 23: /* SGR@@Italic off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_ITALIC);
+              break;
+            case 24: /* SGR@@Underscore off@@ */
+              vt->cstyle ^= (vt->cstyle & (STYLE_UNDERLINE|STYLE_UNDERLINE_VAR) );
+              break;
+            case 25: /* SGR@@Blink off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_BLINK);
+              vt->cstyle ^= (vt->cstyle & STYLE_BLINK_FAST);
+              break;
+            case 26: /* SGR@@Proportional spacing @@ */
+              vt->cstyle |= STYLE_PROPORTIONAL;
+              break;
+            case 27: /* SGR@@Reverse off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_REVERSE);
+              break;
+            case 28: /* SGR@@Hidden off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_HIDDEN);
+              break;
+            case 29: /* SGR@@Strikethrough off@@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_STRIKETHROUGH);
+              break;
+            case 30: /* SGR@@black text color@@ */
+              set_fg_idx (0);
+              break;
+            case 31: /* SGR@@red text color@@ */
+              set_fg_idx (1);
+              break;
+            case 32: /* SGR@@green text color@@ */
+              set_fg_idx (2);
+              break;
+            case 33: /* SGR@@yellow text color@@ */
+              set_fg_idx (3);
+              break;
+            case 34: /* SGR@@blue text color@@ */
+              set_fg_idx (4);
+              break;
+            case 35: /* SGR@@magenta text color@@ */
+              set_fg_idx (5);
+              break;
+            case 36: /* SGR@@cyan text color@@ */
+              set_fg_idx (6);
+              break;
+            case 37: /* SGR@@light gray text color@@ */
+              set_fg_idx (7);
+              break;
+          /* SGR@38;5;Pn@256 color index foreground color@where Pn is 0-15 is system colors 16-(16+6*6*6) is 
a 6x6x6  RGB cube and in the end a grayscale without white and black.@ */
+              /* SGR@38;2;Pr;Pg;Pb@24 bit RGB foreground color each of Pr Pg and Pb have 0-255 range@@ */
+            case 39: /* SGR@@default text color@@ */
+              set_fg_idx (vt->reverse_video?0:15);
+              vt->cstyle ^= (vt->cstyle & STYLE_FG_COLOR_SET);
+              break;
+            case 40: /* SGR@@black background color@@ */
+              set_bg_idx (0);
+              break;
+            case 41: /* SGR@@red background color@@ */
+              set_bg_idx (1);
+              break;
+            case 42: /* SGR@@green background color@@ */
+              set_bg_idx (2);
+              break;
+            case 43: /* SGR@@yellow background color@@ */
+              set_bg_idx (3);
+              break;
+            case 44: /* SGR@@blue background color@@ */
+              set_bg_idx (4);
+              break;
+            case 45: /* SGR@@magenta background color@@ */
+              set_bg_idx (5);
+              break;
+            case 46: /* SGR@@cyan background color@@ */
+              set_bg_idx (6);
+              break;
+            case 47: /* SGR@@light gray background color@@ */
+              set_bg_idx (7);
+              break;
 
-#define CTX_RGBA8_R_SHIFT  0
-#define CTX_RGBA8_G_SHIFT  8
-#define CTX_RGBA8_B_SHIFT  16
-#define CTX_RGBA8_A_SHIFT  24
+          /* SGR@48;5;Pn@256 color index background color@where Pn is 0-15 is system colors 16-(16+6*6*6) is 
a 6x6x6  RGB cube and in the end a grayscale without white and black.@ */
+          /* SGR@48;2;Pr;Pg;Pb@24 bit RGB background color@Where Pr Pg and Pb have 0-255 range@ */
 
-#define CTX_RGBA8_R_MASK   (0xff << CTX_RGBA8_R_SHIFT)
-#define CTX_RGBA8_G_MASK   (0xff << CTX_RGBA8_G_SHIFT)
-#define CTX_RGBA8_B_MASK   (0xff << CTX_RGBA8_B_SHIFT)
-#define CTX_RGBA8_A_MASK   (0xff << CTX_RGBA8_A_SHIFT)
+            case 49: /* SGR@@default background color@@ */
+              set_bg_idx (vt->reverse_video?15:0);
+              vt->cstyle ^= (vt->cstyle & STYLE_BG_COLOR_SET);
+              break;
+            case 50: /* SGR@@Proportional spacing off @@ */
+              vt->cstyle ^= (vt->cstyle & STYLE_PROPORTIONAL);
+              break;
+            // 51 : framed
+            // 52 : encircled
+            case 53: /* SGR@@Overlined@@ */
+              vt->cstyle |= STYLE_OVERLINE;
+              break;
+            case 55: /* SGR@@Not Overlined@@ */
+              vt->cstyle ^= (vt->cstyle&STYLE_OVERLINE);
+              break;
+            case 90: /* SGR@@dark gray text color@@ */
+              set_fg_idx (8);
+              break;
+            case 91: /* SGR@@light red text color@@ */
+              set_fg_idx (9);
+              break;
+            case 92: /* SGR@@light green text color@@ */
+              set_fg_idx (10);
+              break;
+            case 93: /* SGR@@light yellow text color@@ */
+              set_fg_idx (11);
+              break;
+            case 94: /* SGR@@light blue text color@@ */
+              set_fg_idx (12);
+              break;
+            case 95: /* SGR@@light magenta text color@@ */
+              set_fg_idx (13);
+              break;
+            case 96: /* SGR@@light cyan text color@@ */
+              set_fg_idx (14);
+              break;
+            case 97: /* SGR@@white text color@@ */
+              set_fg_idx (15);
+              break;
+            case 100: /* SGR@@dark gray background color@@ */
+              set_bg_idx (8);
+              break;
+            case 101: /* SGR@@light red background color@@ */
+              set_bg_idx (9);
+              break;
+            case 102: /* SGR@@light green background color@@ */
+              set_bg_idx (10);
+              break;
+            case 103: /* SGR@@light yellow background color@@ */
+              set_bg_idx (11);
+              break;
+            case 104: /* SGR@@light blue background color@@ */
+              set_bg_idx (12);
+              break;
+            case 105: /* SGR@@light magenta background color@@ */
+              set_bg_idx (13);
+              break;
+            case 106: /* SGR@@light cyan background color@@ */
+              set_bg_idx (14);
+              break;
+            case 107: /* SGR@@white background color@@ */
+              set_bg_idx (15);
+              break;
+            default:
+              VT_warning ("unhandled style code %i in sequence \\033%s\n", n, sequence);
+              return;
+          }
+      while (s && *s && *s != ';') { s++; }
+      if (s && *s == ';') { s++; }
+    }
+}
 
-#define CTX_RGBA8_RB_MASK  (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK)
-#define CTX_RGBA8_GA_MASK  (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK)
+static void vtcmd_ignore (VT *vt, const char *sequence)
+{
+  VT_info ("ignoring sequence %s", sequence);
+}
 
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
+static void vtcmd_clear_all_tabs (VT *vt, const char *sequence)
+{
+  memset (vt->tabs, 0, sizeof (vt->tabs) );
+}
 
+static void vtcmd_clear_current_tab (VT *vt, const char *sequence)
+{
+  vt->tabs[ (int) (vt->cursor_x-1)] = 0;
+}
 
-inline static int ctx_grad_index (float v)
+static void vtcmd_horizontal_tab_set (VT *vt, const char *sequence)
 {
-  int ret = v * (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f) + 0.5f;
-  if (ret >= CTX_GRADIENT_CACHE_ELEMENTS)
-    return CTX_GRADIENT_CACHE_ELEMENTS - 1;
-  if (ret >= 0 && ret < CTX_GRADIENT_CACHE_ELEMENTS)
-    return ret;
-  return 0;
+  vt->tabs[ (int) vt->cursor_x-1] = 1;
 }
 
-extern uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
-extern uint8_t ctx_gradient_cache_u8_a[CTX_GRADIENT_CACHE_ELEMENTS][4];
-extern int ctx_gradient_cache_valid;
+static void vtcmd_save_cursor_position (VT *vt, const char *sequence)
+{
+  vt->saved_x = vt->cursor_x;
+  vt->saved_y = vt->cursor_y;
+}
 
-//static void
-//ctx_gradient_cache_reset (void)
-//{
-//  ctx_gradient_cache_valid = 0;
-//}
+static void vtcmd_restore_cursor_position (VT *vt, const char *sequence)
+{
+  _vt_move_to (vt, vt->saved_y, vt->saved_x);
+}
 
 
-#endif
+static void vtcmd_save_cursor (VT *vt, const char *sequence)
+{
+  vt->saved_style   = vt->cstyle;
+  vt->saved_origin  = vt->origin;
+  vtcmd_save_cursor_position (vt, sequence);
+  for (int i = 0; i < 4; i++)
+    { vt->saved_charset[i] = vt->charset[i]; }
+}
 
-CTX_INLINE static void
-_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+static void vtcmd_restore_cursor (VT *vt, const char *sequence)
 {
-  float v = x;
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
+  vtcmd_restore_cursor_position (vt, sequence);
+  vt->cstyle  = vt->saved_style;
+  vt->origin  = vt->saved_origin;
+  for (int i = 0; i < 4; i++)
+    { vt->charset[i] = vt->saved_charset[i]; }
+}
 
-  if (g->n_stops == 0)
-    {
-      rgba[0] = rgba[1] = rgba[2] = v * 255;
-      rgba[3] = 255;
-      return;
-    }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
-    {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
-    }
-  if (stop == NULL && next_stop)
+static void vtcmd_erase_n_chars (VT *vt, const char *sequence)
+{
+  int n = parse_int (sequence, 1);
+  while (n--)
     {
-      color = & (next_stop->color);
+      vt_line_replace_utf8 (vt->current_line, vt->cursor_x - 1 + n, " ");
+      vt_line_set_style (vt->current_line, vt->cursor_x + n - 1, vt->cstyle);
     }
-  else if (stop && next_stop == NULL)
+}
+
+static void vtcmd_delete_n_chars (VT *vt, const char *sequence)
+{
+  int n = parse_int (sequence, 1);
+  int count = n;
+  while (count--)
     {
-      color = & (stop->color);
+      vt_line_remove (vt->current_line, vt->cursor_x - 1);
     }
-  else if (stop && next_stop)
+}
+
+static void vtcmd_delete_n_lines (VT *vt, const char *sequence)
+{
+  int n = parse_int (sequence, 1);
+  for (int a = 0; a < n; a++)
     {
-      uint8_t stop_rgba[4];
-      uint8_t next_rgba[4];
-      ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
-      for (int c = 0; c < 4; c++)
-        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
-      return;
+      int i;
+      CtxList *l;
+      VtLine *string = vt->current_line;
+      vt_line_set (string, "");
+      ctx_list_remove (&vt->lines, vt->current_line);
+      for (i=vt->rows, l = vt->lines; l; l=l->next, i--)
+        {
+          if (i == vt->margin_bottom)
+            {
+              vt->current_line = string;
+              ctx_list_insert_before (&vt->lines, l, string);
+              break;
+            }
+        }
+      _vt_move_to (vt, vt->cursor_y, vt->cursor_x); // updates current_line
     }
-  else
+}
+
+static void vtcmd_insert_character (VT *vt, const char *sequence)
+{
+  int n = parse_int (sequence, 1);
+  while (n--)
     {
-      color = & (g->stops[g->n_stops-1].color);
+      vt_line_insert_utf8 (vt->current_line, vt->cursor_x-1, " ");
     }
-  ctx_color_get_rgba8 (rasterizer->state, color, rgba);
-  if (rasterizer->swap_red_green)
-  {
-    uint8_t tmp = rgba[0];
-    rgba[0] = rgba[2];
-    rgba[2] = tmp;
-  }
+  while (vt->current_line->string.utf8_length > vt->cols)
+    { vt_line_remove (vt->current_line, vt->cols); }
+  // XXX update style
 }
 
-#if CTX_GRADIENT_CACHE
-static void
-ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
-#endif
+static void vtcmd_scroll_up (VT *vt, const char *sequence)
+{
+  int n = parse_int (sequence, 1);
+  if (n == 0) { n = 1; }
+  while (n--)
+    { vt_scroll (vt, -1); }
+}
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+static void vtcmd_scroll_down (VT *vt, const char *sequence)
 {
-#if CTX_GRADIENT_CACHE
-  uint32_t* rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(x)][0]));
-  *((uint32_t*)rgba) = *rgbap;
-#else
- _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba);
-#endif
+  int n = parse_int (sequence, 1);
+  if (n == 0) { n = 1; }
+  while (n--)
+    { vt_scroll (vt, 1); }
 }
-#endif
 
-CTX_INLINE static void
-ctx_RGBA8_associate_alpha (uint8_t *u8)
+static void vtcmd_insert_blank_lines (VT *vt, const char *sequence)
 {
-  uint32_t val = *((uint32_t*)(u8));
-  int a = val >> CTX_RGBA8_A_SHIFT;
-  if (a!=255)
+  int n = parse_int (sequence, 1);
+  if (n == 0) { n = 1; }
   {
-    if (a)
-    {
-      uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
-      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
-      *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
-    }
-    else
-    {
-      *((uint32_t*)(u8)) = 0;
-    }
+    int st = vt->margin_top;
+    int sb = vt->margin_bottom;
+    vt->margin_top = vt->cursor_y;
+    while (n--)
+      {
+        vt_scroll (vt, 1);
+      }
+    vt->margin_top = st;
+    vt->margin_bottom = sb;
   }
 }
 
-CTX_INLINE static void
-ctx_u8_associate_alpha (int components, uint8_t *u8)
+static void vtcmd_set_default_font (VT *vt, const char *sequence)
 {
-  switch (u8[components-1])
-  {
-          case 255:break;
-          case 0: 
-            for (int c = 0; c < components-1; c++)
-             u8[c] = 0;
-            break;
-          default:
-  for (int c = 0; c < components-1; c++)
-    u8[c] = (u8[c] * u8[components-1]) /255;
-  }
+  vt->charset[0] = 0;
 }
 
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
-static void
-ctx_gradient_cache_prime (CtxRasterizer *rasterizer)
+static void vtcmd_set_alternate_font (VT *vt, const char *sequence)
 {
-  if (ctx_gradient_cache_valid)
+  vt->charset[0] = 1;
+}
+
+int _ctx_set_frame (Ctx *ctx, int frame);
+int _ctx_frame (Ctx *ctx);
+
+static void vt_ctx_exit (void *data)
+{
+  VT *vt = data;
+  vt->state = vt_state_neutral;
+  vt->rev ++;
+  if (!vt->current_line)
     return;
-  for (int u = 0; u < CTX_GRADIENT_CACHE_ELEMENTS; u++)
+#if 0
+  fprintf (stderr, "\n");
+  if (vt->current_line->prev)
+  fprintf (stderr, "---prev(%i)----\n%s", (int)strlen(vt->current_line->prev),vt->current_line->prev);
+  fprintf (stderr, "---new(%i)----\n%s", 
(int)strlen(vt->current_line->frame->str),vt->current_line->frame->str);
+  fprintf (stderr, "--------\n");
+#endif
+
+#if CTX_VT_USE_FRAME_DIFF
+  if (vt->current_line->prev)
+    free (vt->current_line->prev);
+  vt->current_line->prev = NULL;
+  if (vt->current_line->frame)
   {
-    float v = u / (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f);
-    _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &ctx_gradient_cache_u8[u][0]);
-    //*((uint32_t*)(&ctx_gradient_cache_u8_a[u][0]))= *((uint32_t*)(&ctx_gradient_cache_u8[u][0]));
-    memcpy(&ctx_gradient_cache_u8_a[u][0], &ctx_gradient_cache_u8[u][0], 4);
-    ctx_RGBA8_associate_alpha (&ctx_gradient_cache_u8_a[u][0]);
+    vt->current_line->prev = vt->current_line->frame->str;
+    vt->current_line->prev_length = vt->current_line->frame->length;
+
+    ctx_string_free (vt->current_line->frame, 0);
+    vt->current_line->frame = NULL;
   }
-  ctx_gradient_cache_valid = 1;
+#endif
+
+  void *tmp = vt->current_line->ctx;
+  vt->current_line->ctx = vt->current_line->ctx_copy;
+  vt->current_line->ctx_copy = tmp;
+
+  _ctx_set_frame (vt->current_line->ctx, _ctx_frame (vt->current_line->ctx) + 1);
+  _ctx_set_frame (vt->current_line->ctx_copy, _ctx_frame (vt->current_line->ctx));
+#if 1
+  if (vt->ctxp) // XXX: ugly hack to aid double buffering
+    ((void**)vt->ctxp)[0]= vt->current_line->ctx;
+#endif
+
+  //ctx_parser_free (vt->ctxp);
+  //vt->ctxp = NULL;
 }
+#if 0
+#define CTX_x            CTX_STRH('x',0,0,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_lower_bottom CTX_STRH('l','o','w','e','r','-','b','o','t','t','o','m',0,0)
+#define CTX_lower        CTX_STRH('l','o','w','e','r',0,0,0,0,0,0,0,0,0)
+#define CTX_raise        CTX_STRH('r','a','i','s','e',0,0,0,0,0,0,0,0,0)
+#define CTX_raise_top    CTX_STRH('r','a','i','s','e','-','t','o','p',0,0,0,0,0)
+#define CTX_terminate    CTX_STRH('t','e','r','m','i','n','a','t','e',0,0,0,0,0)
+#define CTX_maximize     CTX_STRH('m','a','x','i','m','i','z','e',0,0,0,0,0,0)
+#define CTX_unmaximize   CTX_STRH('u','n','m','a','x','i','m','i','z','e',0,0,0,0)
+#define CTX_width        CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0)
+#define CTX_title        CTX_STRH('t','i','t','l','e',0,0,0,0,0,0,0,0,0)
+#define CTX_action       CTX_STRH('a','c','t','i','o','n',0,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)
 #endif
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+
+static int vt_get_prop (VT *vt, const char *key, const char **val, int *len)
 {
-  float v = x;
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
-  if (g->n_stops == 0)
-    {
-      rgba[0] = rgba[1] = rgba[2] = v * 255;
-      rgba[1] = 255;
-      return;
-    }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
-    {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
-    }
-  if (stop == NULL && next_stop)
-    {
-      color = & (next_stop->color);
-    }
-  else if (stop && next_stop == NULL)
-    {
-      color = & (stop->color);
-    }
-  else if (stop && next_stop)
-    {
-      uint8_t stop_rgba[4];
-      uint8_t next_rgba[4];
-      ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
-      for (int c = 0; c < 2; c++)
-        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
-      return;
-    }
-  else
-    {
-      color = & (g->stops[g->n_stops-1].color);
-    }
-  ctx_color_get_graya_u8 (rasterizer->state, color, rgba);
+#if 0
+  uint32_t key_hash = ctx_strhash (key);
+  char str[4096]="";
+  fprintf (stderr, "%s: %s %i\n", __FUNCTION__, key, key_hash);
+  CtxClient *client = ctx_client_by_id (ct->id);
+  if (!client)
+    return 0;
+  switch (key_hash)
+  {
+    case CTX_title:
+      sprintf (str, "setkey %s %s\n", key, client->title);
+      break;
+    case CTX_x:      
+      sprintf (str, "setkey %s %i\n", key, client->x);
+      break;
+    case CTX_y:    
+      sprintf (str, "setkey %s %i\n", key, client->y);
+      break;
+    case CTX_width:
+      sprintf (str, "setkey %s %i\n", key, client->width);
+      break;
+    case CTX_height:
+      sprintf (str, "setkey %s %i\n", key, client->width);
+      break;
+    default:
+      sprintf (str, "setkey %s undefined\n", key);
+      break;
+  }
+  if (str[0])
+  {
+    vtpty_write ((void*)ct, str, strlen (str));
+    fprintf (stderr, "%s", str);
+  }
+#endif
+  return 0;
 }
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba)
+static void vtcmd_set_mode (VT *vt, const char *sequence)
 {
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
-  if (g->n_stops == 0)
-    {
-      rgba[0] = rgba[1] = rgba[2] = v;
-      rgba[3] = 1.0;
-      return;
-    }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
-    {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
-    }
-  if (stop == NULL && next_stop)
-    {
-      color = & (next_stop->color);
-    }
-  else if (stop && next_stop == NULL)
-    {
-      color = & (stop->color);
-    }
-  else if (stop && next_stop)
+  int set = 1;
+  if (sequence[strlen (sequence)-1]=='l')
+    { set = 0; }
+  if (sequence[1]=='?')
     {
-      float stop_rgba[4];
-      float next_rgba[4];
-      ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) / (next_stop->pos - stop->pos);
-      for (int c = 0; c < 4; c++)
-        { rgba[c] = ctx_lerpf (stop_rgba[c], next_rgba[c], dx); }
-      return;
+      int qval;
+      sequence++;
+qagain:
+      qval = parse_int (sequence, 1);
+      switch (qval)
+        {
+          case 1: /*MODE;DECCKM;Cursor key mode;Application;Cursor;*/
+            vt->cursor_key_application = set;
+            break;
+          case 2: /*MODE;DECANM;VT52 emulation;on;off; */
+            if (set==0)
+              { vt->state = vt_state_vt52; }
+            break;
+          case 3: /*MODE;DECCOLM;Column mode;132 columns;80 columns;*/
+            vtcmd_set_132_col (vt, set);
+            break; // set 132 col
+          case 4: /*MODE;DECSCLM;Scrolling mode;smooth;jump;*/
+            vt->smooth_scroll = set;
+            break; // set 132 col
+          case 5: /*MODE;DECSCNM;Screen mode;Reverse;Normal;*/
+            vt->reverse_video = set;
+            break;
+          case 6: /*MODE;DECOM;Origin mode;Relative;Absolute;*/
+            vt->origin = set;
+            if (set)
+              {
+                _vt_move_to (vt, vt->margin_top, 1);
+                vt_carriage_return (vt);
+              }
+            else
+              { _vt_move_to (vt, 1, 1); }
+            break;
+          case 7: /*MODE;DECAWM;Autowrap;on;off;*/
+            vt->autowrap = set;
+            break;
+          case 8: /*MODE;DECARM;Auto repeat;on;off;*/
+            vt->keyrepeat = set;
+            break;
+          // case 9: // send mouse x & y on button press 
+
+          // 10 - Block DECEDM
+          // 18 - Print form feed  DECPFF  default off
+          // 19 - Print extent fullscreen DECPEX  default on
+          case 12:
+            vtcmd_ignore (vt, sequence);
+            break; // blinking_cursor
+          case 25:/*MODE;DECTCEM;Cursor visible;on;off; */
+            vt->cursor_visible = set;
+            break;
+          case 30: // from rxvt - show/hide scrollbar
+            break;
+          case 34: // DECRLM - right to left mode
+            break;
+          case 38: // DECTEK - enter tektronix mode
+            break;
+          case 60: // horizontal cursor coupling
+          case 61: // vertical cursor coupling
+            break;
+          case 69:/*MODE;DECVSSM;Left right margin mode;on;off; */
+            vt->left_right_margin_mode = set;
+            break;
+          case 80:/* DECSDM Sixel scrolling */
+            break;
+          case 437:/*MODE;;Encoding/cp437mode;cp437;utf8; */
+            vt->encoding = set ? 1 : 0;
+            break;
+          case 1000:/*MODE;;Mouse reporting;on;off;*/
+            vt->mouse = set;
+            break;
+          case 1001:
+          case 1002:/*MODE;;Mouse drag;on;off;*/
+            vt->mouse_drag = set;
+            break;
+          case 1003:/*MODE;;Mouse all;on;off;*/
+            vt->mouse_all = set;
+            break;
+          case 1006:/*MODE;;Mouse decimal;on;off;*/
+            vt->mouse_decimal = set;
+            break;
+          case 47:
+          case 1047:
+          //case 1048:
+          case 1049:/*MODE;;Alt screen;on;off;*/
+            if (set)
+              {
+                if (vt->in_alt_screen)
+                  {
+                  }
+                else
+                  {
+                    vtcmd_save_cursor (vt, "");
+                    vt->saved_lines = vt->lines;
+                    vt->saved_line_count = vt->line_count;
+                    vt->line_count = 0;
+                    vt->lines = NULL;
+                    for (int i = 0; i <  vt->rows; i++)
+                      {
+                        vt->current_line = vt_line_new_with_size ("", vt->cols);
+                        ctx_list_append (&vt->lines, vt->current_line);
+                        vt->line_count++;
+                      }
+                    vt->in_alt_screen = 1;
+                    vt_line_feed (vt);
+                    _vt_move_to (vt, 1, 1);
+                    vt_carriage_return (vt);
+                  }
+              }
+            else
+              {
+                if (vt->in_alt_screen)
+                  {
+                    while (vt->lines)
+                      {
+                        vt_line_free (vt->lines->data, 1);
+                        ctx_list_remove (&vt->lines, vt->lines->data);
+                      }
+                    vt->line_count = vt->saved_line_count;
+                    vt->lines = vt->saved_lines;
+                    vtcmd_restore_cursor (vt, "");
+                    vt->saved_lines = NULL;
+                    vt->in_alt_screen = 0;
+                  }
+                else
+                  {
+                  }
+              }
+            break; // alt screen
+          case 1010: /*MODE;;scroll on output;on;off; */ //rxvt
+            vt->scroll_on_output = set;
+            break;
+          case 1011:/*MODE:;scroll on input;on;off; */ //rxvt)
+            vt->scroll_on_input = set;
+            break;
+          case 2004:/*MODE;;bracketed paste;on;off; */
+            vt->bracket_paste = set;
+            break;
+          case 201:/*MODE;;ctx-events;on;off;*/
+            vt->ctx_events = set;
+            break;
+          
+          case 200:/*MODE;;ctx vector graphics mode;on;;*/
+            if (set)
+              {
+                if (!vt->current_line->ctx)
+                  {
+                    vt->current_line->ctx = ctx_new ();
+                    vt->current_line->ctx_copy = ctx_new ();
+                    ctx_set_texture_cache (vt->current_line->ctx_copy, vt->current_line->ctx);
+                    _ctx_set_transformation (vt->current_line->ctx, 0);
+                    _ctx_set_transformation (vt->current_line->ctx_copy, 0);
+
+                    //ctx_set_texture_cache (vt->current_line->ctx, vt->current_line->ctx_copy);
+                    //ctx_set_texture_cache (vt->current_line->ctx_copy, vt->current_line->ctx);
+#if CTX_VT_USE_FRAME_DIFF
+                    vt->current_line->frame = ctx_string_new ("");
+#endif
+                  }
+                if (vt->ctxp)
+                  ctx_parser_free (vt->ctxp);
+
+                vt->ctxp = ctx_parser_new (vt->current_line->ctx,
+                                           vt->cols * vt->cw, vt->rows * vt->ch,
+                                           vt->cw, vt->ch, vt->cursor_x, vt->cursor_y,
+                                           (void*)vt_set_prop, (void*)vt_get_prop, vt, vt_ctx_exit, vt);
+                vt->utf8_holding[vt->utf8_pos=0]=0; // XXX : needed?
+                vt->state = vt_state_ctx;
+              }
+            break;
+          default:
+            VT_warning ("unhandled CSI ? %i%s", qval, set?"h":"l");
+            return;
+        }
+      if (strchr (sequence + 1, ';') )
+        {
+          sequence = strchr (sequence + 1, ';');
+          goto qagain;
+        }
     }
   else
     {
-      color = & (g->stops[g->n_stops-1].color);
+      int val;
+again:
+      val = parse_int (sequence, 1);
+      switch (val)
+        {
+          case 1:/*GATM - transfer enhanced data */
+          case 2:/*KAM - keyboard action mode */
+            break;
+          case 3:/*CRM - control representation mode */
+            /* show control chars? */
+            break;
+          case 4:/*MODE2;IRM;Insert Mode;Insert;Replace; */
+            vt->insert_mode = set;
+            break;
+          case 9: /* interlace mode */
+            break;
+          case 12:/*MODE2;SRM;Local echo;on;off; */
+            vt->echo = set;
+            break;
+          case 20:/*MODE2;LNM;Carriage Return on LF/Newline;on;off;*/
+            ;
+            vt->cr_on_lf = set;
+            break;
+          case 21: // GRCM - whether SGR accumulates or a reset on each command
+            break;
+          case 32: // WYCRTSAVM - screen saver
+            break;
+          case 33: // WYSTCURM  - steady cursor
+            break;
+          case 34: // WYULCURM  - underline cursor
+            break;
+          default:
+            VT_warning ("unhandled CSI %ih", val);
+            return;
+        }
+      if (strchr (sequence, ';') && sequence[0] != ';')
+        {
+          sequence = strchr (sequence, ';');
+          goto again;
+        }
     }
-  ctx_color_get_rgba (rasterizer->state, color, rgba);
 }
-#endif
 
-static void
-ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+static void vtcmd_request_mode (VT *vt, const char *sequence)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
-
-  for (int i = 0; i < count; i ++)
-  {
-
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  int width = buffer->width;
-  int height = buffer->height;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
-    {
-      *((uint32_t*)(rgba)) = 0;
-    }
-  else
+  char buf[64]="";
+  if (sequence[1]=='?')
     {
-      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 < width)
-      {
-        src01 = src00 + bpp;
-      }
-      uint8_t *src11 = src01;
-      uint8_t *src10 = src00;
-      if ( v + 1 < 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)
+      int qval;
+      sequence++;
+      qval = parse_int (sequence, 1);
+      int is_set = -1; // -1 undefiend   0 reset 1 set  1000 perm_reset  1001 perm_set
+      switch (qval)
         {
           case 1:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[0]; }
-            rgba[3] = 255;
+            is_set = vt->cursor_key_application;
             break;
-          case 2:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[0]; }
-            rgba[3] = src[1];
+          case 2: /*VT52 emulation;;enable; */
+          //if (set==0) vt->in_vt52 = 1;
+            is_set = 1001;
             break;
           case 3:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[c]; }
-            rgba[3] = 255;
+            is_set = 0;
             break;
           case 4:
-            for (int c = 0; c < 4; c++)
-              { rgba[c] = src[c]; }
+            is_set = vt->smooth_scroll;
+            break;
+          case 5:
+            is_set = vt->reverse_video;
+            break;
+          case 6:
+            is_set = vt->origin;
+            break;
+          case 7:
+            is_set = vt->autowrap;
+            break;
+          case 8:
+            is_set = vt->keyrepeat;
+            break;
+          case 9: // should be dynamic
+            is_set = 1000;
+            break;
+          case 10:
+          case 11:
+          case 12:
+          case 13:
+          case 14:
+          case 16:
+          case 18:
+          case 19:
+            is_set = 1000;
+            break;
+          case 25:
+            is_set = vt->cursor_visible;
+            break;
+          case 45:
+            is_set = 1000;
+            break;
+          case 47:
+            is_set = vt->in_alt_screen;
+            break;
+          case 69:
+            is_set = vt->left_right_margin_mode;
+            break;
+          case 437:
+            is_set = vt->encoding;
+            break;
+          case 1000:
+            is_set = vt->mouse;
+            break;
+            // 1001 hilite tracking
+          case 1002:
+            is_set = vt->mouse_drag;
+            break;
+          case 1003:
+            is_set = vt->mouse_all;
+            break;
+          case 1006:
+            is_set = vt->mouse_decimal;
+            break;
+          case 201:
+            is_set = vt->ctx_events;
+            break;
+          case 2004:
+            is_set = vt->bracket_paste;
+            break;
+          case 1010: // scroll to bottom on tty output (rxvt)
+            is_set = vt->scroll_on_output;
+            break;
+          case 1011: // scroll to bottom on key press (rxvt)
+            is_set = vt->scroll_on_input;
+            break;
+          case 1049:
+            is_set = vt->in_alt_screen;
+            break;
+            break;
+          case 200:/*ctx protocol;On;;*/
+            is_set = (vt->state == vt_state_ctx);
+            break;
+          case 80:/* DECSDM Sixel scrolling */
+          case 30: // from rxvt - show/hide scrollbar
+          case 34: // DECRLM - right to left mode
+          case 60: // horizontal cursor coupling
+          case 61: // vertical cursor coupling
+          default:
             break;
         }
-      }
-      if (rasterizer->swap_red_green)
+      switch (is_set)
       {
-        uint8_t tmp = rgba[0];
-        rgba[0] = rgba[2];
-        rgba[2] = tmp;
+        case 0:
+          sprintf (buf, "\033[?%i;%i$y", qval, 2);
+          break;
+        case 1:
+          { sprintf (buf, "\033[?%i;%i$y", qval, 1); }
+          break;
+        case 1000:
+          sprintf (buf, "\033[?%i;%i$y", qval, 4);
+          break;
+        case 1001:
+          sprintf (buf, "\033[?%i;%i$y", qval, 3);
+          break;
+        case -1:
+          { sprintf (buf, "\033[?%i;%i$y", qval, 0); }
       }
     }
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-}
-
-#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;
+  else
+    {
+      int val;
+      val = parse_int (sequence, 1);
+      switch (val)
+        {
+          case 1:
+            sprintf (buf, "\033[%i;%i$y", val, 0);
+            break;
+          case 2:/* AM - keyboard action mode */
+            sprintf (buf, "\033[%i;%i$y", val, 0);
+            break;
+          case 3:/*CRM - control representation mode */
+            sprintf (buf, "\033[%i;%i$y", val, 0);
+            break;
+          case 4:/*Insert Mode;Insert;Replace; */
+            sprintf (buf, "\033[%i;%i$y", val, vt->insert_mode?1:2);
+            break;
+          case 9: /* interlace mode */
+            sprintf (buf, "\033[%i;%i$y", val, 1);
+            break;
+          case 12:
+            sprintf (buf, "\033[%i;%i$y", val, vt->echo?1:2);
+            break;
+          case 20:/*Carriage Return on LF/Newline;on;off;*/
+            ;
+            sprintf (buf, "\033[%i;%i$y", val, vt->cr_on_lf?1:2);
+            break;
+          case 21: // GRCM - whether SGR accumulates or a reset on each command
+          default:
+            sprintf (buf, "\033[%i;%i$y", val, 0);
+        }
+    }
+  if (buf[0])
+    { vt_write (vt, buf, strlen (buf) ); }
 }
 
-inline static void
-ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+static void vtcmd_set_t (VT *vt, const char *sequence)
 {
-  if (dither_red_blue == 0)
-    { return; }
-  for (int c = 0; c < 3; c ++)
+  /* \e[21y is request title - allows inserting keychars */
+  if      (!strcmp (sequence,  "[1t")) { ctx_client_unshade (vt->id); }
+  else if (!strcmp (sequence,  "[2t")) { ctx_client_shade (vt->id); } 
+  else if (!strncmp (sequence, "[3;", 3)) {
+    int x=0,y=0;
+    sscanf (sequence, "[3;%i;%ir", &y, &x);
+    ctx_client_move (vt->id, x, y);
+  }
+  else if (!strncmp (sequence, "[4;", 3))
+  {
+    int width = 0, height = 0;
+    sscanf (sequence, "[4;%i;%ir", &height , &width);
+    if (width < 0) width = vt->cols * vt->cw;
+    if (height < 0) height = vt->rows * vt->ch;
+    if (width == 0) width = ctx_width (vt->root_ctx);
+    if (height == 0) height = ctx_height (vt->root_ctx);
+    ctx_client_resize (vt->id, width, height);
+  }
+  else if (!strcmp (sequence, "[5t") ) { ctx_client_raise_top (vt->id); } 
+  else if (!strcmp (sequence, "[6t") ) { ctx_client_lower_bottom (vt->id); } 
+  else if (!strcmp (sequence, "[7t") ) { vt->rev++; /* refresh */ }
+  else if (!strncmp (sequence, "[8;", 3) )
+  {
+    int cols = 0, rows = 0;
+    sscanf (sequence, "[8;%i;%ir", &rows, &cols);
+    if (cols < 0) cols = vt->cols;
+    if (rows < 0) rows = vt->rows;
+    if (cols == 0) cols = ctx_width (vt->root_ctx) / vt->cw;
+    if (rows == 0) rows = ctx_height (vt->root_ctx) / vt->ch;
+    ctx_client_resize (vt->id, cols * vt->cw, rows * vt->ch);
+  }
+  else if (!strcmp (sequence, "[9;0t") ) { ctx_client_unmaximize (vt->id); } 
+  else if (!strcmp (sequence, "[9;1t") ) { ctx_client_maximize (vt->id);} 
+
+  /* should actually be full-screen */
+  else if (!strcmp (sequence, "[10;0t") ) { ctx_client_unmaximize (vt->id); } 
+  else if (!strcmp (sequence, "[10;1t") ) { ctx_client_maximize (vt->id);} 
+  else if (!strcmp (sequence, "[10;2t") ) { ctx_client_toggle_maximized (vt->id);} 
+
+  else if (!strcmp (sequence, "[11t") )  /* report window state  */
     {
-      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);
+      char buf[128];
+      if (ctx_client_is_iconified (vt->id))
+        sprintf (buf, "\033[2t");
+      else
+        sprintf (buf, "\033[1t");
+      vt_write (vt, buf, strlen (buf) );
     }
-}
-
-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 ++)
+  else if (!strcmp (sequence, "[13t") ) /* request terminal position */
     {
-      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue);
-      rgba[c] = CTX_CLAMP (val, 0, 255);
+      char buf[128];
+      sprintf (buf, "\033[3;%i;%it", ctx_client_y (vt->id), ctx_client_x (vt->id));
+      vt_write (vt, buf, strlen (buf) );
     }
-}
-#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)
+  else if (!strcmp (sequence, "[14t") ) /* request terminal dimensions in px */
     {
-    if (a ==255)
+      char buf[128];
+      sprintf (buf, "\033[4;%i;%it", vt->rows * vt->ch, vt->cols * vt->cw);
+      vt_write (vt, buf, strlen (buf) );
+    }
+  else if (!strcmp (sequence, "[15t") ) /* request root dimensions in px */
     {
-      *((uint32_t*)(out)) = val;
-    } else
+      char buf[128];
+      sprintf (buf, "\033[5;%i;%it", ctx_height (vt->root_ctx), ctx_width(vt->root_ctx));
+      vt_write (vt, buf, strlen (buf) );
+    }
+  else if (!strcmp (sequence, "[16t") ) /* request char dimensions in px */
     {
-      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);
+      char buf[128];
+      sprintf (buf, "\033[6;%i;%it", vt->ch, vt->cw);
+      vt_write (vt, buf, strlen (buf) );
     }
+  else if (!strcmp (sequence, "[18t") ) /* request terminal dimensions */
+    {
+      char buf[128];
+      sprintf (buf, "\033[8;%i;%it", vt->rows, vt->cols);
+      vt_write (vt, buf, strlen (buf) );
     }
-    else
+  else if (!strcmp (sequence, "[19t") ) /* request root window size in char */
     {
-      *((uint32_t*)(out)) = 0;
+      char buf[128];
+      sprintf (buf, "\033[9;%i;%it", ctx_height(vt->root_ctx)/vt->ch, ctx_width (vt->root_ctx)/vt->cw);
+      vt_write (vt, buf, strlen (buf) );
     }
-}
 
-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];
-  }
+#if 0
+  {"[", 's',  foo, VT100}, /*args:PnSP id:DECSWBV Set warning bell volume */
+#endif
+  else if (sequence[strlen (sequence)-2]==' ') /* DECSWBV */
+    {
+      int val = parse_int (sequence, 0);
+      if (val <= 1) { vt->bell = 0; }
+      if (val <= 8) { vt->bell = val; }
+    }
   else
-  {
-  for (int c = 0; c < components; c++)
-    out[c] = 0;
-  }
+    {
+      // XXX: X for ints >=24 resize to that number of lines
+      VT_info ("unhandled subsequence %s", sequence);
+    }
 }
 
-CTX_INLINE static void
-ctx_float_associate_alpha (int components, float *rgba)
+static void _vt_htab (VT *vt)
 {
-  float alpha = rgba[components-1];
-  for (int c = 0; c < components-1; c++)
-    rgba[c] *= alpha;
+  do
+    {
+      vt->cursor_x ++;
+    }
+  while (vt->cursor_x < VT_MARGIN_RIGHT &&  ! vt->tabs[ (int) vt->cursor_x-1]);
+  if (vt->cursor_x > VT_MARGIN_RIGHT)
+    { vt->cursor_x = VT_MARGIN_RIGHT; }
 }
 
-CTX_INLINE static void
-ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
+static void _vt_rev_htab (VT *vt)
 {
-  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];
+  do
+    {
+      vt->cursor_x--;
+    }
+  while ( ! vt->tabs[ (int) vt->cursor_x-1] && vt->cursor_x > 1);
+  if (vt->cursor_x < VT_MARGIN_LEFT)
+    { vt_carriage_return (vt); }
 }
 
-CTX_INLINE static void
-ctx_RGBAF_associate_alpha (float *rgba)
+static void vtcmd_insert_n_tabs (VT *vt, const char *sequence)
 {
-  ctx_float_associate_alpha (4, rgba);
+  int n = parse_int (sequence, 1);
+  while (n--)
+    {
+      _vt_htab (vt);
+    }
 }
 
-CTX_INLINE static void
-ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
+static void vtcmd_rev_n_tabs (VT *vt, const char *sequence)
 {
-  ctx_float_deassociate_alpha (4, rgba, dst);
+  int n = parse_int (sequence, 1);
+  while (n--)
+    {
+      _vt_rev_htab (vt);
+    }
 }
 
-
-static inline void ctx_swap_red_green_u8 (void *data)
+static void vtcmd_set_double_width_double_height_top_line
+(VT *vt, const char *sequence)
 {
-  uint8_t *rgba = (uint8_t*)data;
-  uint8_t tmp = rgba[0];
-  rgba[0] = rgba[2];
-  rgba[2] = tmp;
+  vt->current_line->double_width         = 1;
+  vt->current_line->double_height_top    = 1;
+  vt->current_line->double_height_bottom = 0;
 }
-
-static void
-ctx_fragment_swap_red_green_u8 (void *out, int count)
+static void vtcmd_set_double_width_double_height_bottom_line
+(VT *vt, const char *sequence)
 {
-  uint8_t *rgba = (uint8_t*)out;
-  for (int x = 0; x < count; x++)
-  {
-    ctx_swap_red_green_u8 (rgba);
-    rgba += 4;
-  }
+  vt->current_line->double_width         = 1;
+  vt->current_line->double_height_top    = 0;
+  vt->current_line->double_height_bottom = 1;
 }
-
-/**** rgb8 ***/
-
-static void
-ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer,
-                                   float x,
-                                   float y,
-                                   void *out, int count, float dx, float dy)
+static void vtcmd_set_single_width_single_height_line
+(VT *vt, const char *sequence)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int width = buffer->width;
-  int height = buffer->height;
-
-  for (int i = 0; i < count; i++)
-  {
-
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
-    {
-      *((uint32_t*)(rgba))= 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) * width + (u + ou);
-
-            if (o>=0 && o < width * 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;
-    }
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
-#endif
+  vt->current_line->double_width         = 0;
+  vt->current_line->double_height_top    = 0;
+  vt->current_line->double_height_bottom = 0;
 }
-
 static void
-ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
+vtcmd_set_double_width_single_height_line
+(VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  vt->current_line->double_width         = 1;
+  vt->current_line->double_height_top    = 0;
+  vt->current_line->double_height_bottom = 0;
 }
 
-static void
-ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
+static void vtcmd_set_led (VT *vt, const char *sequence)
 {
-  uint8_t *rgba = (uint8_t *) out;
-
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int width = buffer->width;
-  int height = buffer->height;
-
-  for (int i = 0; i < count; i++)
-  {
-
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
-    {
-      *((uint32_t*)(rgba))= 0;
-    }
-  else
+  int val = 0;
+  //fprintf (stderr, "%s\n", sequence);
+  for (const char *s = sequence; *s; s++)
     {
-      int bpp = 3;
-      rgba[3]=255;
-      uint8_t *src00 = (uint8_t *) buffer->data;
-      int stride = buffer->stride;
-      src00 += v * stride + u * bpp;
-      uint8_t *src01 = src00;
-      if ( u + 1 < width)
-      {
-        src01 = src00 + bpp;
-      }
-      uint8_t *src11 = src01;
-      uint8_t *src10 = src00;
-      if ( v + 1 < height)
-      {
-        src10 = src00 + stride;
-        src11 = src01 + stride;
-      }
-      float dx = (x-(int)(x)) * 255.9f;
-      float dy = (y-(int)(y)) * 255.9f;
-      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);
-      }
+      switch (*s)
+        {
+          case '0': val = 0; break;
+          case '1': val = 1; break;
+          case '2': val = 2; break;
+          case '3': val = 3; break;
+          case '4': val = 4; break;
+          case ';':
+          case 'q':
+            if (val == 0)
+              { vt->leds[0] = vt->leds[1] = vt->leds[2] = vt->leds[3] = 0; }
+            else
+              { vt->leds[val-1] = 1; }
+            val = 0;
+            break;
+        }
     }
-    x += dx;
-    y += dy;
-    rgba += 4;
-  }
-#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_swap_red_green (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
+static void vtcmd_char_at_cursor (VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  char *buf="";
+  vt_write (vt, buf, strlen (buf) );
 }
 
-static CTX_INLINE void
-ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer,
-                                       float x,
-                                       float y,
-                                       void *out, int count, float dx, float dy)
+static void vtcmd_DECELR (VT *vt, const char *sequence)
 {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  uint8_t *rgba = (uint8_t *) out;
-  uint8_t *src = (uint8_t *) buffer->data;
-
-  x += 0.5f;
-  y += 0.5f;
-
-  if (dy == 0.0f && dx > 0.999f && dx < 1.001f)
-  {
-    int v = y - g->texture.y0;
-    int u = x - g->texture.x0;
-  
-    if (v < buffer->height && v > 0)
-    {
-      int o = v * buffer->stride + u * 3;
-      int i;
-      for (i = 0; i < count && u < buffer->width; i++)
-      {
-        if (CTX_UNLIKELY(u < 0))
-          {
-            *((uint32_t*)(rgba))= 0;
-          }
-        else
-          {
-            rgba[0] = src[o];
-            rgba[1] = src[o+1];
-            rgba[2] = src[o+2]; 
-            rgba[3]=255;
-          }
-        rgba += 4;
-        o += 3;
-        u+=1;
-      }
-      for (; i < count; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba += 4;
-      }
-    }
-    else
-    {
-      for (int i = 0; i < count; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba+=4;
-      }
-    }
-  }
+  int ps1 = parse_int (sequence, 0);
+  int ps2 = 0;
+  const char *s = strchr (sequence, ';');
+  if (ps1) {/* unused */};
+  if (s)
+    { ps2 = parse_int (s, 0); }
+  if (ps2 == 1)
+    { vt->unit_pixels = 1; }
   else
-  {
-    for (int i = 0; i < count; i++)
-    {
-  
-    int u = x - g->texture.x0;
-    int v = y - g->texture.y0;
-    if (CTX_UNLIKELY( u < 0 || v < 0 ||
-         u >= buffer->width ||
-         v >= buffer->height))
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-    else
-      {
-        int      o = v * buffer->stride + u * 3;
-        uint8_t *src00 = (uint8_t *) buffer->data;
-        rgba[0] = src00[o];
-        rgba[1] = src00[o+1];
-        rgba[2] = src00[o+2]; 
-        rgba[3]=255;
-      }
-  
-      rgba += 4;
-      x += dx;
-      y += dy;
-    }
-  }
-#if CTX_DITHER
-  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-  //                    rasterizer->format->dither_green);
-#endif
+    { vt->unit_pixels = 0; }
 }
 
-
-static CTX_INLINE void
-ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
-                                                      float x,
-                                                      float y,
-                                                      void *out, int count, float dx, float dy)
+static void vtcmd_graphics (VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  fprintf (stderr, "gfx intro [%s]\n",sequence); // maybe implement such as well?
 }
 
-static void
-ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer,
-                               float x,
-                               float y,
-                               void *out, int count, float dx, float dy)
+static void vtcmd_report (VT *vt, const char *sequence)
 {
-  if (rasterizer->state->gstate.image_smoothing)
-  {
-    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-    if (factor <= 0.50f)
-    {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-    }
-    else if (factor > 0.99f && factor < 1.01f)
+  char buf[64]="";
+  if (!strcmp (sequence, "[5n") ) // DSR device status report
     {
-      // XXX missing translate test
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+      sprintf (buf, "\033[0n"); // we're always OK :)
     }
-    else
+  else if (!strcmp (sequence, "[?15n") ) // printer status
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+      sprintf (buf, "\033[?13n"); // no printer
     }
-  }
-  else
-  {
-    if (rasterizer->swap_red_green)
-      ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-    else
-      ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  }
-#if CTX_DITHER
-  {
-  uint8_t *rgba = (uint8_t*)out;
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-  }
-#endif
-}
-
-
-/************** rgba8 */
-
-static void
-ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-
-  for (int i = 0; i < count; i ++)
-  {
-
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  if ( u < 0 || v < 0 ||
-       u >= buffer->width ||
-       v >= buffer->height)
+  else if (!strcmp (sequence, "[?26n") ) // keyboard dialect
     {
-      *((uint32_t*)(rgba))= 0;
+      sprintf (buf, "\033[?27;1n"); // north american/ascii
     }
-  else
+  else if (!strcmp (sequence, "[?25n") ) // User Defined Key status
     {
-      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;
-          int width = buffer->width;
-          int height = buffer->height;
-          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) * width + (u + ou);
-
-            if (o>=0 && o < width * 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;
+      sprintf (buf, "\033[?21n"); // locked
     }
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
+#if 0
+  {"[6n", 0, },  /* id:DSR  cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */
 #endif
-}
-
-static void
-ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
-                                   float x,
-                                   float y,
-                                   void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-
-  for (int i = 0; i < count; i ++)
-  {
-
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  if ( u < 0 || v < 0 ||
-       u >= buffer->width ||
-       v >= buffer->height)
+  else if (!strcmp (sequence, "[6n") ) // DSR cursor position report
     {
-      *((uint32_t*)(rgba))= 0;
+      sprintf (buf, "\033[%i;%iR", vt->cursor_y - (vt->origin? (vt->margin_top - 1) :0), (int) vt->cursor_x 
- (vt->origin? (VT_MARGIN_LEFT-1) :0) );
     }
-  else
+  else if (!strcmp (sequence, "[?6n") ) // DECXPR extended cursor position report
     {
-      int bpp = 4;
-      uint8_t *src00 = (uint8_t *) buffer->data;
-      int stride = buffer->stride;
-      src00 += v * 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 + stride;
-        src11 = src01 + 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);
-      }
-    }
-
-
-    x += dx;
-    y += dy;
-    rgba += 4;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
+#if 0
+  {"[?6n", 0, },  /* id:DEXCPR  extended cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */
 #endif
-}
-
-static void
-ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
-                                        float x,
-                                        float y,
-                                        void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int bwidth = buffer->width;
-  int bheight = buffer->height;
-  x += 0.5f;
-  y += 0.5f;
-
-  if (dy == 0.0f && dx > 0.999f && dx < 1.001f)
-  {
-    int u = x - g->texture.x0;
-    int v = y - g->texture.y0;
-    if (u >= 0 && v >= 0 && u + count < bwidth && v < bheight)
+      sprintf (buf, "\033[?%i;%i;1R", vt->cursor_y - (vt->origin? (vt->margin_top - 1) :0), (int) 
vt->cursor_x - (vt->origin? (VT_MARGIN_LEFT-1) :0) );
+    }
+  else if (!strcmp (sequence, "[>c") )
     {
-      uint32_t *src = (uint32_t *) buffer->data;
-      memcpy (out, &src[v * bwidth + u], count * 4);
-      return;
+      sprintf (buf, "\033[>23;01;1c");
     }
-  }
-  //else
-  {
-    for (int i = 0; i < count; i ++)
+  else if (sequence[strlen (sequence)-1]=='c') // device attributes
     {
-  
-    int u = x - g->texture.x0;
-    int v = y - g->texture.y0;
-    if ( u < 0 || v < 0 ||
-         u >= bwidth ||
-         v >= bheight)
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-    else
-      {
-        int      i = (v * bwidth + u);
-        uint32_t *src = (uint32_t *) buffer->data;
-        *((uint32_t*)(rgba))= src[i];
-      }
-  
-      x += dx;
-      y += dy;
-      rgba += 4;
+      //buf = "\033[?1;2c"; // what rxvt reports
+      //buf = "\033[?1;6c"; // VT100 with AVO ang GPO
+      //buf = "\033[?2c";     // VT102
+      sprintf (buf, "\033[?63;14;4;22c");
     }
-  }
-#if CTX_DITHER
-  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-  //                    rasterizer->format->dither_green);
-#endif
+  else if (sequence[strlen (sequence)-1]=='x') // terminal parameters
+    {
+      if (!strcmp (sequence, "[1x") )
+        { sprintf (buf, "\033[3;1;1;120;120;1;0x"); }
+      else
+        { sprintf (buf, "\033[2;1;1;120;120;1;0x"); }
+    }
+  if (buf[0])
+    { vt_write (vt, buf, strlen (buf) ); }
+}
+
+static char *charmap_cp437[]=
+{
+  " ","☺","☻","♥","♦","♣","♠","•","◘","○","◙","♂","♀","♪","♫","☼",
+  "►","◄","↕","‼","¶","§","▬","↨","↑","↓","→","←","∟","↔","▲","▼",
+  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/",
+  "0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
+  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O",
+  "P","Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
+  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
+  "p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","⌂",
+  "Ç","ü","é","â","ä","à","å","ç","ê","ë","è","ï","î","ì","Ä","Å",
+  "É","æ","Æ","ô","ö","ò","û","ù","ÿ","Ö","Ü","¢","£","¥","₧","ƒ",
+  "á","í","ó","ú","ñ","Ñ","ª","º","¿","⌐","¬","½","¼","¡","«","»",
+  "░","▒","▓","│","┤","╡","╢","╖","╕","╣","║","╗","╝","╜","╛","┐",
+  "└","┴","┬","├","─","┼","╞","╟","╚","╔","╩","╦","╠","═","╬","╧",
+  "╨","╤","╥","╙","╘","╒","╓","╫","╪","┘","┌","█","▄","▌","▐","▀",
+  "α","ß","Γ","π","Σ","σ","µ","τ","Φ","Θ","Ω","δ","∞","φ","ε","∩",
+  "≡","±","≥","≤","⌠","⌡","÷","≈","°","∙","·","√","ⁿ","²","■"," "
+};
 
 
-#if CTX_DITHER
-  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-  //                    rasterizer->format->dither_green);
-#endif
-}
+static char *charmap_graphics[]=
+{
+  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0",
+  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
+  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
+  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
+  "◆","▒","␉","␌","␍","␊","°","±","␤","␋","┘","┐","┌","└","┼","⎺","⎻",
+  "─","⎼","⎽","├","┤","┴","┬","│","≤","≥","π","≠","£","·"," "
+};
 
-static void
-ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
+static char *charmap_uk[]=
+{
+  " ","!","\"","£","$","%","&","'","(",")","*","+",",","-",".","/","0",
+  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
+  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
+  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
+  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
+  "q","r","s","t","u","v","w","x","y","z","{","|","}","~"," "
+};
+
+static char *charmap_ascii[]=
+{
+  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0",
+  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
+  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
+  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
+  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
+  "q","r","s","t","u","v","w","x","y","z","{","|","}","~"," "
+};
+
+static void vtcmd_justify (VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  int n = parse_int (vt->argument_buf, 0);
+  switch (n)
+    {
+      case 0:
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+        vt->justify = n;
+        break;
+      default:
+        vt->justify = 0;
+    }
 }
 
-static void
-ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
+static void vtcmd_sixel_related_req (VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  fprintf (stderr, "it happens!\n");
 }
 
-static void
-ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
+static void vtcmd_set_charmap (VT *vt, const char *sequence)
 {
-  ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  int slot = 0;
+  int set = sequence[1];
+  if (sequence[0] == ')') { slot = 1; }
+  if (set == 'G') { set = 'B'; }
+  vt->charset[slot] = set;
 }
+#if 0
 
-static void
-ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer,
-                                float x,
-                                float y,
-                                void *out, int count, float dx, float dy)
-{
-  if (rasterizer->state->gstate.image_smoothing)
+CSI Pm ' }    '
+Insert Ps Column (s) (default = 1) (DECIC), VT420 and up.
+
+CSI Pm ' ~    '
+Delete Ps Column (s) (default = 1) (DECDC), VT420 and up.
+
+
+in text.  When bracketed paste mode is set, the program will receive:
+ESC [ 2 0 0 ~,
+      followed by the pasted text, followed by
+      ESC [ 2 0 1 ~ .
+
+
+            CSI I
+            when the terminal gains focus, and CSI O  when it loses focus.
+#endif
+
+#define COMPAT_FLAG_LEVEL_0   (1<<1)
+#define COMPAT_FLAG_LEVEL_1   (1<<2)
+#define COMPAT_FLAG_LEVEL_2   (1<<3)
+#define COMPAT_FLAG_LEVEL_3   (1<<4)
+#define COMPAT_FLAG_LEVEL_4   (1<<5)
+#define COMPAT_FLAG_LEVEL_5   (1<<6)
+
+#define COMPAT_FLAG_LEVEL_102 (1<<7)
+
+#define COMPAT_FLAG_ANSI      (1<<8)
+#define COMPAT_FLAG_OBSOLETE  (1<<9)
+
+#define COMPAT_FLAG_ANSI_COLOR (1<<10)
+#define COMPAT_FLAG_256_COLOR  (1<<11)
+#define COMPAT_FLAG_24_COLOR   (1<<12)
+
+#define COMPAT_FLAG_MOUSE_REPORT (1<<13)
+
+#define COMPAT_FLAG_AUDIO      (1<<14)
+#define COMPAT_FLAG_GRAPHICS   (1<<15)
+
+#define ANSI    COMPAT_FLAG_ANSI
+#define OBS     COMPAT_FLAG_OBSOLETE
+
+#define VT100   (COMPAT_FLAG_LEVEL_0|COMPAT_FLAG_LEVEL_1)
+#define VT102   (VT100|COMPAT_FLAG_LEVEL_102)
+#define VT200   (VT102|COMPAT_FLAG_LEVEL_2)
+#define VT300   (VT200|COMPAT_FLAG_LEVEL_3)
+#define VT400   (VT300|COMPAT_FLAG_LEVEL_4)
+#define VT220   VT200
+#define VT320   VT300
+
+#define XTERM   (VT400|COMPAT_FLAG_24_COLOR|COMPAT_FLAG_256_COLOR|COMPAT_FLAG_ANSI_COLOR)
+
+#define VT2020  (XTERM|COMPAT_FLAG_GRAPHICS|COMPAT_FLAG_AUDIO)
+
+
+            static Sequence sequences[]=
   {
-    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-    if (factor <= 0.50f)
-    {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-    }
-    else if (factor > 0.99f && factor < 1.01f)
-    {
-      // XXX: also verify translate == 0 for this fast path to be valid
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-    }
-    else
+    /*
+      prefix suffix  command */
+    //{"B",  0,  vtcmd_break_permitted},
+    //{"C",  0,  vtcmd_nobreak_here},
+    {"D", 0,    vtcmd_index, VT100}, /* args: id:IND Index  */
+    {"E",  0,   vtcmd_next_line}, /* ref:none id:  Next line */
+    {"_", 'G',  vtcmd_graphics},
+    {"H",   0,  vtcmd_horizontal_tab_set, VT100}, /* id:HTS Horizontal Tab Set */
+
+    //{"I",  0,  vtcmd_char_tabulation_with_justification},
+    //{"K",  0,  PLD partial line down
+    //{"L",  0,  PLU partial line up
+    {"M",  0,   vtcmd_reverse_index, VT100}, /* ref:none id:RI Reverse Index */
+    //{"N",  0,  vtcmd_ignore}, /* Set Single Shift 2 - SS2*/
+    //{"O",  0,  vtcmd_ignore}, /* Set Single Shift 3 - SS3*/
+
+#if 0
+    {"[0F", 0, vtcmd_justify, ANSI}, /* ref:none id:JFY disable justification and wordwrap  */ // needs 
special link to ANSI standard
+    {"[1F", 0, vtcmd_justify, ANSI}, /* ref:none id:JFY enable wordwrap  */
+#endif
+
+    /* these need to occur before vtcmd_preceding_line to have precedence */
+    {"[0 F", 0, vtcmd_justify, ANSI},
+    {"[1 F", 0, vtcmd_justify, ANSI},
+    {"[2 F", 0, vtcmd_justify},
+    {"[3 F", 0, vtcmd_justify},
+    {"[4 F", 0, vtcmd_justify},
+    {"[5 F", 0, vtcmd_justify},
+    {"[6 F", 0, vtcmd_justify},
+    {"[7 F", 0, vtcmd_justify},
+    {"[8 F", 0, vtcmd_justify},
+// XXX missing DECIC DECDC  insert and delete column
+    {"[", 'A', vtcmd_cursor_up, VT100},   /* args:Pn    id:CUU Cursor Up */
+    {"[",  'B', vtcmd_cursor_down, VT100}, /* args:Pn    id:CUD Cursor Down */
+    {"[",  'C', vtcmd_cursor_forward, VT100}, /* args:Pn id:CUF Cursor Forward */
+    {"[",  'D', vtcmd_cursor_backward, VT100}, /* args:Pn id:CUB Cursor Backward */
+    {"[",  'j', vtcmd_cursor_backward, ANSI}, /* args:Pn ref:none id:HPB Horizontal Position Backward */
+    {"[",  'k', vtcmd_cursor_up, ANSI}, /* args:Pn ref:none id:VPB Vertical Position Backward */
+    {"[",  'E', vtcmd_next_line, VT100}, /* args:Pn id:CNL Cursor Next Line */
+    {"[",  'F', vtcmd_cursor_preceding_line, VT100}, /* args:Pn id:CPL Cursor Preceding Line */
+    {"[",  'G', vtcmd_horizontal_position_absolute}, /* args:Pn id:CHA Cursor Horizontal Absolute */
+    {"[",  'H', vtcmd_cursor_position, VT100}, /* args:Pl;Pc id:CUP Cursor Position */
+    {"[",  'I', vtcmd_insert_n_tabs}, /* args:Pn id:CHT Cursor Horizontal Forward Tabulation */
+    {"[",  'J', vtcmd_erase_in_display, VT100}, /* args:Ps id:ED Erase in Display */
+    {"[",  'K', vtcmd_erase_in_line, VT100}, /* args:Ps id:EL Erase in Line */
+    {"[",  'L', vtcmd_insert_blank_lines, VT102}, /* args:Pn id:IL Insert Line */
+    {"[",  'M', vtcmd_delete_n_lines, VT102}, /* args:Pn id:DL Delete Line   */
+    // [ N is EA - Erase in field
+    // [ O is EA - Erase in area
+    {"[",  'P', vtcmd_delete_n_chars, VT102}, /* args:Pn id:DCH Delete Character */
+    // [ Q is SEE - Set editing extent
+    // [ R is CPR - active cursor position report
+    {"[?", 'S', vtcmd_sixel_related_req},
+    {"[",  'S', vtcmd_scroll_up, VT100},   /* args:Pn id:SU Scroll Up */
+    {"[",  'T', vtcmd_scroll_down, VT100}, /* args:Pn id:SD Scroll Down */
+    {"[",/*SP*/'U', vtcmd_set_line_home, ANSI}, /* args:PnSP id=SLH Set Line Home */
+    {"[",/*SP*/'V', vtcmd_set_line_limit, ANSI},/* args:PnSP id=SLL Set Line Limit */
+    // [ W is cursor tabulation control
+    // [ Pn Y  - cursor line tabulation
+    //
+    {"[",  'X', vtcmd_erase_n_chars}, /* args:Pn id:ECH Erase Character */
+    {"[",  'Z', vtcmd_rev_n_tabs},    /* args:Pn id:CBT Cursor Backward Tabulation */
+    {"[",  '^', vtcmd_scroll_down}  , /* muphry alternate from ECMA */
+    {"[",  '@', vtcmd_insert_character, VT102}, /* args:Pn id:ICH Insert Character */
+
+    {"[",  'a', vtcmd_cursor_forward, ANSI}, /* args:Pn id:HPR Horizontal Position Relative */
+    {"[",  'b', vtcmd_cursor_forward, ANSI}, /* REP previous char XXX incomplete */
+    {"[",  'c', vtcmd_report}, /* ref:none id:DA args:... Device Attributes */
+    {"[",  'd', vtcmd_goto_row},       /* args:Pn id:VPA Vertical Position Absolute  */
+    {"[",  'e', vtcmd_cursor_down},    /* args:Pn id:VPR Vertical Position Relative */
+    {"[",  'f', vtcmd_cursor_position, VT100}, /* args:Pl;Pc id:HVP Cursor Position */
+    {"[g", 0,   vtcmd_clear_current_tab, VT100}, /* id:TBC clear current tab */
+    {"[0g", 0,  vtcmd_clear_current_tab, VT100}, /* id:TBC clear current tab */
+    {"[3g", 0,  vtcmd_clear_all_tabs, VT100},    /* id:TBC clear all tabs */
+    {"[",  'm', vtcmd_set_graphics_rendition, VT100}, /* args:Ps;Ps;.. id:SGR Select Graphics Rendition */
+    {"[",  'n', vtcmd_report, VT200}, /* id:DSR args:... CPR Cursor Position Report  */
+    {"[",  'r', vtcmd_set_top_and_bottom_margins, VT100}, /* args:Pt;Pb id:DECSTBM Set Top and Bottom 
Margins */
+#if 0
+    // handled by set_left_and_right_margins - in if 0 to be documented
+    {"[s",  0,  vtcmd_save_cursor_position, VT100}, /*ref:none id:SCP Save Cursor Position */
+#endif
+    {"[u",  0,  vtcmd_restore_cursor_position, VT100}, /*ref:none id:RCP Restore Cursor Position */
+    {"[",  's', vtcmd_set_left_and_right_margins, VT400}, /* args:Pl;Pr id:DECSLRM Set Left and Right 
Margins */
+    {"[",  '`', vtcmd_horizontal_position_absolute, ANSI},  /* args:Pn id:HPA Horizontal Position Absolute */
+
+    {"[",  'h', vtcmd_set_mode, VT100},   /* args:Pn[;...] id:SM Set Mode */
+    {"[",  'l', vtcmd_set_mode, VT100}, /* args:Pn[;...]  id:RM Reset Mode */
+    {"[",  't', vtcmd_set_t},
+    {"[",  'q', vtcmd_set_led, VT100}, /* args:Ps id:DECLL Load LEDs */
+    {"[",  'x', vtcmd_report}, /* ref:none id:DECREQTPARM */
+    {"[",  'z', vtcmd_DECELR}, /* ref:none id:DECELR set locator res  */
+
+    {"5",   0,  vtcmd_char_at_cursor, VT300}, /* ref:none id:DECXMIT */
+    {"6",   0,  vtcmd_back_index, VT400}, /* id:DECBI Back index (hor. scroll) */
+    {"7",   0,  vtcmd_save_cursor, VT100}, /* id:DECSC Save Cursor */
+    {"8",   0,  vtcmd_restore_cursor, VT100}, /* id:DECRC Restore Cursor */
+    {"9",   0,  vtcmd_forward_index, VT400}, /* id:DECFI Forward index (hor. scroll)*/
+
+    //{"Z", 0,  vtcmd_device_attributes},
+    //{"%G",0,  vtcmd_set_default_font}, // set_alternate_font
+
+
+    {"(0",  0,   vtcmd_set_charmap},
+    {"(1",  0,   vtcmd_set_charmap},
+    {"(2",  0,   vtcmd_set_charmap},
+    {"(A",  0,   vtcmd_set_charmap},
+    {"(B",  0,   vtcmd_set_charmap},
+    {")0",  0,   vtcmd_set_charmap},
+    {")1",  0,   vtcmd_set_charmap},
+    {")2",  0,   vtcmd_set_charmap},
+    {")A",  0,   vtcmd_set_charmap},
+    {")B",  0,   vtcmd_set_charmap},
+    {"%G",  0,   vtcmd_set_charmap},
+
+    {"#3",  0,   vtcmd_set_double_width_double_height_top_line, VT100}, /*id:DECDHL Top half of 
double-width, double-height line */
+    {"#4",  0,   vtcmd_set_double_width_double_height_bottom_line, VT100}, /*id:DECDHL Bottom half of 
double-width, double-height line */
+    {"#5",  0,   vtcmd_set_single_width_single_height_line, VT100}, /* id:DECSWL Single-width line */
+    {"#6",  0,   vtcmd_set_double_width_single_height_line, VT100}, /* id:DECDWL Double-width line */
+
+    {"#8",  0,   vtcmd_screen_alignment_display, VT100}, /* id:DECALN Screen Alignment Pattern */
+    {"=",   0,   vtcmd_ignore},  // keypad mode change
+    {">",   0,   vtcmd_ignore},  // keypad mode change
+    {"c",   0,   vtcmd_reset_to_initial_state, VT100}, /* id:RIS Reset to Initial State */
+    {"[!", 'p',  vtcmd_ignore},       // soft reset?
+    {"[",  'p',  vtcmd_request_mode}, /* args:Pa$ id:DECRQM Request ANSI Mode */
+#if 0
+    {"[?",  'p',  vtcmd_request_mode}, /* args:Pd$ id:DECRQM Request DEC Mode */
+#endif
+
+    {NULL, 0, NULL}
+  };
+
+  static void handle_sequence (VT *vt, const char *sequence)
+{
+  int i0 = strlen (sequence)-1;
+  int i;
+  vt->rev ++;
+  for (i = 0; sequences[i].prefix; i++)
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+      if (!strncmp (sequence, sequences[i].prefix, strlen (sequences[i].prefix) ) )
+        {
+          if (! (sequences[i].suffix && (sequence[i0] != sequences[i].suffix) ) )
+            {
+              VT_command ("%s", sequence);
+              sequences[i].vtcmd (vt, sequence);
+              return;
+            }
+        }
     }
-  }
-  else
-  {
-    if (rasterizer->swap_red_green)
-      ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-    else
-      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  }
-  //ctx_fragment_swap_red_green_u8 (out, count);
-#if CTX_DITHER
-  uint8_t *rgba = (uint8_t*)out;
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
+#ifndef ASANBUILD
+  VT_warning ("unhandled: %c%c%c%c%c%c%c%c%c\n", sequence[0], sequence[1], sequence[2], sequence[3], 
sequence[4], sequence[5], sequence[6], sequence[7], sequence[8]);
 #endif
 }
 
-static void
-ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+static void vt_line_feed (VT *vt)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
-  for (int i = 0; i < count; i ++)
-  {
-  int u = x - g->texture.x0;
-  int v = y - g->texture.y0;
-  if ( u < 0 || v < 0 ||
-       u >= buffer->width ||
-       v >= buffer->height)
+  int was_home = vt->at_line_home;
+  if (vt->margin_top == 1 && vt->margin_bottom == vt->rows)
     {
-      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+      if (vt->lines == NULL ||
+          (vt->lines->data == vt->current_line && vt->cursor_y != vt->rows) )
+        {
+          vt->current_line = vt_line_new_with_size ("", vt->cols);
+          ctx_list_prepend (&vt->lines, vt->current_line);
+          vt->line_count++;
+        }
+      if (vt->cursor_y >= vt->margin_bottom)
+        {
+          vt->cursor_y = vt->margin_bottom;
+          vt_scroll (vt, -1);
+        }
+      else
+        {
+          vt->cursor_y++;
+        }
     }
   else
     {
-      uint8_t *src = (uint8_t *) buffer->data;
-      src += v * buffer->stride + u / 8;
-      if (*src & (1<< (u & 7) ) )
+      if (vt->lines->data == vt->current_line &&
+          (vt->cursor_y != vt->margin_bottom) && 0)
         {
-          rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+          vt->current_line = vt_line_new_with_size ("", vt->cols);
+          ctx_list_prepend (&vt->lines, vt->current_line);
+          vt->line_count++;
         }
-      else
+      vt->cursor_y++;
+      if (vt->cursor_y > vt->margin_bottom)
         {
-          for (int c = 0; c < 4; c++)
-            { rgba[c] = 255;
-            }//g->texture.rgba[c];
-            //}
+          vt->cursor_y = vt->margin_bottom;
+          vt_scroll (vt, -1);
         }
     }
-
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  if (vt->cr_on_lf)
+    { vt_carriage_return (vt); }
+  vt_trimlines (vt, vt->rows);
+  if (was_home)
+    { vt_carriage_return (vt); }
 }
 
-#if CTX_GRADIENTS
-static void
-ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+//#include "vt-encodings.h"
+
+#ifndef NO_SDL
+static void vt_state_apc_audio (VT *vt, int byte)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i <  count; i ++)
-  {
-    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
-    uint32_t *rgbap = (uint32_t*)&ctx_gradient_cache_u8[ctx_grad_index(v)][0];
-    *((uint32_t*)rgba) = *rgbap;
+  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
+    {
+      vt_audio (vt, vt->argument_buf);
+      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
+    }
+  else
+    {
+      vt_argument_buf_add (vt, byte);
+    }
+}
+
 #else
-    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
-#endif
-#if CTX_DITHER
-    ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                        rasterizer->format->dither_green);
-#endif
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
+
+void vt_audio_task (VT *vt, int click)
+{
 }
 
-static void
-ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+void vt_bell (VT *vt)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i <  count; i ++)
-  {
-  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                g->linear_gradient.length) -
-              g->linear_gradient.start) * (g->linear_gradient.rdelta);
-#if CTX_GRADIENT_CACHE
-  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0]));
-  *((uint32_t*)rgba) = *rgbap;
-#else
-  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
-#endif
-#if CTX_DITHER
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
+}
+static void vt_state_apc_audio (VT *vt, int byte)
+{
+  vt->state = vt_state_apc_generic;
 }
 
 #endif
 
 static void
-ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+vt_carriage_return (VT *vt)
 {
-  uint8_t  rgba[4];
-  uint8_t *rgba_out = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba);
-
-  if (rasterizer->swap_red_green)
-  {
-    int tmp = rgba[0];
-    rgba[0] = rgba[2];
-    rgba[2] = tmp;
-  }
-  for (int i = 0; i < count; i++, rgba_out+=4)
-    memcpy (rgba_out, rgba, 4);
+  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
+  vt->cursor_x = VT_MARGIN_LEFT;
+  vt->at_line_home = 1;
 }
-#if CTX_ENABLE_FLOAT
 
-#if CTX_GRADIENTS
-static void
-ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+/* if the byte is a non-print control character, handle it and return 1
+ * oterhwise return 0*/
+static int _vt_handle_control (VT *vt, int byte)
 {
-  float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
-  {
-    float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                  g->linear_gradient.length) -
-                g->linear_gradient.start) * (g->linear_gradient.rdelta);
-    ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba);
-    x += dx;
-    y += dy;
-    rgba += 4;
-  }
+  /* the big difference between ANSI-BBS mode and VT100+ mode is that
+   * most C0 characters are printable
+   */
+  if (CTX_UNLIKELY(vt->encoding == 1)) // this codepage is for ansi-bbs use
+    switch (byte)
+      {
+        case '\0':
+          return 1;
+        case 1:    /* SOH start of heading */
+        case 2:    /* STX start of text */
+        case 3:    /* ETX end of text */
+        case 4:    /* EOT end of transmission */
+        case 5:    /* ENQuiry */
+        case 6:    /* ACKnolwedge */
+        case '\v': /* VT vertical tab */
+        case '\f': /* VF form feed */
+        case 14: /* SO shift in - alternate charset */
+        case 15: /* SI shift out - (back to normal) */
+        case 16: /* DLE data link escape */
+        case 17: /* DC1 device control 1 - XON */
+        case 18: /* DC2 device control 2 */
+        case 19: /* DC3 device control 3 - XOFF */
+        case 20: /* DC4 device control 4 */
+        case 21: /* NAK negative ack */
+        case 22: /* SYNchronous idle */
+        case 23: /* ETB end of trans. blk */
+        case 24: /* CANcel (vt100 aborts sequence) */
+        case 25: /* EM  end of medium */
+        case 26: /* SUB stitute */
+        case 28: /* FS file separator */
+        case 29: /* GS group separator */
+        case 30: /* RS record separator */
+        case 31: /* US unit separator */
+          _vt_add_str (vt, charmap_cp437[byte]);
+          return 1;
+      }
+  switch (byte)
+    {
+      case '\0':
+      case 1:    /* SOH start of heading */
+      case 2:    /* STX start of text */
+      case 3:    /* ETX end of text */
+      case 4:    /* EOT end of transmission */
+      case 6:    /* ACKnolwedge */
+        return 1;
+      case 5:    /* ENQuiry */
+        {
+          const char *reply = getenv ("TERM_ENQ_REPLY");
+          if (reply)
+            {
+              char *copy = strdup (reply);
+              for (uint8_t *c = (uint8_t *) copy; *c; c++)
+                {
+                  if (*c < ' ' || * c > 127) { *c = 0; }
+                }
+              vt_write (vt, reply, strlen (reply) );
+              free (copy);
+            }
+        }
+        return 1;
+      case '\a': /* BELl */
+        vt_bell (vt);
+        return 1;
+      case '\b': /* BS */
+        _vt_backspace (vt);
+        return 1;
+      case '\t': /* HT tab */
+        _vt_htab (vt);
+        return 1;
+      case '\v': /* VT vertical tab */
+      case '\f': /* VF form feed */
+      case '\n': /* LF line ffed */
+        vt_line_feed (vt);
+        return 1;
+      case '\r': /* CR carriage return */
+        vt_carriage_return (vt);
+        return 1;
+      case 14: /* SO shift in - alternate charset */
+        vt->shifted_in = 1;  // XXX not in vt52
+        return 1;
+      case 15: /* SI shift out - (back to normal) */
+        vt->shifted_in = 0;
+        return 1;
+      case 16: /* DLE data link escape */
+      case 17: /* DC1 device control 1 - XON */
+      case 18: /* DC2 device control 2 */
+      case 19: /* DC3 device control 3 - XOFF */
+      case 20: /* DC4 device control 4 */
+      case 21: /* NAK negative ack */
+      case 22: /* SYNchronous idle */
+      case 23: /* ETB end of trans. blk */
+      case 24: /* CANcel (vt100 aborts sequence) */
+      case 25: /* EM  end of medium */
+      case 26: /* SUB stitute */
+        _vt_add_str (vt, "¿");  // in vt52? XXX
+        return 1;
+      case 27: /* ESCape */
+        return 0;
+        break;
+      case 28: /* FS file separator */
+      case 29: /* GS group separator */
+      case 30: /* RS record separator */
+      case 31: /* US unit separator */
+      case 127: /* DEL */
+        return 1;
+      default:
+        return 0;
+    }
 }
 
-static void
-ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+void vt_open_log (VT *vt, const char *path)
 {
-  float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
-  {
-  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);
-    x+=dx;
-    y+=dy;
-    rgba +=4;
-  }
+  unlink (path);
+  vt->log = fopen (path, "w");
 }
-#endif
-
 
-static void
-ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+/* the function shared by sixels, kitty mode and iterm2 mode for
+ * doing inline images. it attaches an image to the current line
+ */
+static void display_image (VT *vt, Image *image,
+                           int col,
+                           float xoffset,
+                           float yoffset,
+                           int rows,
+                           int cols,
+                           int subx,
+                           int suby,
+                           int subw,
+                           int subh
+                          )
 {
-  float *rgba = (float *) out;
-  for (int i = 0; i < count; i++)
+  int i = 0;
+  for (i = 0; vt->current_line->images[i] && i < 4; i++)
   {
-    CtxSource *g = &rasterizer->state->gstate.source_fill;
-    ctx_color_get_rgba (rasterizer->state, &g->color, rgba);
-    rgba += 4;
+     if (vt->current_line->image_col[i] == vt->cursor_x)
+       break;
   }
-}
-
-static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  float *outf = (float *) out;
-  uint8_t rgba[4];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
+  //for (i = 0; vt->current_line->images[i] && i < 4; i++);
+  if (i >= 4) { i = 3; }
+  /* this needs a struct and dynamic allocation */
+  vt->current_line->images[i] = image;
+  vt->current_line->image_col[i] = vt->cursor_x;
+  vt->current_line->image_X[i] = xoffset;
+  vt->current_line->image_Y[i] = yoffset;
+  vt->current_line->image_subx[i] = subx;
+  vt->current_line->image_suby[i] = suby;
+  vt->current_line->image_subw[i] = subw;
+  vt->current_line->image_subh[i] = subh;
+  vt->current_line->image_rows[i] = rows;
+  vt->current_line->image_cols[i] = cols;
+}
+
+static int vt_gfx_pending=0;
+
+void vt_gfx (VT *vt, const char *command)
+{
+  const char *payload = NULL;
+  char key = 0;
+  int  value;
+  int  pos = 1;
+  if (vt->gfx.multichunk == 0)
+    {
+      memset (&vt->gfx, 0, sizeof (GfxState) );
+      vt->gfx.action='t';
+      vt->gfx.transmission='d';
+    }
+  while (command[pos] != ';')
+    {
+      pos ++; // G or ,
+      if (command[pos] == ';') { break; }
+      key = command[pos];
+      pos++;
+      if (command[pos] == ';') { break; }
+      pos ++; // =
+      if (command[pos] == ';') { break; }
+      if (command[pos] >= '0' && command[pos] <= '9')
+        { value = atoi (&command[pos]); }
+      else
+        { value = command[pos]; }
+      while (command[pos] &&
+             command[pos] != ',' &&
+             command[pos] != ';') { pos++; }
+      switch (key)
+        {
+          case 'a':
+            vt->gfx.action = value;
+            break;
+          case 'd':
+            vt->gfx.delete = value;
+            break;
+          case 'i':
+            vt->gfx.id = value;
+            break;
+          case 'S':
+            vt->gfx.buf_size = value;
+            break;
+          case 's':
+            vt->gfx.buf_width = value;
+            break;
+          case 'v':
+            vt->gfx.buf_height = value;
+            break;
+          case 'f':
+            vt->gfx.format = value;
+            break;
+          case 'm':
+            vt->gfx.multichunk = value;
+            break;
+          case 'o':
+            vt->gfx.compression = value;
+            break;
+          case 't':
+            vt->gfx.transmission = value;
+            break;
+          case 'x':
+            vt->gfx.x = value;
+            break;
+          case 'y':
+            vt->gfx.y = value;
+            break;
+          case 'w':
+            vt->gfx.w = value;
+            break;
+          case 'h':
+            vt->gfx.h = value;
+            break;
+          case 'X':
+            vt->gfx.x_cell_offset = value;
+            break;
+          case 'Y':
+            vt->gfx.y_cell_offset = value;
+            break;
+          case 'c':
+            vt->gfx.columns = value;
+            break;
+          case 'r':
+            vt->gfx.rows = value;
+            break;
+          case 'z':
+            vt->gfx.z_index = value;
+            break;
+        }
+    }
+  payload = &command[pos+1];
+  {
+    int chunk_size = strlen (payload);
+    int old_size = vt->gfx.data_size;
+    // accumulate incoming data
+    if (vt->gfx.data == NULL)
+      {
+        vt->gfx.data_size = chunk_size;
+        vt->gfx.data = malloc (vt->gfx.data_size + 1);
+      }
+    else
+      {
+        vt->gfx.data_size += chunk_size;
+        vt->gfx.data = realloc (vt->gfx.data, vt->gfx.data_size + 1);
+      }
+    memcpy (vt->gfx.data + old_size, payload, chunk_size);
+    vt->gfx.data[vt->gfx.data_size]=0;
+  }
+  if (vt->gfx.multichunk == 0)
     {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+      if (vt->gfx.transmission != 'd') /* */
+        {
+          char buf[256];
+          sprintf (buf, "\033_Gi=%i;only direct transmission supported\033\\",
+                   vt->gfx.id);
+          vt_write (vt, buf, strlen (buf) );
+          goto cleanup;
+        }
+      {
+        int bin_length = vt->gfx.data_size;
+        uint8_t *data2 = malloc (vt->gfx.data_size);
+        bin_length = ctx_base642bin ( (char *) vt->gfx.data,
+                                     &bin_length,
+                                     data2);
+        memcpy (vt->gfx.data, data2, bin_length + 1);
+        vt->gfx.data_size = bin_length;
+        free (data2);
+      }
+      if (vt->gfx.buf_width)
+        {
+          // implicit buf_size
+          vt->gfx.buf_size = vt->gfx.buf_width * vt->gfx.buf_height *
+                             (vt->gfx.format == 24 ? 3 : 4);
+        }
+      if (vt->gfx.compression == 'z')
+        {
+          //vt->gfx.buf_size)
+          unsigned char *data2 = malloc (vt->gfx.buf_size + 1);
+          /* if a buf size is set (rather compression, but
+           * this works first..) then */
+          unsigned long actual_uncompressed_size = vt->gfx.buf_size;
+          int z_result = uncompress (data2, &actual_uncompressed_size,
+                                     vt->gfx.data,
+                                     vt->gfx.data_size);
+          if (z_result != Z_OK)
+            {
+              char buf[256]= "\033_Go=z;zlib error\033\\";
+              vt_write (vt, buf, strlen (buf) );
+              goto cleanup;
+            }
+          free (vt->gfx.data);
+          vt->gfx.data = data2;
+          vt->gfx.data_size = actual_uncompressed_size;
+          vt->gfx.compression = 0;
+        }
+      if (vt->gfx.format == 100)
+        {
+          int channels;
+          uint8_t *new_data = stbi_load_from_memory (vt->gfx.data, vt->gfx.data_size, &vt->gfx.buf_width, 
&vt->gfx.buf_height, &channels, 4);
+          if (!new_data)
+            {
+              char buf[256]= "\033_Gf=100;image decode error\033\\";
+              vt_write (vt, buf, strlen (buf) );
+              goto cleanup;
+            }
+          vt->gfx.format = 32;
+          free (vt->gfx.data);
+          vt->gfx.data = new_data;
+          vt->gfx.data_size= vt->gfx.buf_width * vt->gfx.buf_height * 4;
+        }
+      Image *image = NULL;
+      switch (vt->gfx.action)
+        {
+          case 't': // transfer
+          case 'T': // transfer and present
+            switch (vt->gfx.format)
+              {
+                case 24:
+                case 32:
+                  image = image_add (vt->gfx.buf_width, vt->gfx.buf_height, vt->gfx.id,
+                                     vt->gfx.format, vt->gfx.data_size, vt->gfx.data);
+                  vt->gfx.data = NULL;
+                  vt->gfx.data_size=0;
+                  break;
+              }
+            if (vt->gfx.action == 't')
+              { break; }
+          // fallthrough
+          case 'p': // present
+            if (!image && vt->gfx.id)
+              { image = image_query (vt->gfx.id); }
+            if (image)
+              {
+                display_image (vt, image, vt->cursor_x, vt->gfx.rows, vt->gfx.columns,
+                               vt->gfx.x_cell_offset * 1.0 / vt->cw,
+                               vt->gfx.y_cell_offset * 1.0 / vt->ch,
+                               vt->gfx.x,
+                               vt->gfx.y,
+                               vt->gfx.w,
+                               vt->gfx.h);
+                int right = (image->width + (vt->cw-1) ) /vt->cw;
+                int down = (image->height + (vt->ch-1) ) /vt->ch;
+                for (int i = 0; i<down - 1; i++)
+                  { vtcmd_index (vt, " "); }
+                for (int i = 0; i<right; i++)
+                  { vtcmd_cursor_forward (vt, " "); }
+              }
+            break;
+          case 'q': // query
+            if (image_query (vt->gfx.id) )
+              {
+                char buf[256];
+                sprintf (buf, "\033_Gi=%i;OK\033\\", vt->gfx.id);
+                vt_write (vt, buf, strlen (buf) );
+              }
+            break;
+          case 'd': // delete
+            {
+              int row = vt->rows; // probably not right at start of session XXX
+              for (CtxList *l = vt->lines; l; l = l->next, row --)
+                {
+                  VtLine *line = l->data;
+                  for (int i = 0; i < 4; i ++)
+                    {
+                      int free_resource = 0;
+                      int match = 0;
+                      if (line->images[i])
+                        switch (vt->gfx.delete)
+                          {
+                            case 'A':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'a': /* all images visible on screen */
+                              match = 1;
+                              break;
+                            case 'I':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'i': /* all images with specified id */
+                              if ( ( (Image *) (line->images[i]) )->id == vt->gfx.id)
+                                { match = 1; }
+                              break;
+                            case 'P':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'p': /* all images intersecting cell
+          specified with x and y */
+                              if (line->image_col[i] == vt->gfx.x &&
+                                  row == vt->gfx.y)
+                                { match = 1; }
+                              break;
+                            case 'Q':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'q': /* all images with specified cell (x), row(y) and z */
+                              if (line->image_col[i] == vt->gfx.x &&
+                                  row == vt->gfx.y)
+                                { match = 1; }
+                              break;
+                            case 'Y':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'y': /* all images with specified row (y) */
+                              if (row == vt->gfx.y)
+                                { match = 1; }
+                              break;
+                            case 'X':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'x': /* all images with specified column (x) */
+                              if (line->image_col[i] == vt->gfx.x)
+                                { match = 1; }
+                              break;
+                            case 'Z':
+                              free_resource = 1;
+                              /* FALLTHROUGH */
+                            case 'z': /* all images with specified z-index (z) */
+                              break;
+                          }
+                      if (match)
+                        {
+                          line->images[i] = NULL;
+                          if (free_resource)
+                            {
+                              // XXX : NYI
+                            }
+                        }
+                    }
+                }
+            }
+            break;
+        }
+cleanup:
+      if (vt->gfx.data)
+        { free (vt->gfx.data); }
+      vt->gfx.data = NULL;
+      vt->gfx.data_size=0;
+      vt->gfx.multichunk=0;
+      vt_gfx_pending = 0;
     }
-  for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); }
+  else
+     vt_gfx_pending = 1;
 }
 
-static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer)
+static void vt_state_vt52 (VT *vt, int byte)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+  /* in vt52 mode, utf8_pos being non 0 means we got ESC prior */
+  switch (vt->utf8_pos)
     {
-      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;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF;
-#endif
+      case 0:
+        if (_vt_handle_control (vt, byte) == 0)
+          switch (byte)
+            {
+              case 27: /* ESC */
+                vt->utf8_pos = 1;
+                break;
+              default:
+                {
+                  char str[2] = {byte & 127, 0};
+                  /* we're not validating utf8, and our utf8 manipulation
+                   * functions are not robust against malformed utf8,
+                   * hence we strip to ascii
+                   */
+                  _vt_add_str (vt, str);
+                }
+                break;
+            }
+        break;
+      case 1:
+        vt->utf8_pos = 0;
+        switch (byte)
+          {
+            case 'A':
+              vtcmd_cursor_up (vt, " ");
+              break;
+            case 'B':
+              vtcmd_cursor_down (vt, " ");
+              break;
+            case 'C':
+              vtcmd_cursor_forward (vt, " ");
+              break;
+            case 'D':
+              vtcmd_cursor_backward (vt, " ");
+              break;
+            case 'F':
+              vtcmd_set_alternate_font (vt, " ");
+              break;
+            case 'G':
+              vtcmd_set_default_font (vt, " ");
+              break;
+            case 'H':
+              _vt_move_to (vt, 1, 1);
+              break;
+            case 'I':
+              vtcmd_reverse_index (vt, " ");
+              break;
+            case 'J':
+              vtcmd_erase_in_display (vt, "[0J");
+              break;
+            case 'K':
+              vtcmd_erase_in_line (vt, "[0K");
+              break;
+            case 'Y':
+              vt->utf8_pos = 2;
+              break;
+            case 'Z':
+              vt_write (vt, "\033/Z", 3);
+              break;
+            case '<':
+              vt->state = vt_state_neutral;
+              break;
+            default:
+              break;
+          }
+        break;
+      case 2:
+        _vt_move_to (vt, byte - 31, vt->cursor_x);
+        vt->utf8_pos = 3;
+        break;
+      case 3:
+        _vt_move_to (vt, vt->cursor_y, byte - 31);
+        vt->utf8_pos = 0;
+        break;
     }
-  return ctx_fragment_color_RGBAF;
 }
-#endif
 
-static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer)
+static void vt_sixels (VT *vt, const char *sixels)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (gstate->source_fill.type)
+  uint8_t colors[256][3];
+  int width = 0;
+  int height = 0;
+  int x = 0;
+  int y = 0;
+  Image *image;
+  uint8_t *pixels = NULL;
+  int repeat = 1;
+  const char *p = sixels;
+  int pal_no = 0;
+#if 0
+  for (; *p && *p != ';'; p++);
+  if (*p == ';') { p ++; }
+  printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p) );
+  // should be 0
+  for (; *p && *p != ';'; p++);
+  if (*p == ';') { p ++; }
+  printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p) );
+  // if 1 then transparency is enabled - otherwise use bg color
+  for (; *p && *p != 'q'; p++);
+#endif
+  //for (; *p && *p != '"'; p++);
+  while (*p && *p != 'q') { p++; }
+  if (*p == 'q') { p++; }
+  if (*p == '"') { p++; }
+  //printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p));
+  for (; *p && *p != ';'; p++);
+  if (*p == ';') { p ++; }
+  //printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p));
+  for (; *p && *p != ';'; p++);
+  if (*p == ';') { p ++; }
+  width = atoi (p);
+  for (; *p && *p != ';'; p++);
+  if (*p == ';') { p ++; }
+  height = atoi (p);
+  if (width * height > 2048 * 2048)
+    return;
+  if (width <= 0 || height <=0)
     {
-      case CTX_SOURCE_TEXTURE:
-        switch (buffer->format->bpp)
-          {
-            case 1:  return ctx_fragment_image_gray1_RGBA8;
-            case 24: 
-              {
-                if (gstate->image_smoothing)
-                {
-                  float factor = ctx_matrix_get_scale (&gstate->transform);
-                          //fprintf (stderr, "{%.3f}", factor);
-                  if (factor < 0.5f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_box;
-                  }
-                  else if (factor > 0.99f && factor < 1.01f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_nearest;
-                  }
-                  else
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_bi;
-                  }
-                }
-                else
+      width = 0;
+      height = 0;
+      // XXX  : a copy paste dry-run
+      for (const char *t=p; *t; t++)
+        {
+          if (*t == '#')
+            {
+              t++;
+              while (*t && *t >= '0' && *t <= '9') { t++; }
+              if (*t == ';')
                 {
-                  if (rasterizer->swap_red_green)
-                    return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
-                  return ctx_fragment_image_rgb8_RGBA8_nearest;
+                  for (; *t && *t != ';'; t++);
+                  if (*t == ';') { t ++; }
+                  for (; *t && *t != ';'; t++);
+                  if (*t == ';') { t ++; }
+                  for (; *t && *t != ';'; t++);
+                  if (*t == ';') { t ++; }
+                  for (; *t && *t != ';'; t++);
+                  if (*t == ';') { t ++; }
+                  while (*t && *t >= '0' && *t <= '9') { t++; }
+                  t--;
                 }
-              }
-              break;
-            case 32:
-              {
-                if (gstate->image_smoothing)
+              else
                 {
-                  float factor = ctx_matrix_get_scale (&gstate->transform);
-                          //fprintf (stderr, "[%.3f]", factor);
-                  if (factor < 0.5f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_box;
-                  }
-                  else if (factor > 0.99f && factor < 1.01f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_nearest;
-                  }
-                  else
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_bi;
-                  }
+                  t--;
                 }
-                else
+            }
+          else if (*t == '$') // carriage return
+            {
+              if (x > width) { width = x; }
+              x = 0;
+            }
+          else if (*t == '-') // line feed
+            {
+              y += 6;
+              x = 0;
+            }
+          else if (*t == '!') // repeat
+            {
+              t++;
+              repeat = atoi (t);
+              while (*t && *t >= '0' && *t <= '9') { t++; }
+              t--;
+            }
+          else if (*t >= '?' && *t <= '~') /* sixel data */
+            {
+              x += repeat;
+              repeat = 1;
+            }
+        }
+      height = y;
+    }
+  x = 0;
+  y = 0;
+  pixels = calloc (width * (height + 6), 4);
+  image = image_add (width, height, 0,
+                     32, width*height*4, pixels);
+  uint8_t *dst = pixels;
+  for (; *p; p++)
+    {
+      if (*p == '#')
+        {
+          p++;
+          pal_no = atoi (p);
+          if (pal_no < 0 || pal_no > 255) { pal_no = 255; }
+          while (*p && *p >= '0' && *p <= '9') { p++; }
+          if (*p == ';')
+            {
+              /* define a palette */
+              for (; *p && *p != ';'; p++);
+              if (*p == ';') { p ++; }
+              // color_model , 2 is rgb
+              for (; *p && *p != ';'; p++);
+              if (*p == ';') { p ++; }
+              colors[pal_no][0] = atoi (p) * 255 / 100;
+              for (; *p && *p != ';'; p++);
+              if (*p == ';') { p ++; }
+              colors[pal_no][1] = atoi (p) * 255 / 100;
+              for (; *p && *p != ';'; p++);
+              if (*p == ';') { p ++; }
+              colors[pal_no][2] = atoi (p) * 255 / 100;
+              while (*p && *p >= '0' && *p <= '9') { p++; }
+              p--;
+            }
+          else
+            {
+              p--;
+            }
+        }
+      else if (*p == '$') // carriage return
+        {
+          x = 0;
+          dst = &pixels[ (4 * width * y)];
+        }
+      else if (*p == '-') // line feed
+        {
+          y += 6;
+          x = 0;
+          dst = &pixels[ (4 * width * y)];
+        }
+      else if (*p == '!') // repeat
+        {
+          p++;
+          repeat = atoi (p);
+          while (*p && *p >= '0' && *p <= '9') { p++; }
+          p--;
+        }
+      else if (*p >= '?' && *p <= '~') /* sixel data */
+        {
+          int sixel = (*p) - '?';
+          if (x + repeat <= width && y < height)
+            {
+              for (int bit = 0; bit < 6; bit ++)
                 {
-                  if (rasterizer->swap_red_green)
-                    return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
-                  return ctx_fragment_image_rgba8_RGBA8_nearest;
+                  if (sixel & (1 << bit) )
+                    {
+                      for (int u = 0; u < repeat; u++)
+                        {
+                          for (int c = 0; c < 3; c++)
+                            {
+                              dst[ (bit * width * 4) + u * 4 + c] = colors[pal_no][c];
+                              dst[ (bit * width * 4) + u * 4 + 3] = 255;
+                            }
+                        }
+                    }
                 }
-              }
-            default: return ctx_fragment_image_RGBA8;
-          }
-      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBA8;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8;
-#endif
+            }
+          x   += repeat;
+          dst += (repeat * 4);
+          repeat = 1;
+        }
     }
-  return ctx_fragment_color_RGBA8;
+  if (image)
+    {
+      display_image (vt, image, vt->cursor_x, 0,0, 0.0, 0.0, 0,0,0,0);
+      int right = (image->width + (vt->cw-1) ) /vt->cw;
+      int down = (image->height + (vt->ch-1) ) /vt->ch;
+      for (int i = 0; i<down - 1; i++)
+        { vtcmd_index (vt, " "); }
+      for (int i = 0; i<right; i++)
+        { vtcmd_cursor_forward (vt, " "); }
+      vt_line_feed (vt);
+      vt_carriage_return (vt);
+    }
+  vt->rev++;
 }
 
-static void
-ctx_init_uv (CtxRasterizer *rasterizer,
-             int x0, int count,
-             float *u0, float *v0, float *ud, float *vd)
+static inline void vt_ctx_unrled (VT *vt, char byte)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  *u0 = x0;
-  *v0 = rasterizer->scanline / 15;//rasterizer->aa;
-  float u1 = *u0 + count;
-  float v1 = *v0;
-
-  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 CTX_VT_USE_FRAMEDIFF
+  ctx_string_append_byte (vt->current_line->frame, byte);
+#endif
+  ctx_parser_feed_byte (vt->ctxp, byte);
 }
 
-#if 1
-static void
-ctx_u8_source_over_normal_opaque_color (int components, CTX_COMPOSITE_ARGUMENTS)
+static void vt_state_ctx (VT *vt, int byte)
 {
-  while (count--)
+#if 0
+  //fprintf (stderr, "%c", byte);
+  if (CTX_UNLIKELY(byte == CTX_CODEC_CHAR))
   {
-    int cov = *coverage;
-    if (cov)
+    if (CTX_UNLIKELY(vt->in_prev_match))
     {
-    if (cov == 255)
-    {
-        switch (components)
+      char *prev = vt->current_line->prev;
+      int prev_length = vt->current_line->prev_length;
+      int start = atoi (vt->reference);
+      int len = 0;
+      if (strchr (vt->reference, ' '))
+        len = atoi (strchr (vt->reference, ' ')+1);
+
+      //fprintf (stderr, "%i-%i:", start, len);
+
+      if (start < 0) start = 0;
+      if (start >= (prev_length))start = prev_length-1;
+      if (len + start > prev_length)
+        len = prev_length - start;
+
+      //fprintf (stderr, "%i-%i\n", start, len);
+
+      if (CTX_UNLIKELY (start == 0 && len == 0))
+      {
+        vt_ctx_unrled (vt, CTX_CODEC_CHAR);
+      }
+      else
+      {
+        if (prev)
+        for (int i = 0; i < len && start + i < prev_length; i++)
         {
-          case 4:
-            *((uint32_t*)(dst)) = *((uint32_t*)(src));
-            break;
-          default:
-            for (int c = 0; c < components; c++)
-              dst[c] = src[c];
+          vt_ctx_unrled (vt, prev[start + i]);
         }
+      }
+      vt->ref_len = 0;
+      vt->reference[0]=0;
+      vt->in_prev_match = 0;
     }
     else
     {
-        for (int c = 0; c < components; c++)
-          dst[c] = dst[c]+((src[c]-dst[c]) * cov) / 255;
-    }
+      vt->reference[0]=0;
+      vt->ref_len = 0;
+      vt->in_prev_match = 1;
     }
-    coverage ++;
-    dst+=components;
   }
-}
-#endif
-
-static void
-ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  if (rasterizer->fragment)
-    {
-      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-    }
-
-  while (count--)
+  else
   {
-    int cov = *coverage;
-    if (cov == 0)
-    {
-      for (int c = 0; c < components; c++)
-        { dst[c] = 0; }
-    }
-    else
+    if (CTX_UNLIKELY(vt->in_prev_match))
     {
-      if (rasterizer->fragment)
+      if (vt->ref_len < 15)
       {
-        rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
-        u0+=ud;
-        v0+=vd;
+        vt->reference[vt->ref_len++]=byte;
+        vt->reference[vt->ref_len]=0;
       }
-    if (cov == 255)
-    {
-      for (int c = 0; c < components; c++)
-        dst[c] = src[c];
     }
     else
     {
-      uint8_t ralpha = 255 - cov;
-      for (int c = 0; c < components; c++)
-        { dst[c] = (src[c]*cov + 0 * ralpha) / 255; }
+      vt_ctx_unrled (vt, byte);
     }
-    }
-    dst += components;
-    coverage ++;
   }
+#else
+      vt_ctx_unrled (vt, byte);
+#endif
 }
 
-static void
-ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+static int vt_decoder_feed (VT *vt, int byte)
 {
-  while (count--)
+  int encoding = vt->encoding;
+  switch (encoding)
+    {
+      case 0: /* utf8 */
+        if (!vt->utf8_expected_bytes)
+          {
+            vt->utf8_expected_bytes = mrg_utf8_len (byte) - 1;
+            vt->utf8_pos = 0;
+          }
+        if (vt->utf8_expected_bytes)
+          {
+            vt->utf8_holding[vt->utf8_pos++] = byte;
+            vt->utf8_holding[vt->utf8_pos] = 0;
+            if (vt->utf8_pos == vt->utf8_expected_bytes + 1)
+              {
+                vt->utf8_expected_bytes = 0;
+                vt->utf8_pos = 0;
+              }
+            else
+              {
+                return 1;
+              }
+          }
+        else
+          {
+            vt->utf8_holding[0] = byte;
+            vt->utf8_holding[0] &= 127;
+            vt->utf8_holding[1] = 0;
+            if (vt->utf8_holding[0] == 0)
+              { vt->utf8_holding[0] = 32; }
+          }
+        break;
+      case 1:
+        if ( ! (byte>=0 && byte < 256) )
+          { byte = 255; }
+        strcpy ( (char *) &vt->utf8_holding[0], &charmap_cp437[byte][0]);
+        vt->utf8_expected_bytes = mrg_utf8_len (byte) - 1; // ?
+        break;
+      default:
+        vt->utf8_holding[0] = byte & 127;
+        vt->utf8_holding[1] = 0;
+        break;
+    }
+  return 0;
+}
+
+static void vt_state_swallow (VT *vt, int byte)
+{
+  vt->state = vt_state_neutral;
+}
+
+static int vt_decode_hex_digit (char digit)
+{
+  if (digit >= '0' && digit <='9')
+    return digit - '0';
+  if (digit >= 'a' && digit <='f')
+    return digit - 'a' + 10;
+  if (digit >= 'A' && digit <='F')
+    return digit - 'A' + 10;
+  return 0;
+}
+
+static int vt_decode_hex (const char *two_digits)
+{
+  return vt_decode_hex_digit (two_digits[0]) * 16 +
+         vt_decode_hex_digit (two_digits[1]);
+}
+
+static uint8_t palettes[][16][3]=
+{
+  {
+{0, 0, 0},
+{160, 41, 41},
+{74, 160, 139},
+{135, 132, 83},
+{36, 36, 237},
+{171, 74, 223},
+{59, 107, 177},
+{195, 195, 195},
+{111, 111, 111},
+{237, 172, 130},
+{153, 237, 186},
+{233, 216, 8},
+{130, 180, 237},
+{214, 111, 237},
+{29, 225, 237},
+{255, 255, 255},
+
+  },
+
   {
+    {0, 0, 0},
+    {127, 0, 0},
+    {90, 209, 88},
+    {136, 109, 0},
+    {3, 9, 235},
+    {90, 4, 150},
+    {43, 111, 150},
+    {178, 178, 178},
+    {87, 87, 87},
+    {193, 122, 99},
+    {110, 254, 174},
+    {255, 200, 0},
+    {10, 126, 254},
+    {146, 155, 249},
+    {184, 208, 254},
+    {255, 255, 255},
+
+  },{
+    {0, 0, 0},
+    {147, 53, 38},
+    {30, 171, 82},
+    {188, 153, 0},
+    {32, 71, 193},
+    {236, 49, 188},
+    {42, 182, 253},
+    {149, 149, 149},
+    {73, 73, 73},
+    {210, 36, 0},
+    {96, 239, 97},
+    {247, 240, 2},
+    {93, 11, 249},
+    {222, 42, 255},
+    {11, 227, 255},
+    {233, 235, 235},
+  },
+
+
+  { {0, 0, 0},{97, 27, 0},{129, 180, 0},{127, 100, 0},{44, 15, 255},{135, 10, 167},{20, 133, 164},{174, 174, 
174},{71, 71, 71},{167, 114, 90},{162, 214, 127},{255, 251, 83},{118, 77, 253},{192, 121, 255},{14, 217, 
255},{255, 255, 255},
+  },{
+
+
 #if 0
-    int cov = *coverage;
-    if (cov)
     {
-      if (cov == 255)
-      {
-#endif
-           //__attribute__ ((fallthrough));
-        switch (components)
+      {0, 0, 0},
+      {144, 0, 0},
+      {9, 154, 9},
+      {255, 137, 113},
+      {3, 0, 255},
+      {56, 0, 132},
+      {0, 131, 131},
+      {204, 204, 204},
+      {127, 127, 127},
+      {255, 33, 0},
+      {113, 255, 88},
+      {255, 236, 8},
+      {1, 122, 255},
+      {235, 0, 222},
+      {0, 217, 255},
+      {255, 255, 255},
+    },{
+#endif
+
+
+    {0, 0, 0},
+    {139, 0, 0},
+    {9, 154, 9},
+    {255, 137, 113},
+    {3, 0, 255},
+    {56, 0, 132},
+    {0, 111, 111},
+    {204, 204, 204},
+    {127, 127, 127},
+    {255, 33, 0},
+    {118, 255, 92},
+    {255, 230, 15},
+    {1, 122, 255},
+    {232, 0, 220},
+    {1, 217, 255},
+    {255, 255, 255},
+  },
+  {
+
+    {0, 0, 0},
+    {191, 0, 0},
+    {3, 187, 0},
+    {254, 212, 0},
+    {0, 0, 255},
+    {80, 0, 128},
+    {0, 156, 255},
+    {166, 166, 166},
+    {84, 84, 84},
+    {255, 62, 0},
+    {85, 255, 143},
+    {255, 255, 0},
+    {67, 80, 255},
+    {243, 70, 255},
+    {30, 255, 222},
+    {255, 255, 255},
+  },
+  {
+    /* */
+    { 32, 32, 32}, // 0 - background (black)
+    {165, 15, 21}, // 1               red
+    { 95,130, 10}, // 2               green
+    {205,145, 60}, // 3               yellow
+    { 49,130,189}, // 4               blue
+    {120, 40,160}, // 5               magenta
+    {120,230,230}, // 6               cyan
+    {196,196,196},// 7                light-gray
+    { 85, 85, 85},// 8                dark gray
+
+    {251,106, 74},// 9                light red
+    {130,215,140},// 10               light green
+    {255,255,  0},// 11               light yellow
+    {107,174,214},// 12               light blue
+    {215,130,160},// 13               light magenta
+    {225,255,245},// 14               light cyan
+    {255,255,255},// 15 - foreground (white)
+  },{
+    /* */
+    { 32, 32, 32}, // 0 - background (black)
+    {160,  0,  0}, // 1               red
+    {  9,233,  0}, // 2               green
+    {220,110, 44}, // 3               yellow
+    {  0,  0,200}, // 4               blue
+    { 90,  0,130}, // 5               magenta
+    {  0,156,180}, // 6               cyan
+    {196,196,196}, // 7                light-gray
+    { 85, 85, 85}, // 8                dark gray
+
+    {240, 60, 40}, // 9                light red
+    {170,240, 80}, // 10               light green
+    {248,248,  0}, // 11               light yellow
+    {  0, 40,255}, // 12               light blue
+    {204, 62,214}, // 13               light magenta
+    { 10,234,254}, // 14               light cyan
+    {255,255,255}, // 15 - foreground (white)
+  },
+  /* inspired by DEC */
+  { {  0,  0,  0}, // 0 - background  black
+    {150, 10, 10}, // 1               red
+    { 21,133,  0}, // 2               green
+
+    {103,103, 24}, // 3               yellow
+    { 44, 44,153}, // 4               blue
+    {123, 94,183}, // 5               magenta
+
+    { 20,183,193}, // 6               cyan
+
+    {177,177,177},// 7                light-gray
+    {100,100,100},// 8                dark gray
+
+    {244, 39, 39},// 9                light red
+    { 61,224, 81},// 10               light green
+    {255,255,  0},// 11               light yellow
+    { 61, 61,244},// 12               light blue
+    {240, 11,240},// 13               light magenta
+    { 61,234,234},// 14               light cyan
+
+    {255,255,255},// 15 - foreground  white
+  },
+};
+
+static void vt_state_osc (VT *vt, int byte)
+{
+  // https://ttssh2.osdn.jp/manual/4/en/about/ctrlseq.html
+  // and in "\033\" rather than just "\033", this would cause
+  // a stray char
+  //if (byte == '\a' || byte == 27 || byte == 0 || byte < 32)
+  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
+    {
+      int n = parse_int (vt->argument_buf, 0);
+      switch (n)
         {
-          case 1: dst[0] = 0; break;
-          case 3: dst[2] = 0;
-           /* FALLTHROUGH */
-          case 2: *((uint16_t*)(dst)) = 0; break;
-          case 5: dst[4] = 0;
-           /* FALLTHROUGH */
-          case 4: *((uint32_t*)(dst)) = 0; break;
+          case 0:
+          case 1:
+          case 2:
+#if 0
+    {"]0;New_title\e\",  0, , }, /* id: set window title */ "
+#endif
+            vt_set_title (vt, vt->argument_buf + 3);
+            break;
+          case 4: // set palette entry
+            {
+            int color_no = parse_int (vt->argument_buf + 2, 0);
+            char *rest = vt->argument_buf + 3;
+            rest = strchr (rest, ';');
+
+            if (rest++)
+            if (strlen(rest)>10 &&
+                rest[0] == 'r' &&
+                rest[1] == 'g' &&
+                rest[2] == 'b' &&
+                rest[3] == ':' &&
+                rest[6] == '/' &&
+                rest[9] == '/')
+            {
+              int red = vt_decode_hex (&rest[4]);
+              int green = vt_decode_hex (&rest[7]);
+              int blue = vt_decode_hex (&rest[10]);
+          //  fprintf (stderr, "set color:%i  %i %i %i\n", color_no, red, green, blue);
+              if (color_no >= 0 && color_no <= 15)
+              {
+                palettes[0][color_no][0]=red;
+                palettes[0][color_no][1]=green;
+                palettes[0][color_no][2]=blue;
+              }
+            }
+            }
+            break;
+          case 12: // text cursor color
+            break;
+          case 17: // highlight color
+            break;
+          case 19: // ??
+            break;
+
+          case 10: // text fg
+#if 0
+#if 0
+    {"]11;",  0, , }, /* id: set foreground color */
+#endif
+            {
+              /* request current foreground color, xterm does this to
+                 determine if it can use 256 colors, when this test fails,
+                 it still mixes in color 130 together with stock colors
+               */
+              char buf[128];
+              sprintf (buf, "\033]10;rgb:%2x/%2x/%2x\033\\",
+                       vt->fg_color[0], vt->fg_color[1], vt->fg_color[2]);
+              vt_write (vt, buf, strlen (buf) );
+            }
+#endif
+            break;
+          case 11: // text bg
+#if 0
+    {"]11;",  0, , }, /* id: get background color */
+            {
+              /* get background color */
+              char buf[128];
+              sprintf (buf, "\033]11;rgb:%2x/%2x/%2x\033\\",
+                       vt->bg_color[0], vt->bg_color[1], vt->bg_color[2]);
+              vt_write (vt, buf, strlen (buf) );
+            }
+#endif
+            break;
+#if 0
+    {"]1337;key=value:base64data\b\",  0, vtcmd_erase_in_line, VT100}, /* args:keyvalue id: iterm2 graphics 
*/ "
+#endif
+          case 1337:
+            if (!strncmp (&vt->argument_buf[6], "File=", 5) )
+              {
+                {
+                  /* iTerm2 image protocol */
+                  int width = 0;
+                  int height = 0;
+                  int file_size = 0;
+                  int show_inline = 0;
+                  int preserve_aspect = 1;
+                  char *name = NULL;
+                  char *p = &vt->argument_buf[11];
+                  char key[128]="";
+                  char value[128]="";
+                  int in_key=1;
+                  if (preserve_aspect) {}; /* XXX : NYI */
+                  for (; *p && *p!=':'; p++)
+                    {
+                      if (in_key)
+                        {
+                          if (*p == '=')
+                            { in_key = 0; }
+                          else
+                            {
+                              if (strlen (key) < 124)
+                                {
+                                  key[strlen (key)+1] = 0;
+                                  key[strlen (key)] = *p;
+                                }
+                            }
+                        }
+                      else
+                        {
+                          if (*p == ';')
+                            {
+                              if (!strcmp (key, "name") )
+                                {
+                                  name = strdup (value);
+                                }
+                              else if (!strcmp (key, "width") )
+                                {
+                                  width = atoi (value);
+                                  if (strchr (value, 'x') )
+                                    { /* pixels */ }
+                                  else if (strchr (value, '%') )
+                                    {
+                                      /* percent */
+                                      width = width / 100.0 * (vt->cw * vt->cols);
+                                    }
+                                  else
+                                    { /* chars */ width = width * vt->cw; }
+                                }
+                              else if (!strcmp (key, "height") )
+                                {
+                                  height = atoi (value);
+                                  if (strchr (value, 'x') )
+                                    { /* pixels */ }
+                                  else if (strchr (value, '%') )
+                                    {
+                                      /* percent */
+                                      height = height / 100.0 * (vt->ch * vt->rows);
+                                    }
+                                  else
+                                    { /* chars */ height = height * vt->ch; }
+                                }
+                              else if (!strcmp (key, "preserveAspectRatio") )
+                                {
+                                  preserve_aspect = atoi (value);
+                                }
+                              else if (!strcmp (key, "inline") )
+                                {
+                                  show_inline = atoi (value);
+                                }
+                              key[0]=0;
+                              value[0]=0;
+                              in_key = 1;
+                            }
+                          else
+                            {
+                              if (strlen (value) < 124)
+                                {
+                                  value[strlen (value)+1] = 0;
+                                  value[strlen (value)] = *p;
+                                }
+                            }
+                        }
+                    }
+                  if (key[0])
+                    {
+                      // code-dup
+                      if (!strcmp (key, "name") )
+                        {
+                          name = strdup (value);
+                        }
+                      else if (!strcmp (key, "width") )
+                        {
+                          width = atoi (value);
+                          if (strchr (value, 'x') )
+                            { /* pixels */ }
+                          else if (strchr (value, '%') )
+                            {
+                              /* percent */
+                              width = width / 100.0 * (vt->cw * vt->cols);
+                            }
+                          else
+                            { /* chars */ width = width * vt->cw; }
+                        }
+                      else if (!strcmp (key, "height") )
+                        {
+                          height = atoi (value);
+                          if (strchr (value, 'x') )
+                            { /* pixels */ }
+                          else if (strchr (value, '%') )
+                            {
+                              /* percent */
+                              height = height / 100.0 * (vt->ch * vt->rows);
+                            }
+                          else
+                            { /* chars */ height = height * vt->ch; }
+                        }
+                      else if (!strcmp (key, "preserveAspectRatio") )
+                        {
+                          preserve_aspect = atoi (value);
+                        }
+                      else if (!strcmp (key, "inline") )
+                        {
+                          show_inline = atoi (value);
+                        }
+                    }
+                  if (*p == ':')
+                    {
+                      p++;
+                    }
+                  if (0)
+                    fprintf (stderr, "%s %i %i %i %i{%s\n", name?name:"",
+                             width, height, file_size, show_inline,
+                             p);
+                  Image *image = NULL;
+                  {
+                    int bin_length = vt->argument_buf_len;
+                    uint8_t *data2 = malloc (bin_length);
+                    bin_length = ctx_base642bin ( (char *) p,
+                                                 &bin_length,
+                                                 data2);
+                    int channels = 4;
+                    int buf_width = 0;
+                    int buf_height = 0;
+                    uint8_t *new_data = stbi_load_from_memory (data2, bin_length, &buf_width, &buf_height, 
&channels, 4);
+                    free (data2);
+                    if (new_data)
+                      {
+                        image = image_add (buf_width, buf_height, 0,
+                                           32, buf_width*buf_height*4, new_data);
+                      }
+                    else
+                      {
+                        fprintf (stderr, "image decoding problem %s\n", stbi_failure_reason());
+                        fprintf (stderr, "len: %i\n", bin_length);
+                      }
+                  }
+                  if (image)
+                    {
+                      display_image (vt, image, vt->cursor_x, 0,0, 0.0, 0.0, 0,0,0,0);
+                      int right = (image->width + (vt->cw-1) ) /vt->cw;
+                      int down = (image->height + (vt->ch-1) ) /vt->ch;
+                      for (int i = 0; i<down - 1; i++)
+                        { vtcmd_index (vt, " "); }
+                      for (int i = 0; i<right; i++)
+                        { vtcmd_cursor_forward (vt, " "); }
+                    }
+                }
+              }
+            break;
+          case 104:
+            break;
+          case 8:
+            fprintf (stderr, "unhandled OSC 8, hyperlink\n");
+            break;
           default:
-            for (int c = 0; c < components; c ++)
-              dst[c] = 0;
+            fprintf (stderr, "unhandled OSC %i\n", n);
             break;
         }
-#if 0
-      }
+      if (byte == 27)
+        {
+          vt->state = vt_state_swallow;
+        }
       else
-      {
-        uint8_t ralpha = 255 - cov;
-        for (int c = 0; c < components; c++)
-          { dst[c] = (dst[c] * ralpha) / 255; }
-      }
+        {
+          vt->state = vt_state_neutral;
+        }
+    }
+  else
+    {
+      vt_argument_buf_add (vt, byte);
     }
-    coverage ++;
-#endif
-    dst += components;
-  }
 }
 
-typedef enum {
-  CTX_PORTER_DUFF_0,
-  CTX_PORTER_DUFF_1,
-  CTX_PORTER_DUFF_ALPHA,
-  CTX_PORTER_DUFF_1_MINUS_ALPHA,
-} CtxPorterDuffFactor;
-
-#define  \
-ctx_porter_duff_factors(mode, foo, bar)\
-{\
-  switch (mode)\
-  {\
-     case CTX_COMPOSITE_SOURCE_ATOP:\
-        f_s = CTX_PORTER_DUFF_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_1;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-        f_s = CTX_PORTER_DUFF_1;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1;\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-        f_s = CTX_PORTER_DUFF_ALPHA;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-        f_s = CTX_PORTER_DUFF_1;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-     default:\
-     case CTX_COMPOSITE_CLEAR:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-  }\
-}
 
-#if 1
-static void
-ctx_u8_source_over_normal_color (int components,
-                                 CtxRasterizer         *rasterizer,
-                                 uint8_t * __restrict__ dst,
-                                 uint8_t * __restrict__ src,
-                                 int                    x0,
-                                 uint8_t * __restrict__ coverage,
-                                 int                    count)
+static void vt_state_sixel (VT *vt, int byte)
 {
-  uint8_t tsrc[5];
-  *((uint32_t*)tsrc) = *((uint32_t*)src);
-  ctx_u8_associate_alpha (components, tsrc);
-
-    while (count--)
+  // https://ttssh2.osdn.jp/manual/4/en/about/ctrlseq.html
+  // and in "\033\" rather than just "\033", this would cause
+  // a stray char
+  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
     {
-      int cov = *coverage;
-      if (cov)
-      {
-        if (cov == 255)
+      vt_sixels (vt, vt->argument_buf);
+      if (byte == 27)
         {
-        for (int c = 0; c < components; c++)
-          dst[c] = (tsrc[c]) + (dst[c] * (255-(tsrc[components-1])))/(255);
+          vt->state = vt_state_swallow;
         }
-        else
+      else
         {
-          for (int c = 0; c < components; c++)
-            dst[c] = (tsrc[c] * cov)/255 + (dst[c] * ((255*255)-(tsrc[components-1] * cov)))/(255*255);
-         }
-      }
-      coverage ++;
-      dst+=components;
+          vt->state = vt_state_neutral;
+        }
+    }
+  else
+    {
+      vt_argument_buf_add (vt, byte);
+      //fprintf (stderr, "\r%i ", vt->argument_buf_len);
     }
 }
-#endif
 
-#if CTX_AVX2
-
-#if !__COSMOPOLITAN__
-#include <stdalign.h>
-#endif
-#endif
+//void add_tab (Ctx *ctx, const char *commandline, int can_launch);
+//void vt_screenshot (const char *output_path);
 
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+static void vt_state_apc_generic (VT *vt, int byte)
 {
-    int x = 0;
-
-#if CTX_AVX2
-    if (((size_t)(dst) & 31))
-#endif
-    {
-      for (; (x < count) 
-#if CTX_AVX2
-                        && ((size_t)(dst)&31)
-#endif
-                      ; 
-                      x++)
+  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
     {
-      int cov = coverage[0];
-      if (cov)
+      if (vt->argument_buf[1] == 'G') /* graphics - from kitty */
+        {
+          vt_gfx (vt, vt->argument_buf);
+        }
+      else if (vt->argument_buf[1] == 'C') /* launch command */
       {
-        uint32_t *sip = ((uint32_t*)(tsrc));
-        uint32_t si = *sip;
-        uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-        uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-        uint32_t *dip = ((uint32_t*)(dst));
-        int      si_a  = si >> CTX_RGBA8_A_SHIFT;
-        uint32_t di = *dip;
-        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-        int ir_cov_si_a = 255-((cov*si_a)/255);
-        *((uint32_t*)(dst)) = 
-         (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
-         (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
-      }
-      dst += 4;
-      tsrc += 4;
-      coverage ++;
-    }
-  }
+        if (vt->can_launch)
+        {
+          int   can_launch = 0;
+          int   no_title = 0;
+          int   no_move = 0;
+          int   no_resize = 0;
+          int   layer = 0;
+  // escape subsequent arguments so that we dont have to pass a string?
+          float x = -1.0;
+          float y = -1.0;
+          int   z = 0;
+          float width = -1.0;
+          float height = -1.0;
+
+          for (int i=2; vt->argument_buf[i]; i++)
+          {
+            if (!strncmp (&vt->argument_buf[i], "can_launch=1", strlen ("can_launch=1")))
+              can_launch = 1;
+            if (!strncmp (&vt->argument_buf[i], "no_title=1", strlen("no_title=1")))
+              no_title = 1;
+            if (!strncmp (&vt->argument_buf[i], "no_move=1", strlen("no_move=1")))
+              no_move = 1;
+            else if (!strncmp (&vt->argument_buf[i], "z=", 2))
+              z=atoi(&vt->argument_buf[i]+strlen("z="));
+            else if (!strncmp (&vt->argument_buf[i], "x=", 2))
+              x=atof(&vt->argument_buf[i]+strlen("x="));
+            else if (!strncmp (&vt->argument_buf[i], "y=", 2))
+              y=atof(&vt->argument_buf[i]+strlen("y="));
+            else if (!strncmp (&vt->argument_buf[i], "width=", 6))
+              width=atof(&vt->argument_buf[i]+strlen("width="));
+            else if (!strncmp (&vt->argument_buf[i], "height=", 7))
+              height=atof(&vt->argument_buf[i]+strlen("height="));
+          }
 
-#if CTX_AVX2
-    __m256i a_mask = _mm256_set1_epi32 (0xFF000000);
-    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
-    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
-    __m256i x00ff =   _mm256_set1_epi16(255);
-    __m256i x0101 =   _mm256_set1_epi16(0x0101);
-    __m256i x0080 =   _mm256_set1_epi16(0x0080);
+          if (width + no_resize + layer + height + x + y + no_title + no_move + z + can_launch) {};
 
-    int trailer = (count - x) % 8 + 1;
+          char *sep = strchr(vt->argument_buf, ';');
+          if (sep)
+          {
+            //fprintf (stderr, "[%s]", sep +  1);
+            if (!strncmp (sep + 1, "fbsave", 6))
+            {
+              // vt_screenshot (sep + 8);
+            }
+            else
+            {
+          //  add_tab (ctx, sep + 1, can_launch);
+            }
+          }
+        }
 
-    for (; x <= count-trailer; x+=8)
+      }
+      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
+    }
+  else
     {
-     if (((uint64_t*)(coverage))[0])
-     {
-       __m256i xcov;
-       __m256i x1_minus_cov_mul_a;
-       __m256i xsrc = _mm256_load_si256((__m256i*)(tsrc));
-
-       __m256i xsrc_a = _mm256_and_si256(xsrc, a_mask);
-       xsrc_a = _mm256_srli_epi32 (xsrc_a, 24);
-       xsrc_a |= xsrc_a << 16;
-       xcov  = _mm256_set_epi32(coverage[7],
-                                coverage[6],
-                                coverage[5],
-                                coverage[4],
-                                coverage[3],
-                                coverage[2],
-                                coverage[1],
-                                coverage[0]
-                                );
-       xcov |= xcov << 16;
-       __m256i xcov_mul_a  =
-         _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(xcov,  xsrc_a), x0080), x0101);
-       x1_minus_cov_mul_a = _mm256_sub_epi16(x00ff, xcov_mul_a);
-
-       __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
-       __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
-       __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
-       __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
-       __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-        
-       dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
-       dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
-
-       src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
-       src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi, xcov), x0080), x0101);
-
-       dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
-       dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
-
-       _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
-     }
+      vt_argument_buf_add (vt, byte);
+    }
+}
 
-      dst  += 4 * 8;
-      tsrc += 4 * 8;
-      coverage += 8;
+#if 0
+    {"_G..\e\", 0, vtcmd_delete_n_chars, VT102}, /* ref:none id: <a 
href='https://sw.kovidgoyal.net/kitty/graphics-protocol.html'>kitty graphics</a> */ "
+    {"_A..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id:  <a 
href='https://github.com/hodefoting/atty/'>atty</a> audio input/output */ "
+    {"_C..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id:  run command */ "
+#endif
+static void vt_state_apc (VT *vt, int byte)
+{
+  if (byte == 'A')
+    {
+      vt_argument_buf_add (vt, byte);
+      vt->state = vt_state_apc_audio;
     }
-    for (; x < count; x++)
+  else if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
     {
-      int cov = coverage[0];
-      if (cov)
-      {
-        uint32_t *sip = ((uint32_t*)(tsrc));
-        uint32_t si = *sip;
-        uint32_t *dip = ((uint32_t*)(dst));
-        uint32_t di = *dip;
-
-        uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-        uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-        int      si_a  = si >> CTX_RGBA8_A_SHIFT;
-
-        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-        int ir_cov_si_a = 255-((cov*si_a)/255);
-        *((uint32_t*)(dst)) = 
-         (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
-         (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
-      }
-      dst  += 4;
-      tsrc += 4;
-      coverage ++;
+      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
+    }
+  else
+    {
+      vt_argument_buf_add (vt, byte);
+      vt->state = vt_state_apc_generic;
     }
-#endif
 }
 
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_fragment) (CTX_COMPOSITE_ARGUMENTS)
+static void vt_state_esc_foo (VT *vt, int byte)
 {
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-#if CTX_DITHER
-  int dither_red_blue = rasterizer->format->dither_red_blue;
-  int dither_green = rasterizer->format->dither_green;
-#endif
-  CtxFragment fragment = rasterizer->fragment;
-  int fudge = ((size_t)(dst) & 31);
-#if CTX_AVX2
-  alignas(32)
-#endif
-  uint8_t _tsrc[4 * (count + fudge)];
-  uint8_t *tsrc = &_tsrc[fudge];
-  fragment (rasterizer, u0, v0, tsrc, count, ud, vd);
-#if CTX_DITHER
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-#endif
-  for (int x = 0; x < count ; x++)
-  {
-    ctx_RGBA8_associate_alpha (tsrc);
-#if CTX_DITHER
-    ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
-#endif
-    tsrc += 4;
-#if CTX_DITHER
-    u0 += ud;
-    v0 += vd;
-#endif
-  }
-  tsrc = &_tsrc[fudge];
-
-  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
-                       dst, src, x0, coverage, count, tsrc);
-
+  vt_argument_buf_add (vt, byte);
+  vt->state = vt_state_neutral;
+  handle_sequence (vt, vt->argument_buf);
 }
 
-
-#if CTX_GRADIENTS
-#if CTX_INLINED_GRADIENTS
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_linear_gradient) (CTX_COMPOSITE_ARGUMENTS)
+static void vt_state_esc_sequence (VT *vt, int byte)
 {
-  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);
-  float linear_gradient_dx = g->linear_gradient.dx;
-  float linear_gradient_dy = g->linear_gradient.dy;
-  float linear_gradient_rdelta = g->linear_gradient.rdelta;
-  float linear_gradient_start = g->linear_gradient.start;
-  float linear_gradient_length = g->linear_gradient.length;
-#if CTX_DITHER
-  int dither_red_blue = rasterizer->format->dither_red_blue;
-  int dither_green = rasterizer->format->dither_green;
-#endif
-  int fudge = ((size_t)(dst) & 31);
-#if CTX_AVX2
-  alignas(32)
-#endif
-  uint8_t _tsrc[4 * (count + fudge)];
-  uint8_t *tsrc = &_tsrc[fudge];
-  for (int x = 0; x < count ; x++)
-  {
-      float vv = ( ( (linear_gradient_dx * u0 + linear_gradient_dy * v0) / linear_gradient_length) -
-            linear_gradient_start) * (linear_gradient_rdelta);
-      uint32_t *tsrci = (uint32_t*)tsrc;
-#if CTX_GRADIENT_CACHE
-      uint32_t *cachei = ((uint32_t*)(&ctx_gradient_cache_u8_a[ctx_grad_index(vv)][0]));
-      *tsrci = *cachei;
-#else
-      ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, tsrc);
-      ctx_RGBA8_associate_alpha (tsrc);
-#endif
-#if CTX_DITHER
-      ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
-#endif
-
-    tsrc += 4;
-    u0 += ud;
-    v0 += vd;
-  }
-  tsrc = &_tsrc[fudge];
-  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
-               dst, src, x0, coverage, count, tsrc);
+  if (_vt_handle_control (vt, byte) == 0)
+    {
+      if (byte == 27)
+        {
+        }
+      else if (byte >= '@' && byte <= '~')
+        {
+          vt_argument_buf_add (vt, byte);
+          vt->state = vt_state_neutral;
+          handle_sequence (vt, vt->argument_buf);
+        }
+      else
+        {
+          vt_argument_buf_add (vt, byte);
+        }
+    }
 }
 
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_radial_gradient) (CTX_COMPOSITE_ARGUMENTS)
+static void vt_state_esc (VT *vt, int byte)
 {
-  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);
-  float radial_gradient_x0 = g->radial_gradient.x0;
-  float radial_gradient_y0 = g->radial_gradient.y0;
-  float radial_gradient_r0 = g->radial_gradient.r0;
-  float radial_gradient_rdelta = g->radial_gradient.rdelta;
-#if CTX_DITHER
-  int dither_red_blue = rasterizer->format->dither_red_blue;
-  int dither_green = rasterizer->format->dither_green;
-#endif
-  int fudge = ((size_t)(dst) & 31);
-#if CTX_AVX2
-  alignas(32)
-#endif
-  uint8_t _tsrc[4 * (count + fudge)];
-  uint8_t *tsrc = &_tsrc[fudge];
-  for (int x = 0; x < count ; x++)
-  {
-      float vv = ctx_hypotf (radial_gradient_x0 - u0, radial_gradient_y0 - v0);
-            vv = (vv - radial_gradient_r0) * (radial_gradient_rdelta);
-#if CTX_GRADIENT_CACHE
-      uint32_t *tsrcp = (uint32_t*)tsrc;
-      uint32_t *cp = ((uint32_t*)(&ctx_gradient_cache_u8_a[ctx_grad_index(vv)][0]));
-      *tsrcp = *cp;
-#else
-      ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, tsrc);
-      ctx_RGBA8_associate_alpha (tsrc);
-#endif
+  if (_vt_handle_control (vt, byte) == 0)
+    switch (byte)
+      {
+        case 27: /* ESCape */
+          break;
+        case ')':
+        case '#':
+        case '(':
+          {
+            char tmp[]= {byte, '\0'};
+            vt_argument_buf_reset (vt, tmp);
+            vt->state = vt_state_esc_foo;
+          }
+          break;
+        case '[':
+        case '%':
+        case '+':
+        case '*':
+          {
+            char tmp[]= {byte, '\0'};
+            vt_argument_buf_reset (vt, tmp);
+            vt->state = vt_state_esc_sequence;
+          }
+          break;
 
-#if CTX_DITHER
-      ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
+#if 0
+    {"Psixel_data\e\",  0, , }, /* id: sixels */ "
 #endif
 
-    tsrc += 4;
-    u0 += ud;
-    v0 += vd;
-  }
-
-  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
-               dst, src, x0, coverage, count, &_tsrc[fudge]);
+        case 'P':
+          {
+            char tmp[]= {byte, '\0'};
+            vt_argument_buf_reset (vt, tmp);
+            vt->state = vt_state_sixel;
+          }
+          break;
+        case ']':
+          {
+            char tmp[]= {byte, '\0'};
+            vt_argument_buf_reset (vt, tmp);
+            vt->state = vt_state_osc;
+          }
+          break;
+        case '^':  // privacy message
+        case '_':  // APC
+        case 'X':  // SOS
+          {
+            char tmp[]= {byte, '\0'};
+            vt_argument_buf_reset (vt, tmp);
+            vt->state = vt_state_apc;
+          }
+          break;
+        default:
+          {
+            char tmp[]= {byte, '\0'};
+            tmp[0]=byte;
+            vt->state = vt_state_neutral;
+            handle_sequence (vt, tmp);
+          }
+          break;
+      }
 }
 
-
-#endif
-#endif
-
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color) (CTX_COMPOSITE_ARGUMENTS)
+static void vt_state_neutral (VT *vt, int byte)
 {
-#if 0
-  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, clip, x0, coverage, count);
-  return;
-#endif
+  if (CTX_UNLIKELY(_vt_handle_control (vt, byte) != 0))
+    return;
+  if (CTX_LIKELY(byte != 27))
   {
-    uint8_t tsrc[4];
-    memcpy (tsrc, src, 4);
-    ctx_RGBA8_associate_alpha (tsrc);
-    uint8_t a = src[3];
-    int x = 0;
-
-#if CTX_AVX2
-    if ((size_t)(dst) & 31)
-#endif
-    {
-      uint32_t *sip = ((uint32_t*)(tsrc));
-      uint32_t si = *sip;
-      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-      if (a==255)
+    if (vt_decoder_feed (vt, byte) )
+      return;
+    if (vt->charset[vt->shifted_in] != 0 &&
+        vt->charset[vt->shifted_in] != 'B')
       {
-
-      for (; (x < count) 
-#if CTX_AVX2
-                      && ((size_t)(dst)&31)
-#endif
-                      ; 
-                      x++)
-    {
-      int cov = coverage[0];
-      if (cov)
+        char **charmap;
+        switch (vt->charset[vt->shifted_in])
+          {
+            case 'A':
+              charmap = charmap_uk;
+              break;
+            case 'B':
+              charmap = charmap_ascii;
+              break;
+            case '0':
+              charmap = charmap_graphics;
+              break;
+            case '1':
+              charmap = charmap_cp437;
+              break;
+            case '2':
+              charmap = charmap_graphics;
+              break;
+            default:
+              charmap = charmap_ascii;
+              break;
+          }
+        if ( (vt->utf8_holding[0] >= ' ') && (vt->utf8_holding[0] <= '~') )
+          {
+            _vt_add_str (vt, charmap[vt->utf8_holding[0]-' ']);
+          }
+      }
+    else
       {
-        int r_cov = 255-cov;
-        uint32_t di = *((uint32_t*)(dst));
-        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-        *((uint32_t*)(dst)) = 
-         (((si_rb * cov + di_rb * r_cov) >> 8) & CTX_RGBA8_RB_MASK) |
-         (((si_ga * cov + di_ga * r_cov) >> 8) & CTX_RGBA8_GA_MASK);
+        // ensure vt->utf8_holding contains a valid utf8
+        uint32_t codepoint;
+        uint32_t state = 0;
+        for (int i = 0; vt->utf8_holding[i]; i++)
+          { utf8_decode (&state, &codepoint, vt->utf8_holding[i]); }
+        if (state != UTF8_ACCEPT)
+          {
+            /* otherwise mangle it so that it does */
+            vt->utf8_holding[0] &= 127;
+            vt->utf8_holding[1] = 0;
+            if (vt->utf8_holding[0] == 0)
+              { vt->utf8_holding[0] = 32; }
+          }
+        _vt_add_str (vt, (char *) vt->utf8_holding);
       }
-      dst += 4;
-      coverage ++;
-    }
   }
-    else
+  else // ESCape
+  {
+    vt->state = vt_state_esc;
+  }
+}
+
+int vt_poll (VT *vt, int timeout)
+{
+  if (!vt) return 0;
+  int read_size = sizeof (vt->buf);
+  int got_data = 0;
+
+  // read_size 1m1.142s
+  // read_size*10  52s
+  // read_size*5   53.8s
+  // read_size*4   53.78s
+  // read_size*3   .....s
+  // read_size*2   56.99s
+  int remaining_chars = read_size * 3;// * 100;
+  int len = 0;
+  vt_audio_task (vt, 0);
+#if 1
+  if (vt->cursor_visible && vt->smooth_scroll)
     {
-      int si_a = si >> CTX_RGBA8_A_SHIFT;
-      for (; (x < count) 
-#if CTX_AVX2
-                      && ((size_t)(dst)&31)
+      remaining_chars = vt->cols / 2;
+    }
 #endif
-                      ; 
-                      x++)
+  read_size = MIN (read_size, remaining_chars);
+  long start_ticks = ctx_ticks ();
+  long ticks = start_ticks;
+  while (remaining_chars > 0 &&
+         vt_waitdata (vt, 0) &&
+         ( ticks - start_ticks < timeout ||  vt->state == vt_state_ctx))
+    {
+  if (vt->in_smooth_scroll)
+    {
+      remaining_chars = 1;
+      // XXX : need a bail condition -
+      // /// so that we can stop accepting data until autowrap or similar
+    }
+      len = vt_read (vt, vt->buf, read_size);
+      if (len >0)
       {
-        int cov = *coverage;
-        if (cov)
-        {
-          uint32_t di = *((uint32_t*)(dst));
-          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-          int ir_cov_si_a = 255-((cov*si_a)>>8);
-          *((uint32_t*)(dst)) = 
-           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
-           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
-        }
-        dst += 4;
-        coverage ++;
+     // fwrite (vt->buf, len, 1, vt->log);
+     // fwrite (vt->buf, len, 1, stdout);
+      }
+      for (int i = 0; i < len; i++)
+        { vt->state (vt, vt->buf[i]); }
+      // XXX allow state to break out in ctx mode on flush
+      got_data+=len;
+      remaining_chars -= len;
+      if (vt->state == vt_state_ctx) {
+         if (remaining_chars < read_size)
+         {
+           remaining_chars = read_size * 2;
+         }
       }
+      vt_audio_task (vt, 0);
+      ticks = ctx_ticks ();
     }
+  if (got_data < 0)
+    {
+      if (kill (vt->vtpty.pid, 0) != 0)
+        {
+          vt->vtpty.done = 1;
+        }
     }
+  return got_data;
+}
 
-#if CTX_AVX2
-                    
-    __m256i xsrc = _mm256_set1_epi32( *((uint32_t*)tsrc)) ;
-    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
-    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
-    __m256i x00ff =   _mm256_set1_epi16(255);
-    __m256i x0101 =   _mm256_set1_epi16(0x0101);
-    __m256i x0080 =   _mm256_set1_epi16(0x0080);
-
-    int trailer = (count - x) % 8 + 1;
+/******/
 
-    for (; x <= count-trailer; x+=8)
-    {
-      __m256i xcov;
-      __m256i x1_minus_cov_mul_a;
-     
-     if (((uint64_t*)(coverage))[0])
-     {
-       if (CTX_LIKELY(((uint64_t*)(coverage))[0] != 0xffffffffffffffff))
-       {
-         xcov  = _mm256_set_epi32(coverage[7],
-                                  coverage[6],
-                                  coverage[5],
-                                  coverage[4],
-                                  coverage[3],
-                                  coverage[2],
-                                  coverage[1],
-                                  coverage[0]
-                                  );
-         xcov |= xcov << 16;
-
-        x1_minus_cov_mul_a = 
-           _mm256_sub_epi16(x00ff, _mm256_mulhi_epu16 (
-                   _mm256_adds_epu16 (_mm256_mullo_epi16(xcov,
-                                      _mm256_set1_epi16(a)), x0080), x0101));
-       }
-       else
-       {
-          if (a == 255)
-          {
-            _mm256_store_si256((__m256i*)dst, xsrc);
-            dst += 4 * 8;
-            coverage += 8;
-            continue;
-          }
+static const char *keymap_vt52[][2]=
+{
+  {"up",    "\033A" },
+  {"down",  "\033B" },
+  {"right", "\033C" },
+  {"left",  "\033D" },
+};
 
-          xcov = x00ff;
-          x1_minus_cov_mul_a = _mm256_set1_epi16(255-a);
-       }
-      __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
-      __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
-      __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
-      __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
-      __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-        
-      dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
-      dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
+static const char *keymap_application[][2]=
+{
+  {"up",    "\033OA" },
+  {"down",  "\033OB" },
+  {"right", "\033OC" },
+  {"left",  "\033OD" },
+};
 
-      src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
-      src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi,  xcov), x0080), x0101);
+static const char *keymap_general[][2]=
+{
+  {"up",             "\033[A"},
+  {"down",           "\033[B"},
+  {"right",          "\033[C"},
+  {"left",           "\033[D"},
+  {"end",            "\033[F"},
+  {"home",           "\033[H"},
+  {"shift-up",       "\033[1;2A"},
+  {"shift-down",     "\033[1;2B"},
+  {"shift-right",    "\033[1;2C"},
+  {"shift-left",     "\033[1;2D"},
+  {"alt-a",          "\033a"},
+  {"alt-b",          "\033b"},
+  {"alt-c",          "\033c"},
+  {"alt-d",          "\033d"},
+  {"alt-e",          "\033e"},
+  {"alt-f",          "\033f"},
+  {"alt-g",          "\033g"},
+  {"alt-h",          "\033h"},
+  {"alt-i",          "\033i"},
+  {"alt-j",          "\033j"},
+  {"alt-k",          "\033k"},
+  {"alt-l",          "\033l"},
+  {"alt-m",          "\033m"},
+  {"alt-n",          "\033n"},
+  {"alt-o",          "\033o"},
+  {"alt-p",          "\033p"},
+  {"alt-q",          "\033q"},
+  {"alt-r",          "\033r"},
+  {"alt-s",          "\033s"},
+  {"alt-t",          "\033t"},
+  {"alt-u",          "\033u"},
+  {"alt-v",          "\033v"},
+  {"alt-w",          "\033w"},
+  {"alt-x",          "\033x"},
+  {"alt-y",          "\033y"},
+  {"alt-z",          "\033z"},
+  {"alt- ",          "\033 "},
+  {"alt-space",      "\033 "},
+  {"alt-0",          "\0330"},
+  {"alt-1",          "\0331"},
+  {"alt-2",          "\0332"},
+  {"alt-3",          "\0333"},
+  {"alt-4",          "\0334"},
+  {"alt-5",          "\0335"},
+  {"alt-6",          "\0336"},
+  {"alt-7",          "\0337"},
+  {"alt-8",          "\0338"},
+  {"alt-9",          "\0339"},
+  {"alt-return",     "\033\r"},
+  {"alt-backspace",  "\033\177"},
+  {"alt-up",         "\033[1;3A"},
+  {"alt-down",       "\033[1;3B"},
+  {"alt-right",      "\033[1;3C"},
+  {"alt-left",       "\033[1;3D"},
+  {"shift-alt-up",   "\033[1;4A"},
+  {"shift-alt-down", "\033[1;4B"},
+  {"shift-alt-right","\033[1;4C"},
+  {"shift-alt-left", "\033[1;4D"},
+  {"control-space",  "\000"},
+  {"control-up",     "\033[1;5A"},
+  {"control-down",   "\033[1;5B"},
+  {"control-right",  "\033[1;5C"},
+  {"control-left",   "\033[1;5D"},
+  {"shift-control-up",    "\033[1;6A"},
+  {"shift-control-down",  "\033[1;6B"},
+  {"shift-control-right", "\033[1;6C"},
+  {"shift-control-left",  "\033[1;6D"},
+  {"insert",         "\033[2~"},
+  {"delete",         "\033[3~"},
+  {"control-delete", "\033[3,5~"},
+  {"shift-delete",   "\033[3,2~"},
+  {"control-shift-delete",  "\033[3,6~"},
+  {"page-up",        "\033[5~"},
+  {"page-down",     "\033[6~"},
+  {"return",         "\r"},
+  {"shift-tab",      "\033Z"},
+  {"shift-return",   "\r"},
+  {"control-return", "\r"},
+  {"space",          " "},
+  {"shift-space",    " "},
+  {"control-a",      "\001"},
+  {"control-b",      "\002"},
+  {"control-c",      "\003"},
+  {"control-d",      "\004"},
+  {"control-e",      "\005"},
+  {"control-f",      "\006"},
+  {"control-g",      "\007"},
+  {"control-h",      "\010"},
+  {"control-i",      "\011"},
+  {"control-j",      "\012"},
+  {"control-k",      "\013"},
+  {"control-l",      "\014"},
+  {"control-m",      "\015"},
+  {"control-n",      "\016"},
+  {"control-o",      "\017"},
+  {"control-p",      "\020"},
+  {"control-q",      "\021"},
+  {"control-r",      "\022"},
+  {"control-s",      "\023"},
+  {"control-t",      "\024"},
+  {"control-u",      "\025"},
+  {"control-v",      "\026"},
+  {"control-w",      "\027"},
+  {"control-x",      "\030"},
+  {"control-y",      "\031"},
+  {"control-z",      "\032"},
+  {"escape",         "\033"},
+  {"tab",            "\t"},
+  {"backspace",      "\177"},
+  {"control-backspace", "\177"},
+  {"shift-backspace","\177"},
+  {"shift-tab",      "\033[Z"},
+
+  {"control-F1",     "\033[>11~"},
+  {"control-F2",     "\033[>12~"},
+  {"control-F3",     "\033[>13~"},
+  {"control-F4",     "\033[>14~"},
+  {"control-F5",     "\033[>15~"},
+
+  {"shift-F1",       "\033[?11~"},
+  {"shift-F2",       "\033[?12~"},
+  {"shift-F3",       "\033[?13~"},
+  {"shift-F4",       "\033[?14~"},
+  {"shift-F5",       "\033[?15~"},
+
+  {"F1",             "\033[11~"},  // hold screen   // ESC O P
+  {"F2",             "\033[12~"},  // print screen  //       Q
+  {"F3",             "\033[13~"},  // set-up                 R
+  {"F4",             "\033[14~"},  // data/talk              S
+  {"F5",             "\033[15~"},  // break
+  {"F6",             "\033[17~"},
+  {"F7",             "\033[18~"},
+  {"F8",             "\033[19~"},
+  {"F9",             "\033[20~"},
+  {"F10",            "\033[21~"},
+  {"F11",            "\033[22~"},
+  {"F12",            "\033[23~"},
+  {"control-/",       "\037"},
+  {"shift-control-/", "\037"},
+  {"control-[",       "\033"},
+  {"control-]",       "\035"},
+  {"shift-control-[", "\033"},
+  {"shift-control-]", "\031"},
+  {"shift-control-`", "\036"},
+  {"control-'",       "'"},
+  {"shift-control-'", "'"},
+  {"control-;",       ";"},
+  {"shift-control-;", ";"},
+  {"control-.",       "."},
+  {"shift-control-.", "."},
+  {"control-,",       ","},
+  {"shift-control-,", ","},
+  {"control-\\",      "\034"},
+  {"control-1",       "1"},
+  {"control-3",       "\033"},
+  {"control-4",       "\034"},
+  {"control-5",       "\035"},
+  {"control-6",       "\036"},
+  {"shift-control-6", "\036"},
+  {"control-7",       "\037"},
+  {"shift-control-7", "\036"},
+  {"control-8",       "\177"},
+  {"control-9",       "9"},
 
-      dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
-      dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
 
-      _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
-     }
+};
 
-      dst += 4 * 8;
-      coverage += 8;
-    }
+void ctx_client_lock (CtxClient *client);
+void ctx_client_unlock (CtxClient *client);
 
-    if (x < count)
+void vt_feed_keystring (VT *vt, CtxEvent *event, const char *str)
+{
+  if (vt->ctx_events)
+  {
+    if (!strcmp (str, "control-l") )
     {
-      uint32_t *sip = ((uint32_t*)(tsrc));
-      uint32_t si = *sip;
-      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-      int si_a = si >> CTX_RGBA8_A_SHIFT;
-      for (; x < count; x++)
-      {
-        int cov = *coverage;
-        if (cov)
-        {
-          uint32_t *dip = ((uint32_t*)(dst));
-          uint32_t di = *dip;
-          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-          int ir_cov_si_a = 255-((cov*si_a)>>8);
-          *((uint32_t*)(dst)) = 
-           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
-           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
-        }
-        dst      += 4;
-        coverage ++;
-      }
+      vt->ctx_events = 0;
+      return;
     }
-#endif
+    vt_write (vt, str, strlen (str) );
+    vt_write (vt, "\n", 1);
+    return;
   }
-}
+  if (!strncmp (str, "keyup",   5)) return;
+  if (!strncmp (str, "keydown", 7)) return;
 
+  if (!strcmp (str, "capslock")) return;
 
-
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color_solid) (CTX_COMPOSITE_ARGUMENTS)
-{
 #if 0
-  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, clip, x0, coverage, count);
-  return;
+  if (!strstr (str, "-page"))
+    vt_set_scroll (vt, 0);
 #endif
-    uint8_t *tsrc = src;
-    int x = 0;
 
-#if CTX_AVX2
-    if ((size_t)(dst) & 31)
-#endif
+  if (!strcmp (str, "idle") )
+     return;
+  else if (!strcmp (str, "shift-control-home"))
     {
-      uint32_t *sip = ((uint32_t*)(tsrc));
-      uint32_t si = *sip;
-      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-      for (; (x < count) 
-#if CTX_AVX2
-                      && ((size_t)(dst)&31)
-#endif
-                      ; 
-                      x++)
+      vt_set_scroll (vt, vt->scrollback_count);
+      vt_rev_inc (vt);
+      return;
+    }
+  else if (!strcmp (str, "shift-control-end"))
     {
-      int cov = coverage[0];
-      if (cov)
-      {
-        int r_cov = 255-cov;
-        uint32_t *dip = ((uint32_t*)(dst));
-        uint32_t di = *dip;
-        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-        *((uint32_t*)(dst)) = 
-         (((si_rb * cov + di_rb * r_cov) >> 8) & CTX_RGBA8_RB_MASK) |
-         (((si_ga * cov + di_ga * r_cov) >> 8) & CTX_RGBA8_GA_MASK);
-      }
-      dst += 4;
-      coverage ++;
+      int new_scroll = 0;
+      vt_set_scroll (vt, new_scroll);
+      vt_rev_inc (vt);
+      return;
     }
-  }
-
-#if CTX_AVX2
-                    
-    __m256i xsrc = _mm256_set1_epi32( *((uint32_t*)tsrc)) ;
-    int trailer = (count - x) % 8 + 1;
-    for (; x < count-trailer; x+=8)
+  else if (!strcmp (str, "shift-control-down"))
     {
-      __m256i xcov;
-      __m256i x1_minus_cov_mul_a;
-    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
-    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
-    __m256i x00ff =   _mm256_set1_epi16(255);
-    __m256i x0101 =   _mm256_set1_epi16(0x0101);
-    __m256i x0080 =   _mm256_set1_epi16(0x0080);
-     uint64_t cov = ((uint64_t*)(coverage))[0];
-     if (cov)
-     {
-       if (CTX_UNLIKELY(cov == 0xffffffffffffffff))
-       {
-         _mm256_store_si256((__m256i*)dst, xsrc);
-       }
-       else
-       {
-          __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
-          __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
-          __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
-          __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
-          __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-            
-          xcov  = _mm256_set_epi32(coverage[7],
-                                   coverage[6],
-                                   coverage[5],
-                                   coverage[4],
-                                   coverage[3],
-                                   coverage[2],
-                                   coverage[1],
-                                   coverage[0]
-                                   );
-          xcov |= xcov << 16;
-
-          x1_minus_cov_mul_a = _mm256_sub_epi16(x00ff, xcov);
-    
-          dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
-          dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
-    
-          src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
-          src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi,  xcov), x0080), x0101);
-    
-          dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
-          dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
-    
-          _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
-        }
-      }
-
-      dst += 4 * 8;
-      coverage += 8;
+      int new_scroll = vt_get_scroll (vt) - 1;
+      vt_set_scroll (vt, new_scroll);
+      vt_rev_inc (vt);
+      return;
     }
-
-    if (x < count)
+  else if (!strcmp (str, "shift-control-up"))
     {
-      uint32_t *sip = ((uint32_t*)(tsrc));
-      uint32_t si = *sip;
-      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
-      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
-      int si_a = si >> CTX_RGBA8_A_SHIFT;
-      for (; x < count; x++)
-      {
-        int cov = *coverage;
-        if (cov)
-        {
-          uint32_t *dip = ((uint32_t*)(dst));
-          uint32_t di = *dip;
-          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
-          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
-          int ir_cov_si_a = 255-((cov*si_a)>>8);
-          *((uint32_t*)(dst)) = 
-           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
-           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
-        }
-        dst += 4;
-        coverage ++;
-      }
+      int new_scroll = vt_get_scroll (vt) + 1;
+      vt_set_scroll (vt, new_scroll);
+      vt_rev_inc (vt);
+      return;
     }
-#endif
-}
+  else if (!strcmp (str, "shift-page-up") ||
+           !strcmp (str, "shift-control-page-up"))
+    {
+      int new_scroll = vt_get_scroll (vt) + vt_get_rows (vt) /2;
+      vt_set_scroll (vt, new_scroll);
+      vt_rev_inc (vt);
+      return;
+    }
+  else if (!strcmp (str, "shift-page-down") ||
+           !strcmp (str, "shift-control-page-down"))
+    {
+      int new_scroll = vt_get_scroll (vt) - vt_get_rows (vt) /2;
+      if (new_scroll < 0) { new_scroll = 0; }
+      vt_set_scroll (vt, new_scroll);
+      vt_rev_inc (vt);
+      return;
+    }
+  else if (!strcmp (str, "shift-control--") ||
+           !strcmp (str, "control--") )
+    {
+      float font_size = vt_get_font_size (vt);
+      //font_size /= 1.15;
+      font_size -=2;//= roundf (font_size);
+      if (font_size < 2) { font_size = 2; }
+      vt_set_font_size (vt, font_size);
+      vt_set_px_size (vt, vt->width, vt->height);
+      return;
+    }
+  else if (!strcmp (str, "shift-control-=") ||
+           !strcmp (str, "control-=") )
+    {
+      float font_size = vt_get_font_size (vt);
+      float old = font_size;
+      //font_size *= 1.15;
+      //
+      //font_size = roundf (font_size);
+      font_size+=2;
 
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_copy_normal) (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
-}
+      if (old == font_size) { font_size = old+1; }
+      if (font_size > 200) { font_size = 200; }
+      vt_set_font_size (vt, font_size);
+      vt_set_px_size (vt, vt->width, vt->height);
 
-static void
-ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
-}
+      return;
+    }
+  else if (!strcmp (str, "shift-control-r") )
+    {
+      vt_open_log (vt, "/tmp/ctx-vt");
+      return;
+    }
+  else if (!strcmp (str, "shift-control-l") )
+    {
+      vt_set_local (vt, !vt_get_local (vt) );
+      return;
+    }
+  else if (!strncmp (str, "mouse-", 5) )
+    {
+      int cw = vt_cw (vt);
+      int ch = vt_ch (vt);
+      if (!strncmp (str + 6, "motion", 6) )
+        {
+          int x = 0, y = 0;
+          char *s = strchr (str, ' ');
+          if (s)
+            {
+              x = atoi (s);
+              s = strchr (s + 1, ' ');
+              if (s)
+                {
+                  y = atoi (s);
+                  vt_mouse (vt, event, VT_MOUSE_MOTION, 1, x/cw + 1, y/ch + 1, x, y);
+                }
+            }
+        }
+      else if (!strncmp (str + 6, "press", 5) )
+        {
+          int x = 0, y = 0, b = 0;
+          char *s = strchr (str, ' ');
+          if (s)
+            {
+              x = atoi (s);
+              s = strchr (s + 1, ' ');
+              if (s)
+                {
+                  y = atoi (s);
+                  s = strchr (s + 1, ' ');
+                  if (s)
+                  {
+                    b = atoi (s);
+                  }
+                  vt_mouse (vt, event, VT_MOUSE_PRESS, b, x/cw + 1, y/ch + 1, x, y);
+                }
+            }
+          //clients[active].drawn_rev = 0;
+        }
+      else if (!strncmp (str + 6, "drag", 4) )
+        {
+          int x = 0, y = 0, b = 0; // XXX initialize B
+          char *s = strchr (str, ' ');
+          if (s)
+            {
+              x = atoi (s);
+              s = strchr (s + 1, ' ');
+              if (s)
+                {
+                  y = atoi (s);
+                  if (s)
+                  {
+                    b = atoi (s);
+                  }
+                  vt_mouse (vt, event, VT_MOUSE_DRAG, b, x/cw + 1, y/ch + 1, x, y);
+                }
+            }
+          //clients[active].drawn_rev = 0;
+        }
+      else if (!strncmp (str + 6, "release", 7) )
+        {
+          int x = 0, y = 0, b = 0;
+          char *s = strchr (str, ' ');
+          if (s)
+            {
+              x = atoi (s);
+              s = strchr (s + 1, ' ');
+              if (s)
+                {
+                  y = atoi (s);
+                  s = strchr (s + 1, ' ');
+                  if (s)
+                  {
+                    b = atoi (s);
+                  }
+                  vt_mouse (vt, event, VT_MOUSE_RELEASE, b, x/cw + 1, y/ch + 1, x, y);
+                }
+            }
+          //clients[active].drawn_rev = 0;
+          // queue-draw
+        }
+      return;
+    }
 
-static void
-ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)
-{
-  switch (components)
+  if (vt->scroll_on_input)
   {
-     case 3:
-       ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2];
-      /* FALLTHROUGH */
-     case 2:
-       *((uint16_t*)(blended)) = *((uint16_t*)(src));
-       ctx_u8_associate_alpha (components, blended);
-       break;
-     case 5:
-       ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4];
-       /* FALLTHROUGH */
-     case 4:
-       *((uint32_t*)(blended)) = *((uint32_t*)(src));
-       ctx_u8_associate_alpha (components, blended);
-       break;
-     default:
-       {
-         uint8_t alpha = src[components-1];
-         for (int i = 0; i<components - 1;i++)
-           blended[i] = (src[i] * alpha)/255;
-         blended[components-1]=alpha;
-       }
-       break;
+    vt->scroll = 0.0;
   }
-}
-
-/* branchless 8bit add that maxes out at 255 */
-static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b)
-{
-  uint16_t s = (uint16_t)a+b;
-  return -(s>>8) | (uint8_t)s;
-}
-
-#if CTX_BLENDING_AND_COMPOSITING
-
-#define ctx_u8_blend_define(name, CODE) \
-static void \
-ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)\
-{\
-  uint8_t *s=src; uint8_t b[components];\
-  ctx_u8_deassociate_alpha (components, dst, b);\
-    CODE;\
-  blended[components-1] = src[components-1];\
-  ctx_u8_associate_alpha (components, blended);\
-}
 
-#define ctx_u8_blend_define_seperable(name, CODE) \
-        ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
 
-ctx_u8_blend_define_seperable(multiply,     blended[c] = (b[c] * s[c])/255;)
-ctx_u8_blend_define_seperable(screen,       blended[c] = s[c] + b[c] - (s[c] * b[c])/255;)
-ctx_u8_blend_define_seperable(overlay,      blended[c] = b[c] < 127 ? (s[c] * b[c])/255 :
-                                                         s[c] + b[c] - (s[c] * b[c])/255;)
-ctx_u8_blend_define_seperable(darken,       blended[c] = ctx_mini (b[c], s[c]))
-ctx_u8_blend_define_seperable(lighten,      blended[c] = ctx_maxi (b[c], s[c]))
-ctx_u8_blend_define_seperable(color_dodge,  blended[c] = b[c] == 0 ? 0 :
-                                     s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c])))
-ctx_u8_blend_define_seperable(color_burn,   blended[c] = b[c] == 1 ? 1 :
-                                     s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c]))
-ctx_u8_blend_define_seperable(hard_light,   blended[c] = s[c] < 127 ? (b[c] * s[c])/255 :
-                                                          b[c] + s[c] - (b[c] * s[c])/255;)
-ctx_u8_blend_define_seperable(difference,   blended[c] = (b[c] - s[c]))
-ctx_u8_blend_define_seperable(divide,       blended[c] = s[c]?(255 * b[c]) / s[c]:0)
-ctx_u8_blend_define_seperable(addition,     blended[c] = ctx_sadd8 (s[c], b[c]))
-ctx_u8_blend_define_seperable(subtract,     blended[c] = ctx_maxi(0, s[c]-b[c]))
-ctx_u8_blend_define_seperable(exclusion,    blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255))
-ctx_u8_blend_define_seperable(soft_light,
-  if (s[c] <= 255/2)
-  {
-    blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255);
-  }
+  if (vt->state == vt_state_vt52)
+    {
+      for (unsigned int i = 0; i<sizeof (keymap_vt52) /sizeof (keymap_vt52[0]); i++)
+        if (!strcmp (str, keymap_vt52[i][0]) )
+          { str = keymap_vt52[i][1]; goto done; }
+    }
   else
-  {
-    int d;
-    if (b[c] <= 255/4)
-      d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255;
-    else
-      d = ctx_sqrtf(b[c]/255.0) * 255.4;
-    blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255;
-  }
-)
+    {
+      if (vt->cursor_key_application)
+        {
+          for (unsigned int i = 0; i<sizeof (keymap_application) /sizeof (keymap_application[0]); i++)
+            if (!strcmp (str, keymap_application[i][0]) )
+              { str = keymap_application[i][1]; goto done; }
+        }
+    }
 
-static int ctx_int_get_max (int components, int *c)
-{
-  int max = 0;
-  for (int i = 0; i < components - 1; i ++)
-  {
-    if (c[i] > max) max = c[i];
-  }
-  return max;
-}
 
-static int ctx_int_get_min (int components, int *c)
-{
-  int min = 400;
-  for (int i = 0; i < components - 1; i ++)
-  {
-    if (c[i] < min) min = c[i];
-  }
-  return min;
+  if (!strcmp (str, "return") )
+    {
+      if (vt->cr_on_lf)
+        { str = "\r\n"; }
+      else
+        { str = "\r"; }
+      goto done;
+    }
+  if (!strcmp (str, "control-space") ||
+      !strcmp (str, "control-`") ||
+      !strcmp (str, "control-2") ||
+      !strcmp (str, "shift-control-2") ||
+      !strcmp (str, "shift-control-space") )
+    {
+      str = "\0\0";
+      vt_write (vt, str, 1);
+      return;
+    }
+  for (unsigned int i = 0; i< sizeof (keymap_general) /
+                              sizeof (keymap_general[0]); i++)
+    if (!strcmp (str, keymap_general[i][0]) )
+      {
+        str = keymap_general[i][1];
+        break;
+      }
+done:
+  if (strlen (str) )
+    {
+      if (vt->local_editing)
+        {
+          for (int i = 0; str[i]; i++)
+            {
+              vt->state (vt, str[i]);
+            }
+        }
+      else
+        {
+          vt_write (vt, str, strlen (str) );
+        }
+    }
 }
 
-static int ctx_int_get_lum (int components, int *c)
+void vt_paste (VT *vt, const char *str)
 {
-  switch (components)
-  {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         int sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
-            break;
-  }
+  if (vt->bracket_paste)
+    {
+      vt_write (vt, "\033[200~", 6);
+    }
+  vt_feed_keystring (vt, NULL, str);
+  if (vt->bracket_paste)
+    {
+      vt_write (vt, "\033[201~", 6);
+    }
 }
 
-static int ctx_u8_get_lum (int components, uint8_t *c)
+const char *vt_find_shell_command (void)
 {
-  switch (components)
+  if (access ("/.flatpak-info", F_OK) != -1)
   {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         int sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
-            break;
+    static char ret[512];
+    char buf[256];
+    FILE *fp = popen("flatpak-spawn --host getent passwd $USER|cut -f 7 -d :", "r");
+    if (fp)
+    {
+      while (fgets (buf, sizeof(buf), fp) != NULL)
+      {
+        if (buf[strlen(buf)-1]=='\n')
+          buf[strlen(buf)-1]=0;
+        sprintf (ret, "flatpak-spawn --env=TERM=xterm --host %s", buf);
+      }
+      pclose (fp);
+      return ret;
+    }
   }
-}
-static int ctx_u8_get_sat (int components, uint8_t *c)
-{
-  switch (components)
+
+  if (getenv ("SHELL"))
   {
-    case 3:
-    case 4:
-            { int r = c[0];
-              int g = c[1];
-              int b = c[2];
-              return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b));
-            }
-            break;
-    case 1:
-    case 2:
-            return 0.0;
-            break;
-    default:
-       {
-         int min = 1000;
-         int max = -1000;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           if (c[i] < min) min = c[i];
-           if (c[i] > max) max = c[i];
-         }
-         return max-min;
-       }
-       break;
+    return getenv ("SHELL");
   }
+  int i;
+  const char *command = NULL;
+  struct stat stat_buf;
+  static char *alts[][2] =
+  {
+    {"/bin/bash",     "/bin/bash"},
+    {"/usr/bin/bash", "/usr/bin/bash"},
+    {"/bin/sh",       "/bin/sh"},
+    {"/usr/bin/sh",   "/usr/bin/sh"},
+    {NULL, NULL}
+  };
+  for (i = 0; alts[i][0] && !command; i++)
+    {
+      lstat (alts[i][0], &stat_buf);
+      if (S_ISREG (stat_buf.st_mode) || S_ISLNK (stat_buf.st_mode) )
+        { command = alts[i][1]; }
+    }
+  return command;
 }
 
-static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum)
+static char *string_chop_head (char *orig) /* return pointer to reset after arg */
 {
-  int d = lum - ctx_u8_get_lum (components, c);
-  int tc[components];
-  for (int i = 0; i < components - 1; i++)
-  {
-    tc[i] = c[i] + d;
-  }
+  int j=0;
+  int eat=0; /* number of chars to eat at start */
 
-  int l = ctx_int_get_lum (components, tc);
-  int n = ctx_int_get_min (components, tc);
-  int x = ctx_int_get_max (components, tc);
+  if(orig)
+    {
+      int got_more;
+      char *o = orig;
+      while(o[j] == ' ')
+        {j++;eat++;}
 
-  if (n < 0 && l!=n)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * l) / (l-n));
-  }
+      if (o[j]=='"')
+        {
+          eat++;j++;
+          while(o[j] != '"' &&
+                o[j] != 0)
+            j++;
+          o[j]='\0';
+          j++;
+        }
+      else if (o[j]=='\'')
+        {
+          eat++;j++;
+          while(o[j] != '\'' &&
+                o[j] != 0)
+            j++;
+          o[j]='\0';
+          j++;
+        }
+      else
+        {
+          while(o[j] != ' ' &&
+                o[j] != 0 &&
+                o[j] != ';')
+            j++;
+        }
+      if (o[j] == 0 ||
+          o[j] == ';')
+        got_more = 0;
+      else
+        got_more = 1;
+      o[j]=0; /* XXX: this is where foo;bar won't work but foo ;bar works*/
 
-  if (x > 255 && x!=l)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l));
-  }
-  for (int i = 0; i < components - 1; i++)
-    c[i] = tc[i];
+      if(eat)
+       {
+         int k;
+         for (k=0; k<j-eat; k++)
+           orig[k] = orig[k+eat];
+       }
+      if (got_more)
+        return &orig[j+1];
+    }
+  return NULL;
 }
 
-static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat)
-{
-  int max = 0, mid = 1, min = 2;
-  
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-
-  if (c[max] > c[min])
-  {
-    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
-    c[max] = sat;
-  }
-  else
-  {
-    c[mid] = c[max] = 0;
-  }
-  c[min] = 0;
-}
+void _ctx_add_listen_fd (int fd);
+void _ctx_remove_listen_fd (int fd);
 
-ctx_u8_blend_define(color,
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s));
-)
+static pid_t
+vt_forkpty (int  *amaster,
+            char *aname,
+            const struct termios *termp,
+            const struct winsize *winsize)
+{
+  pid_t pid;
+  int master = posix_openpt (O_RDWR|O_NOCTTY);
+  int slave;
 
-ctx_u8_blend_define(hue,
-  int in_sat = ctx_u8_get_sat(components, b);
-  int in_lum = ctx_u8_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_u8_set_sat(components, blended, in_sat);
-  ctx_u8_set_lum(components, blended, in_lum);
-)
+  if (master < 0)
+    return -1;
+  if (grantpt (master) != 0)
+    return -1;
+  if (unlockpt (master) != 0)
+    return -1;
+#if 0
+  char name[1024];
+  if (ptsname_r (master, name, sizeof(name)-1))
+    return -1;
+#else
+  char *name = NULL;
+  if ((name = ptsname (master)) == NULL)
+    return -1;
+#endif
 
-ctx_u8_blend_define(saturation,
-  int in_sat = ctx_u8_get_sat(components, s);
-  int in_lum = ctx_u8_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_u8_set_sat(components, blended, in_sat);
-  ctx_u8_set_lum(components, blended, in_lum);
-)
+  slave = open(name, O_RDWR|O_NOCTTY);
 
-ctx_u8_blend_define(luminosity,
-  int in_lum = ctx_u8_get_lum(components, s);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_u8_set_lum(components, blended, in_lum);
-)
-#endif
+  if (termp)   tcsetattr(slave, TCSAFLUSH, termp);
+  if (winsize) ioctl(slave, TIOCSWINSZ, winsize);
 
-CTX_INLINE static void
-ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)
-{
-#if CTX_BLENDING_AND_COMPOSITING
-  switch (blend)
+  pid = fork();
+  if (pid < 0)
   {
-    case CTX_BLEND_NORMAL:      ctx_u8_blend_normal      (components, dst, src, blended); break;
-    case CTX_BLEND_MULTIPLY:    ctx_u8_blend_multiply    (components, dst, src, blended); break;
-    case CTX_BLEND_SCREEN:      ctx_u8_blend_screen      (components, dst, src, blended); break;
-    case CTX_BLEND_OVERLAY:     ctx_u8_blend_overlay     (components, dst, src, blended); break;
-    case CTX_BLEND_DARKEN:      ctx_u8_blend_darken      (components, dst, src, blended); break;
-    case CTX_BLEND_LIGHTEN:     ctx_u8_blend_lighten     (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_BURN:  ctx_u8_blend_color_burn  (components, dst, src, blended); break;
-    case CTX_BLEND_HARD_LIGHT:  ctx_u8_blend_hard_light  (components, dst, src, blended); break;
-    case CTX_BLEND_SOFT_LIGHT:  ctx_u8_blend_soft_light  (components, dst, src, blended); break;
-    case CTX_BLEND_DIFFERENCE:  ctx_u8_blend_difference  (components, dst, src, blended); break;
-    case CTX_BLEND_EXCLUSION:   ctx_u8_blend_exclusion   (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR:       ctx_u8_blend_color       (components, dst, src, blended); break;
-    case CTX_BLEND_HUE:         ctx_u8_blend_hue         (components, dst, src, blended); break;
-    case CTX_BLEND_SATURATION:  ctx_u8_blend_saturation  (components, dst, src, blended); break;
-    case CTX_BLEND_LUMINOSITY:  ctx_u8_blend_luminosity  (components, dst, src, blended); break;
-    case CTX_BLEND_ADDITION:    ctx_u8_blend_addition    (components, dst, src, blended); break;
-    case CTX_BLEND_DIVIDE:      ctx_u8_blend_divide      (components, dst, src, blended); break;
-    case CTX_BLEND_SUBTRACT:    ctx_u8_blend_subtract    (components, dst, src, blended); break;
-  }
-#else
-  switch (blend)
+    return pid;
+  } else if (pid == 0)
   {
-    default:                    ctx_u8_blend_normal      (components, dst, src, blended); break;
-  }
+    close (master);
+    setsid ();
+    dup2 (slave, STDIN_FILENO);
+    dup2 (slave, STDOUT_FILENO);
+    dup2 (slave, STDERR_FILENO);
 
-#endif
+    close (slave);
+    return 0;
+  }
+  ioctl (slave, TIOCSCTTY, NULL);
+  close (slave);
+  *amaster = master;
+  return pid;
 }
 
-CTX_INLINE static void
-__ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
-                     int                    components,
-                     uint8_t * __restrict__ dst,
-                     uint8_t * __restrict__ src,
-                     int                    x0,
-                     uint8_t * __restrict__ coverage,
-                     int                    count,
-                     CtxCompositingMode     compositing_mode,
-                     CtxFragment            fragment,
-                     CtxBlend               blend)
+static void vt_run_command (VT *vt, const char *command, const char *term)
 {
-  CtxPorterDuffFactor f_s, f_d;
-  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
-  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
-
-  {
-    uint8_t tsrc[components];
-    float u0 = 0; float v0 = 0;
-    float ud = 0; float vd = 0;
-    if (CTX_UNLIKELY(fragment))
-      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-
-    while (count--)
+  struct winsize ws;
+  //signal (SIGCHLD,signal_child);
+#if 0
+  int was_pidone = (getpid () == 1);
+#else
+  int was_pidone = 0; // do no special treatment, all child processes belong
+                      // to root
+#endif
+  signal (SIGINT,SIG_DFL);
+  ws.ws_row = vt->rows;
+  ws.ws_col = vt->cols;
+  ws.ws_xpixel = ws.ws_col * vt->cw;
+  ws.ws_ypixel = ws.ws_row * vt->ch;
+  vt->vtpty.pid = vt_forkpty (&vt->vtpty.pty, NULL, NULL, &ws);
+  if (vt->vtpty.pid == 0)
     {
-      int cov = *coverage;
-
-      if (CTX_UNLIKELY(
-        (compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 255)||
-        (compositing_mode == CTX_COMPOSITE_SOURCE_OVER      && cov == 0) ||
-        (compositing_mode == CTX_COMPOSITE_XOR              && cov == 0) ||
-        (compositing_mode == CTX_COMPOSITE_DESTINATION_OUT  && cov == 0) ||
-        (compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      && cov == 0)
-        ))
-      {
-        u0 += ud;
-        v0 += vd;
-        coverage ++;
-        dst+=components;
-        continue;
-      }
-
-      if (fragment)
+      int i;
+      if (was_pidone)
       {
-        fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
-        if (blend != CTX_BLEND_NORMAL)
-          ctx_u8_blend (components, blend, dst, tsrc, tsrc);
+        if (setuid(1000)) fprintf (stderr, "setuid failed\n");
       }
       else
       {
-        ctx_u8_blend (components, blend, dst, src, tsrc);
+        for (i = 3; i<768; i++) { close (i); } /*hack, trying to close xcb */
       }
+      unsetenv ("TERM");
+      unsetenv ("COLUMNS");
+      unsetenv ("LINES");
+      unsetenv ("TERMCAP");
+      unsetenv ("COLOR_TERM");
+      unsetenv ("COLORTERM");
+      unsetenv ("VTE_VERSION");
+      unsetenv ("CTX_BACKEND");
+      //setenv ("TERM", "ansi", 1);
+      //setenv ("TERM", "vt102", 1);
+      //setenv ("TERM", "vt100", 1);
+      // setenv ("TERM", term?term:"xterm", 1);
+      setenv ("TERM", term?term:"xterm-256color", 1);
+      setenv ("COLORTERM", "truecolor", 1);
+      //setenv ("CTX_VERSION", "0", 1);
+      setenv ("CTX_BACKEND", "ctx", 1); // speeds up launching of clients
 
-      u0 += ud;
-      v0 += vd;
-      if (global_alpha_u8 != 255)
-        cov = (cov * global_alpha_u8)/255;
-
-      if (cov != 255)
-        for (int c = 0; c < components; c++)
-          tsrc[c] = (tsrc[c] * cov)/255;
-
-      for (int c = 0; c < components; c++)
       {
-        int res = 0;
-        switch (f_s)
-        {
-          case CTX_PORTER_DUFF_0: break;
-          case CTX_PORTER_DUFF_1:             res += (tsrc[c]); break;
-          case CTX_PORTER_DUFF_ALPHA:         res += (tsrc[c] * dst[components-1])/255; break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (tsrc[c] * (255-dst[components-1]))/255; break;
-        }
-        switch (f_d)
+        char *cargv[32];
+        int   cargc;
+        char *rest, *copy;
+        copy = calloc (strlen (command)+2, 1);
+        strcpy (copy, command);
+        rest = copy;
+        cargc = 0;
+        while (rest && cargc < 30 && rest[0] != ';')
         {
-          case CTX_PORTER_DUFF_0: break;
-          case CTX_PORTER_DUFF_1:             res += dst[c]; break;
-          case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * tsrc[components-1])/255; break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-tsrc[components-1]))/255; break;
+          cargv[cargc++] = rest;
+          rest = string_chop_head (rest);
         }
-        dst[c] = res;
-      }
-      coverage ++;
-      dst+=components;
-    }
-  }
-}
-
-#if CTX_AVX2
-CTX_INLINE static void
-ctx_avx2_porter_duff (CtxRasterizer         *rasterizer,
-                      int                    components,
-                      uint8_t * dst,
-                      uint8_t * src,
-                      int                    x0,
-                      uint8_t * coverage,
-                      int                    count,
-                      CtxCompositingMode     compositing_mode,
-                      CtxFragment            fragment,
-                      CtxBlend               blend)
-{
-  CtxPorterDuffFactor f_s, f_d;
-  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
-  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
-//assert ((((size_t)dst) & 31) == 0);
-  int n_pix = 32/components;
-  uint8_t tsrc[components * n_pix];
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  int x = 0;
-  if (fragment)
-    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-
-  __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
-  __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
-  __m256i x00ff =   _mm256_set1_epi16(255);
-  __m256i x0101 =   _mm256_set1_epi16(0x0101);
-  __m256i x0080 =   _mm256_set1_epi16(0x0080);
-  for (; x < count; x+=n_pix)
-  {
-    __m256i xdst  = _mm256_loadu_si256((__m256i*)(dst)); 
-    __m256i xcov;
-    __m256i xsrc;
-    __m256i xsrc_a;
-    __m256i xdst_a;
-
-    int is_blank = 1;
-    int is_full = 0;
-    switch (n_pix)
-    {
-      case 16:
-        if (((uint64_t*)(coverage))[0] &&
-            ((uint64_t*)(coverage))[1])
-           is_blank = 0;
-        else if (((uint64_t*)(coverage))[0] == 0xffffffffffffffff &&
-                 ((uint64_t*)(coverage))[1] == 0xffffffffffffffff)
-           is_full = 1;
-        break;
-      case 8:
-        if (((uint64_t*)(coverage))[0])
-           is_blank = 0;
-        else if (((uint64_t*)(coverage))[0] == 0xffffffffffffffff)
-           is_full = 1;
-        break;
-      case 4:
-        if (((uint32_t*)(coverage))[0])
-           is_blank = 0;
-        else if (((uint32_t*)(coverage))[0] == 0xffffffff)
-           is_full = 1;
-        break;
-      default:
-        break;
-    }
-
-#if 1
-    if (
-      //(compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 255)||
-      (compositing_mode == CTX_COMPOSITE_SOURCE_OVER      && is_blank) ||
-      (compositing_mode == CTX_COMPOSITE_XOR              && is_blank) ||
-      (compositing_mode == CTX_COMPOSITE_DESTINATION_OUT  && is_blank) ||
-      (compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      && is_blank)
-      )
-    {
-      u0 += ud * n_pix;
-      v0 += vd * n_pix;
-      coverage += n_pix;
-      dst+=32;
-      continue;
-    }
-#endif
-
-    if (fragment)
-    {
-      for (int i = 0; i < n_pix; i++)
-      {
-         fragment (rasterizer, u0, v0, &tsrc[i*components], 1, ud, vd);
-         ctx_u8_associate_alpha (components, &tsrc[i*components]);
-         ctx_u8_blend (components, blend,
-                       &dst[i*components],
-                       &tsrc[i*components],
-                       &tsrc[i*components]);
-         u0 += ud;
-         v0 += vd;
-      }
-      xsrc = _mm256_loadu_si256((__m256i*)tsrc);
-    }
-    else
-    {
-#if 0
-      if (blend == CTX_BLEND_NORMAL && components == 4)
-        xsrc = _mm256_set1_epi32 (*((uint32_t*)src));
-    else
-#endif
-      {
- //     for (int i = 0; i < n_pix; i++)
- //       for (int c = 0; c < components; c++)
- //         tsrc[i*components+c]=src[c];
-#if 1
-        uint8_t lsrc[components];
-        for (int i = 0; i < components; i ++)
-          lsrc[i] = src[i];
-  //    ctx_u8_associate_alpha (components, lsrc);
-        for (int i = 0; i < n_pix; i++)
-          ctx_u8_blend (components, blend,
-                        &dst[i*components],
-                        lsrc,
-                        &tsrc[i*components]);
-#endif
-        xsrc = _mm256_loadu_si256((__m256i*)tsrc);
+        cargv[cargc] = NULL;
+        execvp (cargv[0], cargv);
       }
+      exit (0);
     }
-
-    if (is_full)
-       xcov = _mm256_set1_epi16(255);
-    else
-    switch (n_pix)
-    {
-      case 4: xcov  = _mm256_set_epi16(
-               (coverage[3]), (coverage[3]), coverage[3], coverage[3],
-               (coverage[2]), (coverage[2]), coverage[2], coverage[2],
-               (coverage[1]), (coverage[1]), coverage[1], coverage[1],
-               (coverage[0]), (coverage[0]), coverage[0], coverage[0]);
-              break;
-      case 8: 
-          xcov  = _mm256_set_epi32(coverage[7],
-                                   coverage[6],
-                                   coverage[5],
-                                   coverage[4],
-                                   coverage[3],
-                                   coverage[2],
-                                   coverage[1],
-                                   coverage[0]
-                                   );
-          xcov |= xcov << 16;
-              break;
-      case 16: xcov  = _mm256_set_epi16(
-               (coverage[15]),
-               (coverage[14]),
-               (coverage[13]),
-               (coverage[12]),
-               (coverage[11]),
-               (coverage[10]),
-               (coverage[9]),
-               (coverage[8]),
-               (coverage[7]),
-               (coverage[6]),
-               (coverage[5]),
-               (coverage[4]),
-               (coverage[3]),
-               (coverage[2]),
-               (coverage[1]),
-               (coverage[0]));
-              break;
-    }
-#if 0
-    switch (n_pix)
+  else if (vt->vtpty.pid < 0)
     {
-      case 4:
-      xsrc_a = _mm256_set_epi16(
-            tsrc[3*components+(components-1)], 
tsrc[3*components+(components-1)],tsrc[3*components+(components-1)], tsrc[3*components+(components-1)],
-            tsrc[2*components+(components-1)], 
tsrc[2*components+(components-1)],tsrc[2*components+(components-1)], tsrc[2*components+(components-1)],
-            tsrc[1*components+(components-1)], 
tsrc[1*components+(components-1)],tsrc[1*components+(components-1)], tsrc[1*components+(components-1)],
-            tsrc[0*components+(components-1)], 
tsrc[0*components+(components-1)],tsrc[0*components+(components-1)], tsrc[0*components+(components-1)]);
-      xdst_a = _mm256_set_epi16(
-            dst[3*components+(components-1)], 
dst[3*components+(components-1)],dst[3*components+(components-1)], dst[3*components+(components-1)],
-            dst[2*components+(components-1)], 
dst[2*components+(components-1)],dst[2*components+(components-1)], dst[2*components+(components-1)],
-            dst[1*components+(components-1)], 
dst[1*components+(components-1)],dst[1*components+(components-1)], dst[1*components+(components-1)],
-            dst[0*components+(components-1)], 
dst[0*components+(components-1)],dst[0*components+(components-1)], dst[0*components+(components-1)]);
-
-              break;
-      case 8:
-      xsrc_a = _mm256_set_epi16(
-            tsrc[7*components+(components-1)], tsrc[7*components+(components-1)],
-            tsrc[6*components+(components-1)], tsrc[6*components+(components-1)],
-            tsrc[5*components+(components-1)], tsrc[5*components+(components-1)],
-            tsrc[4*components+(components-1)], tsrc[4*components+(components-1)],
-            tsrc[3*components+(components-1)], tsrc[3*components+(components-1)],
-            tsrc[2*components+(components-1)], tsrc[2*components+(components-1)],
-            tsrc[1*components+(components-1)], tsrc[1*components+(components-1)],
-            tsrc[0*components+(components-1)], tsrc[0*components+(components-1)]);
-      xdst_a = _mm256_set_epi16(
-            dst[7*components+(components-1)], dst[7*components+(components-1)],
-            dst[6*components+(components-1)], dst[6*components+(components-1)],
-            dst[5*components+(components-1)], dst[5*components+(components-1)],
-            dst[4*components+(components-1)], dst[4*components+(components-1)],
-            dst[3*components+(components-1)], dst[3*components+(components-1)],
-            dst[2*components+(components-1)], dst[2*components+(components-1)],
-            dst[1*components+(components-1)], dst[1*components+(components-1)],
-            dst[0*components+(components-1)], dst[0*components+(components-1)]);
-              break;
-      case 16: 
-      xsrc_a = _mm256_set_epi16(
-            tsrc[15*components+(components-1)],
-            tsrc[14*components+(components-1)],
-            tsrc[13*components+(components-1)],
-            tsrc[12*components+(components-1)],
-            tsrc[11*components+(components-1)],
-            tsrc[10*components+(components-1)],
-            tsrc[9*components+(components-1)],
-            tsrc[8*components+(components-1)],
-            tsrc[7*components+(components-1)],
-            tsrc[6*components+(components-1)],
-            tsrc[5*components+(components-1)],
-            tsrc[4*components+(components-1)],
-            tsrc[3*components+(components-1)],
-            tsrc[2*components+(components-1)],
-            tsrc[1*components+(components-1)],
-            tsrc[0*components+(components-1)]);
-      xdst_a = _mm256_set_epi16(
-            dst[15*components+(components-1)],
-            dst[14*components+(components-1)],
-            dst[13*components+(components-1)],
-            dst[12*components+(components-1)],
-            dst[11*components+(components-1)],
-            dst[10*components+(components-1)],
-            dst[9*components+(components-1)],
-            dst[8*components+(components-1)],
-            dst[7*components+(components-1)],
-            dst[6*components+(components-1)],
-            dst[5*components+(components-1)],
-            dst[4*components+(components-1)],
-            dst[3*components+(components-1)],
-            dst[2*components+(components-1)],
-            dst[1*components+(components-1)],
-            dst[0*components+(components-1)]);
-              break;
+      VT_error ("forkpty failed (%s)", command);
+      return;
     }
-#endif
+  fcntl(vt->vtpty.pty, F_SETFL, O_NONBLOCK|O_NOCTTY);
+  _ctx_add_listen_fd (vt->vtpty.pty);
+}
 
-    if (global_alpha_u8 != 255)
+void vt_destroy (VT *vt)
+{
+  while (vt->lines)
     {
-      xcov = _mm256_mulhi_epu16(
-              _mm256_adds_epu16(
-                 _mm256_mullo_epi16(xcov,
-                                    _mm256_set1_epi16(global_alpha_u8)),
-                 x0080), x0101);
-      is_full = 0;
+      vt_line_free (vt->lines->data, 1);
+      ctx_list_remove (&vt->lines, vt->lines->data);
+      vt->line_count--;
     }
-
-
-    xsrc_a = _mm256_srli_epi32(xsrc, 24);  // XX 24 is RGB specific
-    if (!is_full)
-    xsrc_a = _mm256_mulhi_epu16(
-              _mm256_adds_epu16(
-                 _mm256_mullo_epi16(xsrc_a, xcov),
-                 x0080), x0101);
-    xsrc_a = xsrc_a | _mm256_slli_epi32(xsrc, 16);
-
-    xdst_a = _mm256_srli_epi32(xdst, 24);
-    xdst_a = xdst_a |  _mm256_slli_epi32(xdst, 16);
-
-
- //  case CTX_COMPOSITE_SOURCE_OVER:
- //     f_s = CTX_PORTER_DUFF_1;
- //     f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;
-
-
-    __m256i dst_lo;
-    __m256i dst_hi; 
-    __m256i src_lo; 
-    __m256i src_hi;
-
-    switch (f_s)
-    {
-      case CTX_PORTER_DUFF_0:
-        src_lo = _mm256_set1_epi32(0);
-        src_hi = _mm256_set1_epi32(0);
-        break;
-      case CTX_PORTER_DUFF_1:
-        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
-        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-
-        //if (!is_full)
-        {
-          src_lo = _mm256_mulhi_epu16(
-                   _mm256_adds_epu16(
-                   _mm256_mullo_epi16(src_lo, xcov),
-                   x0080), x0101);
-          src_hi = _mm256_mulhi_epu16(
-                   _mm256_adds_epu16(
-                   _mm256_mullo_epi16(src_hi, xcov),
-                   x0080), x0101);
-        }
-        break;
-      case CTX_PORTER_DUFF_ALPHA:
-        // res += (tsrc[c] *      dst[components-1])/255;
-        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
-        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-        if (!is_full)
-        {
-           src_lo = _mm256_mulhi_epu16(
-                 _mm256_adds_epu16(
-                 _mm256_mullo_epi16(src_lo, xcov),
-                 x0080), x0101);
-           src_hi = _mm256_mulhi_epu16(
-                    _mm256_adds_epu16(
-                    _mm256_mullo_epi16(src_hi, xcov),
-                    x0080), x0101);
-        }
-        src_lo = _mm256_mulhi_epu16 (
-                      _mm256_adds_epu16 (_mm256_mullo_epi16(src_lo,
-                                         xdst_a), x0080), x0101);
-        src_hi = _mm256_mulhi_epu16 (
-                      _mm256_adds_epu16 (_mm256_mullo_epi16(src_hi,
-                                         xdst_a), x0080), x0101);
-        break;
-      case CTX_PORTER_DUFF_1_MINUS_ALPHA:
-        // res += (tsrc[c] * (255-dst[components-1]))/255;
-        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
-        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
-  //    if (!is_full)
-        {
-          src_lo = _mm256_mulhi_epu16(
-                        _mm256_adds_epu16(
-                        _mm256_mullo_epi16(src_lo, xcov),
-                        x0080), x0101);
-          src_hi = _mm256_mulhi_epu16(
-                        _mm256_adds_epu16(
-                        _mm256_mullo_epi16(src_hi, xcov),
-                        x0080), x0101);
-        }
-        src_lo = _mm256_mulhi_epu16 (
-                  _mm256_adds_epu16 (_mm256_mullo_epi16(src_lo,
-                                     _mm256_sub_epi16(x00ff,xdst_a)), x0080),
-                  x0101);
-        src_hi = _mm256_mulhi_epu16 (
-                 _mm256_adds_epu16 (_mm256_mullo_epi16(src_hi,
-                                    _mm256_sub_epi16(x00ff,xdst_a)), x0080),
-                 x0101);
-        break;
-    }
-    switch (f_d)
-    {
-      case CTX_PORTER_DUFF_0: 
-        dst_lo = _mm256_set1_epi32(0);
-        dst_hi = _mm256_set1_epi32(0);
-        break;
-      case CTX_PORTER_DUFF_1:
-        dst_lo = _mm256_and_si256 (xdst, lo_mask); 
-        dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
-        break;
-      case CTX_PORTER_DUFF_ALPHA:        
-          //res += (dst[c] * tsrc[components-1])/255;
-          dst_lo = _mm256_and_si256 (xdst, lo_mask); 
-          dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
-
-          dst_lo =
-             _mm256_mulhi_epu16 (
-                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_lo,
-                                    xsrc_a), x0080), x0101);
-          dst_hi =
-             _mm256_mulhi_epu16 (
-                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_hi,
-                                    xsrc_a), x0080), x0101);
-          break;
-      case CTX_PORTER_DUFF_1_MINUS_ALPHA:
-          dst_lo = _mm256_and_si256 (xdst, lo_mask); 
-          dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
-          dst_lo = 
-             _mm256_mulhi_epu16 (
-                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_lo,
-                                    _mm256_sub_epi16(x00ff,xsrc_a)), x0080),
-                 x0101);
-          dst_hi = 
-             _mm256_mulhi_epu16 (
-                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_hi,
-                                    _mm256_sub_epi16(x00ff,xsrc_a)), x0080),
-                 x0101);
-          break;
+  while (vt->scrollback)
+    {
+      vt_line_free (vt->scrollback->data, 1);
+      ctx_list_remove (&vt->scrollback, vt->scrollback->data);
     }
-
-    dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
-    dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
-
-#if 0 // to toggle source vs dst
-      src_hi = _mm256_slli_epi16 (src_hi, 8);
-      _mm256_storeu_si256((__m256i*)dst, _mm256_blendv_epi8(src_lo, src_hi, hi_mask));
-#else
-      _mm256_storeu_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi, 8) | dst_lo);
+  if (vt->ctxp)
+    ctx_parser_free (vt->ctxp);
+  //if (vt->ctx)
+  //  { ctx_free (vt->ctx); }
+  free (vt->argument_buf);
+  ctx_list_remove (&vts, vt);
+  kill (vt->vtpty.pid, 9);
+  _ctx_remove_listen_fd (vt->vtpty.pty);
+  close (vt->vtpty.pty);
+#if 1
+  if (vt->title)
+    free (vt->title);
 #endif
+  free (vt);
+}
 
-    coverage += n_pix;
-    dst      += 32;
-  }
+int vt_get_line_count (VT *vt)
+{
+  return vt->line_count;
 }
-#endif
 
-CTX_INLINE static void
-_ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
-                     int                    components,
-                     uint8_t *              dst,
-                     uint8_t * __restrict__ src,
-                     int                    x0,
-                     uint8_t *              coverage,
-                     int                    count,
-                     CtxCompositingMode     compositing_mode,
-                     CtxFragment            fragment,
-                     CtxBlend               blend)
+const char *vt_get_line (VT *vt, int no)
 {
-#if NOT_USABLE_CTX_AVX2
-  int pre_count = 0;
-  if ((size_t)(dst)&31)
+  if (no >= vt->rows)
   {
-    pre_count = (32-(((size_t)(dst))&31))/components;
-  __ctx_u8_porter_duff (rasterizer, components,
-     dst, src, x0, coverage, pre_count, compositing_mode, fragment, blend);
-    dst += components * pre_count;
-    x0 += pre_count;
-    coverage += pre_count;
-    count -= pre_count;
+    CtxList *l = ctx_list_nth (vt->scrollback, no - vt->rows);
+    if (!l)
+      { 
+         return "";
+      }
+    CtxString *str = l->data;
+    return str->str;
   }
-  if (count < 0)
-     return;
-  int post_count = (count & 31);
-  if (src && 0)
+  else
   {
-    src[0]/=2;
-    src[1]/=2;
-    src[2]/=2;
-    src[3]/=2;
+    CtxList *l = ctx_list_nth (vt->lines, no);
+    if (!l)
+      { 
+         return "-";
+      }
+    CtxString *str = l->data;
+    return str->str;
   }
-#if 0
-  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count-post_count, compositing_mode, 
fragment, blend);
-#else
-  ctx_avx2_porter_duff (rasterizer, components, dst, src, x0, coverage, count-post_count, compositing_mode, 
fragment, blend);
-#endif
-  if (src && 0)
+}
+
+int vt_line_is_continuation (VT *vt, int no)
+{
+  if (no >= vt->rows)
   {
-    src[0]*=2;
-    src[1]*=2;
-    src[2]*=2;
-    src[3]*=2;
+    CtxList *l = ctx_list_nth (vt->scrollback, no - vt->rows);
+    if (!l)
+      { 
+         return 1;
+      }
+    VtLine *line = l->data;
+    return line->wrapped;
   }
-  if (post_count > 0)
+  else
   {
-       x0 += (count - post_count);
-       dst += components * (count-post_count);
-       coverage += (count - post_count);
-       __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, post_count, compositing_mode, 
fragment, blend);
+    CtxList *l = ctx_list_nth (vt->lines, no);
+    if (!l)
+      { 
+         return 1;
+      }
+    VtLine *line = l->data;
+    return line->wrapped;
   }
-#else
-  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, 
blend);
-#endif
 }
 
-#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \
-   switch (rasterizer->state->gstate.compositing_mode) \
-   { \
-     case CTX_COMPOSITE_SOURCE_ATOP: \
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
-        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_XOR, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_COPY, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_CLEAR:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_CLEAR, fragment, blend);\
-       break;\
-   }
+int vt_get_cols (VT *vt)
+{
+  return vt->cols;
+}
 
-/* generating one function per compositing_mode would be slightly more efficient,
- * but on embedded targets leads to slightly more code bloat,
- * here we trade off a slight amount of performance
- */
-#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \
-static void \
-CTX_COMPOSITE_SUFFIX(ctx_##comp_format##_porter_duff_##source) (CTX_COMPOSITE_ARGUMENTS) \
-{ \
-  _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\
+int vt_get_rows (VT *vt)
+{
+  return vt->rows;
 }
 
-ctx_u8_porter_duff(RGBA8, 4,color,   NULL,                 rasterizer->state->gstate.blend_mode)
-ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+int vt_get_cursor_x (VT *vt)
+{
+  return vt->cursor_x;
+}
 
-//ctx_u8_porter_duff(comp_name, components,color_##blend_name,  NULL, blend_mode)
+int vt_get_cursor_y (VT *vt)
+{
+  return vt->cursor_y;
+}
+
+static void draw_braille_bit (Ctx *ctx, float x, float y, float cw, float ch, int u, int v)
+{
+  ctx_rectangle (ctx, 0.167 * cw + x + u * cw * 0.5,
+                 y - ch + 0.080 * ch + v * ch * 0.25,
+                 0.33 *cw, 0.33 * cw);
+}
+
+static void draw_sextant_bit (Ctx *ctx, float x, float y, float cw, float ch, int u, int v)
+{
+  ctx_rectangle (ctx,  x + u * cw * 0.5,
+                       y - ch + v * ch * 0.3333,
+                       0.5 *cw, 0.34 * ch);
+}
+
+int vt_special_glyph (Ctx *ctx, VT *vt, float x, float y, int cw, int ch, int unichar)
+{
+  switch (unichar)
+    {
+      case 0x2594: // UPPER_ONE_EIGHT_BLOCK
+        ctx_begin_path (ctx);
+        {
+          float factor = 1.0f/8.0f;
+          ctx_rectangle (ctx, x, y - ch, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2581: // LOWER_ONE_EIGHT_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 1.0f/8.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2582: // LOWER_ONE_QUARTER_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 1.0f/4.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2583: // LOWER_THREE_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 3.0f/8.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2585: // LOWER_FIVE_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 5.0f/8.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2586: // LOWER_THREE_QUARTERS_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 3.0f/4.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2587: // LOWER_SEVEN_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        {
+          float factor = 7.0f/8.0f;
+          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
+          ctx_fill (ctx);
+        }
+        return 0;
+      case 0x2589: // LEFT_SEVEN_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw*7/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258A: // LEFT_THREE_QUARTERS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw*3/4, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258B: // LEFT_FIVE_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw*5/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258D: // LEFT_THREE_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw*3/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258E: // LEFT_ONE_QUARTER_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw/4, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258F: // LEFT_ONE_EIGHT_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x258C: // HALF_LEFT_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw/2, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2590: // HALF_RIGHT_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2, y - ch, cw/2, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb8f: // VT_RIGHT_SEVEN_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw*1/8, y - ch, cw*7/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb8d: // VT_RIGHT_FIVE_EIGHTS_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw*3/8, y - ch, cw*5/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb8b: // VT_RIGHT_ONE_QUARTER_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw*3/4, y - ch, cw/4, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb8e: // VT_RIGHT_THREE_QUARTER_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw*1/4, y - ch, cw*3/4, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2595: // VT_RIGHT_ONE_EIGHT_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw*7/8, y - ch, cw/8, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2580: // HALF_UP_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2584: // _HALF_DOWN_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch/2, cw, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2596: // _QUADRANT LOWER LEFT
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch/2, cw/2, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2597: // _QUADRANT LOWER RIGHT
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x+cw/2, y - ch/2, cw/2, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2598: // _QUADRANT UPPER LEFT
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw/2, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x259D: // _QUADRANT UPPER RIGHT
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2, y - ch, cw/2, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2599: // _QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
+        return 0;
+      case 0x259A: // _QUADRANT UPPER LEFT AND LOWER RIGHT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
+        return 0;
+      case 0x259B: // _QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
+        return 0;
+      case 0x259C: // _QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
+        return 0;
+      case 0x259E: // _QUADRANT UPPER RIGHT AND LOWER LEFT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
+        return 0;
+      case 0x259F: // _QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
+        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
+        return 0;
+      case 0x2588: // FULL_BLOCK:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2591: // LIGHT_SHADE:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw, ch);
+        ctx_save (ctx);
+        ctx_global_alpha (ctx, 0.25);
+        ctx_fill (ctx);
+        ctx_restore (ctx);
+        return 0;
+      case 0x2592: // MEDIUM_SHADE:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw, ch);
+        ctx_save (ctx);
+        ctx_global_alpha (ctx, 0.5);
+        ctx_fill (ctx);
+        ctx_restore (ctx);
+        return 0;
+      case 0x2593: // DARK SHADE:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch, cw, ch);
+        ctx_save (ctx);
+        ctx_global_alpha (ctx, 0.75);
+        ctx_fill (ctx);
+        ctx_restore (ctx);
+        return 0;
+      case 0x23BA: //HORIZONTAL_SCANLINE-1
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x,      y - ch + ch*0.1 - ch * 0.1,
+                       cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x23BB: //HORIZONTAL_SCANLINE-3
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x,      y - ch + ch*0.3 - ch * 0.075,
+                       cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x23BC: //HORIZONTAL_SCANLINE-7
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x,      y - ch + ch*0.7 - ch * 0.025,
+                       cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x23BD: //HORIZONTAL_SCANLINE-9
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x,      y - ch + ch*0.9 + ch * 0.0,
+                       cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2500: //VT_BOX_DRAWINGS_LIGHT_HORIZONTAL // and scanline 5
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2212: // minus -sign
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw * 0.1, y - ch/2 - ch * 0.1 / 2, cw * 0.8, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2502: // VT_BOX_DRAWINGS_LIGHT_VERTICAL:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch + 1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x250c: //VT_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, ch * 0.1, ch/2 + ch*0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, cw/2+ ch * 0.1, ch*0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2510: //VT_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, ch * 0.1, ch/2 + ch*0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x, y - ch/2 - ch*0.1/2, cw/2+ ch * 0.1/2, ch*0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2514: //VT_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch/2+ch*0.1/2);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, cw/2 + ch * 0.1, ch*0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2518: //VT_BOX_DRAWINGS_LIGHT_UP_AND_LEFT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch/2+ ch*0.1/2);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x, y - ch/2-ch*0.1/2, cw/2+ch * 0.1/2, ch*0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x251C: //VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2-ch*0.1/2, cw/2+ch * 0.1, ch*0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2524: //VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x, y - ch/2-ch*0.1/2, cw/2+ch * 0.1/2, ch*0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x252C: // VT_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2-ch*0.1/2, ch * 0.1, ch/2+ch*0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2534: // VT_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch /2+ch*0.1/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x253C: // VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL:
+        ctx_begin_path (ctx);
+        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
+        ctx_fill (ctx);
+        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0xe0a0: // PowerLine branch
+        ctx_save (ctx);
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x+cw/2, y - 0.15 * ch);
+        ctx_rel_line_to (ctx, -cw/3, -ch * 0.7);
+        ctx_rel_line_to (ctx, cw/2, 0);
+        ctx_rel_line_to (ctx, -cw/3, ch * 0.7);
+        ctx_line_width (ctx, cw * 0.25);
+        ctx_stroke (ctx);
+        ctx_restore (ctx);
+        break;
+      // case 0xe0a1: // PowerLine LN
+      // case 0xe0a2: // PowerLine Lock
+      case 0xe0b0: // PowerLine left solid
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, 0, -ch);
+        ctx_rel_line_to (ctx, cw, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0xe0b1: // PowerLine left line
+        ctx_save (ctx);
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y - ch * 0.1);
+        ctx_rel_line_to (ctx, cw * 0.9, -ch/2 * 0.8);
+        ctx_rel_line_to (ctx, -cw * 0.9, -ch/2 * 0.8);
+        ctx_line_width (ctx, cw * 0.2);
+        ctx_stroke (ctx);
+        ctx_restore (ctx);
+        return 0;
+      case 0xe0b2: // PowerLine Right solid
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_move_to (ctx, cw, 0);
+        ctx_rel_line_to (ctx, -cw, -ch/2);
+        ctx_rel_line_to (ctx, cw, -ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0xe0b3: // PowerLine right line
+        ctx_save (ctx);
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y - ch * 0.1);
+        ctx_rel_move_to (ctx, cw, 0);
+        ctx_rel_line_to (ctx, -cw * 0.9, -ch/2 * 0.8);
+        ctx_rel_line_to (ctx,  cw * 0.9, ch/2 * 0.8);
+        ctx_line_width (ctx, cw * 0.2);
+        ctx_stroke (ctx);
+        ctx_restore (ctx);
+        return 0;
+        /*
+      case 0x1fb70: // left triangular one quarter block
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, 0, -ch);
+        ctx_rel_line_to (ctx, cw/2, -ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb72: // right triangular one quarter block
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_move_to (ctx, cw/2, -ch/2);
+        ctx_rel_line_to (ctx, cw/2, -ch/2);
+        ctx_rel_line_to (ctx, 0, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb73: // lower triangular one quarter block
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, cw/2, -ch/2);
+        ctx_rel_line_to (ctx, cw/2, ch/2);
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb71: // upper triangular one quarter block
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_move_to (ctx, cw/2, -ch/2);
+        ctx_rel_line_to (ctx, -cw/2, -ch/2);
+        ctx_rel_line_to (ctx, cw, 0);
+        ctx_fill (ctx);
+        return 0;
+        */
+      case 0x25E2: // VT_BLACK_LOWER_RIGHT_TRIANGLE:
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, cw, -ch);
+        ctx_rel_line_to (ctx, 0, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x25E3: //  VT_BLACK_LOWER_LEFT_TRIANGLE:
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, 0, -ch);
+        ctx_rel_line_to (ctx, cw, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x25E4: // tri
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y);
+        ctx_rel_line_to (ctx, 0, -ch);
+        ctx_rel_line_to (ctx, cw, 0);
+        ctx_fill (ctx);
+        return 0;
+      case 0x25E5: // tri
+        ctx_begin_path (ctx);
+        ctx_move_to (ctx, x, y - ch);
+        ctx_rel_line_to (ctx, cw, 0);
+        ctx_rel_line_to (ctx, 0, ch);
+        ctx_fill (ctx);
+        return 0;
+      case 0x2800:
+      case 0x2801:
+      case 0x2802:
+      case 0x2803:
+      case 0x2804:
+      case 0x2805:
+      case 0x2806:
+      case 0x2807:
+      case 0x2808:
+      case 0x2809:
+      case 0x280A:
+      case 0x280B:
+      case 0x280C:
+      case 0x280D:
+      case 0x280E:
+      case 0x280F:
+      case 0x2810:
+      case 0x2811:
+      case 0x2812:
+      case 0x2813:
+      case 0x2814:
+      case 0x2815:
+      case 0x2816:
+      case 0x2817:
+      case 0x2818:
+      case 0x2819:
+      case 0x281A:
+      case 0x281B:
+      case 0x281C:
+      case 0x281D:
+      case 0x281E:
+      case 0x281F:
+      case 0x2820:
+      case 0x2821:
+      case 0x2822:
+      case 0x2823:
+      case 0x2824:
+      case 0x2825:
+      case 0x2826:
+      case 0x2827:
+      case 0x2828:
+      case 0x2829:
+      case 0x282A:
+      case 0x282B:
+      case 0x282C:
+      case 0x282D:
+      case 0x282E:
+      case 0x282F:
+      case 0x2830:
+      case 0x2831:
+      case 0x2832:
+      case 0x2833:
+      case 0x2834:
+      case 0x2835:
+      case 0x2836:
+      case 0x2837:
+      case 0x2838:
+      case 0x2839:
+      case 0x283A:
+      case 0x283B:
+      case 0x283C:
+      case 0x283D:
+      case 0x283E:
+      case 0x283F:
+        ctx_begin_path (ctx);
+        {
+          int bit_pattern = unichar - 0x2800;
+          int bit = 0;
+          int u = 0;
+          int v = 0;
+          for (bit = 0; bit < 6; bit++)
+            {
+              if (bit_pattern & (1<<bit) )
+                {
+                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
+                }
+              v++;
+              if (v > 2)
+                {
+                  v = 0;
+                  u++;
+                }
+            }
+        }
+        ctx_fill (ctx);
+        return 0;
+      case 0x2840:
+      case 0x2841:
+      case 0x2842:
+      case 0x2843:
+      case 0x2844:
+      case 0x2845:
+      case 0x2846:
+      case 0x2847:
+      case 0x2848:
+      case 0x2849:
+      case 0x284A:
+      case 0x284B:
+      case 0x284C:
+      case 0x284D:
+      case 0x284E:
+      case 0x284F:
+      case 0x2850:
+      case 0x2851:
+      case 0x2852:
+      case 0x2853:
+      case 0x2854:
+      case 0x2855:
+      case 0x2856:
+      case 0x2857:
+      case 0x2858:
+      case 0x2859:
+      case 0x285A:
+      case 0x285B:
+      case 0x285C:
+      case 0x285D:
+      case 0x285E:
+      case 0x285F:
+      case 0x2860:
+      case 0x2861:
+      case 0x2862:
+      case 0x2863:
+      case 0x2864:
+      case 0x2865:
+      case 0x2866:
+      case 0x2867:
+      case 0x2868:
+      case 0x2869:
+      case 0x286A:
+      case 0x286B:
+      case 0x286C:
+      case 0x286D:
+      case 0x286E:
+      case 0x286F:
+      case 0x2870:
+      case 0x2871:
+      case 0x2872:
+      case 0x2873:
+      case 0x2874:
+      case 0x2875:
+      case 0x2876:
+      case 0x2877:
+      case 0x2878:
+      case 0x2879:
+      case 0x287A:
+      case 0x287B:
+      case 0x287C:
+      case 0x287D:
+      case 0x287E:
+      case 0x287F:
+        ctx_begin_path (ctx);
+        draw_braille_bit (ctx, x, y, cw, ch, 0, 3);
+        {
+          int bit_pattern = unichar - 0x2840;
+          int bit = 0;
+          int u = 0;
+          int v = 0;
+          for (bit = 0; bit < 6; bit++)
+            {
+              if (bit_pattern & (1<<bit) )
+                {
+                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
+                }
+              v++;
+              if (v > 2)
+                {
+                  v = 0;
+                  u++;
+                }
+            }
+        }
+        ctx_fill (ctx);
+        return 0;
+      case 0x2880:
+      case 0x2881:
+      case 0x2882:
+      case 0x2883:
+      case 0x2884:
+      case 0x2885:
+      case 0x2886:
+      case 0x2887:
+      case 0x2888:
+      case 0x2889:
+      case 0x288A:
+      case 0x288B:
+      case 0x288C:
+      case 0x288D:
+      case 0x288E:
+      case 0x288F:
+      case 0x2890:
+      case 0x2891:
+      case 0x2892:
+      case 0x2893:
+      case 0x2894:
+      case 0x2895:
+      case 0x2896:
+      case 0x2897:
+      case 0x2898:
+      case 0x2899:
+      case 0x289A:
+      case 0x289B:
+      case 0x289C:
+      case 0x289D:
+      case 0x289E:
+      case 0x289F:
+      case 0x28A0:
+      case 0x28A1:
+      case 0x28A2:
+      case 0x28A3:
+      case 0x28A4:
+      case 0x28A5:
+      case 0x28A6:
+      case 0x28A7:
+      case 0x28A8:
+      case 0x28A9:
+      case 0x28AA:
+      case 0x28AB:
+      case 0x28AC:
+      case 0x28AD:
+      case 0x28AE:
+      case 0x28AF:
+      case 0x28B0:
+      case 0x28B1:
+      case 0x28B2:
+      case 0x28B3:
+      case 0x28B4:
+      case 0x28B5:
+      case 0x28B6:
+      case 0x28B7:
+      case 0x28B8:
+      case 0x28B9:
+      case 0x28BA:
+      case 0x28BB:
+      case 0x28BC:
+      case 0x28BD:
+      case 0x28BE:
+      case 0x28BF:
+        ctx_begin_path (ctx);
+        draw_braille_bit (ctx, x, y, cw, ch, 1, 3);
+        {
+          int bit_pattern = unichar - 0x2880;
+          int bit = 0;
+          int u = 0;
+          int v = 0;
+          for (bit = 0; bit < 6; bit++)
+            {
+              if (bit_pattern & (1<<bit) )
+                {
+                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
+                }
+              v++;
+              if (v > 2)
+                {
+                  v = 0;
+                  u++;
+                }
+            }
+        }
+        ctx_fill (ctx);
+        return 0;
+      case 0x28C0:
+      case 0x28C1:
+      case 0x28C2:
+      case 0x28C3:
+      case 0x28C4:
+      case 0x28C5:
+      case 0x28C6:
+      case 0x28C7:
+      case 0x28C8:
+      case 0x28C9:
+      case 0x28CA:
+      case 0x28CB:
+      case 0x28CC:
+      case 0x28CD:
+      case 0x28CE:
+      case 0x28CF:
+      case 0x28D0:
+      case 0x28D1:
+      case 0x28D2:
+      case 0x28D3:
+      case 0x28D4:
+      case 0x28D5:
+      case 0x28D6:
+      case 0x28D7:
+      case 0x28D8:
+      case 0x28D9:
+      case 0x28DA:
+      case 0x28DB:
+      case 0x28DC:
+      case 0x28DD:
+      case 0x28DE:
+      case 0x28DF:
+      case 0x28E0:
+      case 0x28E1:
+      case 0x28E2:
+      case 0x28E3:
+      case 0x28E4:
+      case 0x28E5:
+      case 0x28E6:
+      case 0x28E7:
+      case 0x28E8:
+      case 0x28E9:
+      case 0x28EA:
+      case 0x28EB:
+      case 0x28EC:
+      case 0x28ED:
+      case 0x28EE:
+      case 0x28EF:
+      case 0x28F0:
+      case 0x28F1:
+      case 0x28F2:
+      case 0x28F3:
+      case 0x28F4:
+      case 0x28F5:
+      case 0x28F6:
+      case 0x28F7:
+      case 0x28F8:
+      case 0x28F9:
+      case 0x28FA:
+      case 0x28FB:
+      case 0x28FC:
+      case 0x28FD:
+      case 0x28FE:
+      case 0x28FF:
+        ctx_begin_path (ctx);
+        draw_braille_bit (ctx, x, y, cw, ch, 0, 3);
+        draw_braille_bit (ctx, x, y, cw, ch, 1, 3);
+        {
+          int bit_pattern = unichar - 0x28C0;
+          int bit = 0;
+          int u = 0;
+          int v = 0;
+          for (bit = 0; bit < 6; bit++)
+            {
+              if (bit_pattern & (1<<bit) )
+                {
+                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
+                }
+              v++;
+              if (v > 2)
+                {
+                  v = 0;
+                  u++;
+                }
+            }
+        }
+        ctx_fill (ctx);
+        return 0;
+      case 0x1fb00:
+      case 0x1fb01:
+      case 0x1fb02:
+      case 0x1fb03:
+      case 0x1fb04:
+      case 0x1fb05:
+      case 0x1fb06:
+      case 0x1fb07:
+      case 0x1fb08:
+      case 0x1fb09:
+      case 0x1fb0a:
+      case 0x1fb0b:
+      case 0x1fb0c:
+      case 0x1fb0d:
+      case 0x1fb0e:
+      case 0x1fb0f:
+      case 0x1fb10:
+      case 0x1fb11:
+      case 0x1fb12:
+      case 0x1fb13:
+      case 0x1fb14:
+      case 0x1fb15:
+      case 0x1fb16:
+      case 0x1fb17:
+      case 0x1fb18:
+      case 0x1fb19:
+      case 0x1fb1a:
+      case 0x1fb1b:
+      case 0x1fb1c:
+      case 0x1fb1d:
+      case 0x1fb1e:
+      case 0x1fb1f:
+      case 0x1fb20:
+      case 0x1fb21:
+      case 0x1fb22:
+      case 0x1fb23:
+      case 0x1fb24:
+      case 0x1fb25:
+      case 0x1fb26:
+      case 0x1fb27:
+      case 0x1fb28:
+      case 0x1fb29:
+      case 0x1fb2a:
+      case 0x1fb2b:
+      case 0x1fb2c:
+      case 0x1fb2d:
+      case 0x1fb2e:
+      case 0x1fb2f:
+      case 0x1fb30:
+      case 0x1fb31:
+      case 0x1fb32:
+      case 0x1fb33:
+      case 0x1fb34:
+      case 0x1fb35:
+      case 0x1fb36:
+      case 0x1fb37:
+      case 0x1fb38:
+      case 0x1fb39:
+      case 0x1fb3a:
+      case 0x1fb3b:
+
+        {
+          ctx_begin_path (ctx);
+          uint32_t bitmask = (unichar - 0x1fb00) + 1;
+          if (bitmask > 20) bitmask ++;
+          if (bitmask > 41) bitmask ++;
+          int bit = 0;
+          for (int v = 0; v < 3; v ++)
+          for (int u = 0; u < 2; u ++, bit ++)
+          {
+            if (bitmask & (1<<bit))
+            {
+              draw_sextant_bit (ctx, x, y, cw, ch, u, v);
+            }
+          }
+          ctx_fill (ctx);
+          return 0;
+        }
+        break;
+      case 0x1fb3c:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch / 3.0f);
+          ctx_rel_line_to (ctx, cw/2, ch/3.0f);
+          ctx_fill (ctx);
+          return 0;
+        }
+        break;
+      case 0x1fb3d:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0f);
+          ctx_rel_line_to (ctx, cw, ch/3.0f);
+          ctx_fill (ctx);
+          return 0;
+        }
+        break;
+      case 0x1fb3e:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0,   -ch/3.0f * 2);
+          ctx_rel_line_to (ctx, cw/2, ch/3.0f * 2);
+          ctx_fill (ctx);
+          return 0;
+        }
+        break;
+      case 0x1fb3f:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0,  -ch/3.0f * 2);
+          ctx_rel_line_to (ctx, cw, ch/3.0f * 2);
+          ctx_fill (ctx);
+          return 0;
+        }
+        break;
+      case 0x1fb40:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, ch);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb41:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw/2, -ch/3.0);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0.0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb42:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, -ch/3.0);
+          ctx_rel_line_to (ctx, 0.0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb43:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0f);
+          ctx_rel_line_to (ctx, cw/2, -ch/3.0f*2.0f);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0.0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb44:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, cw, -ch/3.0 * 2);
+          ctx_rel_line_to (ctx, 0.0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb45:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw/2, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0.0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb46:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, cw, -ch/3.0);
+          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb47:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, cw/2, -ch/3.0);
+          ctx_rel_line_to (ctx, 0.0, ch/3.0);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb48:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw, -ch/3.0);
+          ctx_rel_line_to (ctx, 0.0, ch/3.0);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb49:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, cw/2, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4a:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4b:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, cw/2, ch/3);
+          ctx_rel_line_to (ctx, 0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4c:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, ch/3);
+          ctx_rel_line_to (ctx, 0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4d:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, ch/3);
+          ctx_rel_line_to (ctx, 0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4e:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, cw/2, ch/3 *  2);
+          ctx_rel_line_to (ctx, 0, ch/3.0);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb4f:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, ch/3 *  2);
+          ctx_rel_line_to (ctx, 0, ch/3.0);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb50:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, cw/2, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb51:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, ch/3.0);
+          ctx_rel_line_to (ctx, 0, ch/3.0);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb52:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb53:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw, -ch/3.0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb54:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb55:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw, -ch/3.0*2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb56:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_rel_line_to (ctx, -cw/2, -ch);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb57:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0, -ch/3);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb58:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0, -ch/3);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw, ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb59:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch/3 * 2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5a:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0);
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw, ch/3 * 2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5b:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5c:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3);
+
+          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3);
+          ctx_rel_line_to (ctx, -cw, ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5d:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3 * 2);
+          ctx_rel_line_to (ctx, -cw/2, ch/3);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5e:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3 * 2);
+          ctx_rel_line_to (ctx, -cw, ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb5f:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3);
+          ctx_rel_line_to (ctx, -cw/2, ch/3*2);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb60:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3);
+          ctx_rel_line_to (ctx, -cw, ch/3*2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb61:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch);
+          ctx_rel_line_to (ctx, -cw/2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb62:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/2, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0, ch/3);
+          ctx_rel_line_to (ctx, -cw/2, -ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb63:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3);
+          ctx_rel_line_to (ctx, -cw, -ch/3);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb64:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/2, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0, ch/3*2);
+          ctx_rel_line_to (ctx, -cw/2, -ch/3*2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb65:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3*2);
+          ctx_rel_line_to (ctx, -cw, -ch/3*2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb66:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/2, -ch);
+          ctx_rel_line_to (ctx, cw/2, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, -ch);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb67:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
+          ctx_rel_line_to (ctx, 0, -ch/3);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/3.0*2);
+          ctx_rel_line_to (ctx, -cw, -ch/3.0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb68:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb69:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6a:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6b:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6c:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6d:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6e:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw, -ch);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb6f:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb82:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/8 * 2);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_move_to (ctx, 0, ch/8 * 2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb83:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/8 * 3);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_move_to (ctx, 0, ch/8 * 3);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb84:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/8 * 5);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_move_to (ctx, 0, ch/8 * 5);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb85:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/8 * 6);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_move_to (ctx, 0, ch/8 * 6);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb86:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, 0, -ch/8 * 7);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_rel_move_to (ctx, 0, ch/8 * 7);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb87:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/8*6, 0);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_move_to (ctx, cw/8*2, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_move_to (ctx, -cw/8*2, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb88:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/8*5, 0);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_move_to (ctx, cw/8*3, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_move_to (ctx, -cw/8*3, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb89:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/8*3, 0);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_move_to (ctx, cw/8*5, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_move_to (ctx, -cw/8*5, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb8a:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_move_to (ctx, cw/8*2, 0);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_move_to (ctx, cw/8*6, 0);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_move_to (ctx, -cw/8*6, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb97:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch/4);
+          ctx_rel_move_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/4);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_close_path (ctx);
+          ctx_move_to (ctx, 0, -ch/2);
+          ctx_rel_line_to (ctx, 0, -ch/4);
+          ctx_rel_move_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, 0, ch/4);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb9a:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_rel_move_to (ctx, cw, 0);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, -cw, 0);
+          ctx_fill (ctx);
+          return 0;
+        }
+      case 0x1fb9b:
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x, y);
+          ctx_rel_line_to (ctx, 0, -ch);
+          ctx_rel_line_to (ctx, cw/2, ch/2);
+          ctx_rel_line_to (ctx, cw/2, -ch/2);
+          ctx_rel_line_to (ctx, 0, ch);
+          ctx_rel_line_to (ctx, -cw/2, -ch/2);
+          ctx_rel_line_to (ctx, -cw/2, ch/2);
+          ctx_fill (ctx);
+          return 0;
+        }
 
-static void
-CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop) (CTX_COMPOSITE_ARGUMENTS)
-{
+    }
+  return -1;
 }
 
-
-static void
-ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
+void vt_ctx_glyph (Ctx *ctx, VT *vt, float x, float y, int unichar, int bold, float scale_x, float scale_y, 
float offset_y)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  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)
-  {
-    rasterizer->comp_op = ctx_RGBA8_clear_normal;
+  int did_save = 0;
+  if (unichar <= ' ')
     return;
-  }
-#endif
+  scale_x *= vt->scale_x;
+  scale_y *= vt->scale_y;
 
-
-#if CTX_INLINED_GRADIENTS
-#if CTX_GRADIENTS
-  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_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_linear_gradient);
-     return;
-  }
-  if (gstate->source_fill.type == CTX_SOURCE_RADIAL_GRADIENT &&
-      gstate->blend_mode == CTX_BLEND_NORMAL &&
-      gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+  if (!ctx_renderer_is_term (ctx))
   {
-     rasterizer->comp_op = CTX_COMPOSITE_SUFFIX (ctx_RGBA8_source_over_normal_radial_gradient);
-     return;
+    // TODO : use our own special glyphs when glyphs are not passed through
+    if (!vt_special_glyph (ctx, vt, x, y + offset_y * vt->ch, vt->cw * scale_x, vt->ch * scale_y, unichar) )
+      return;
   }
-#endif
-#endif
 
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  if (scale_x != 1.0 || scale_y != 1.0)
     {
-      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)
+      if (!did_save)
       {
-        uint8_t *rgba = (uint8_t*)&rasterizer->color[0];
-        uint8_t tmp = rgba[0];
-        rgba[0] = rgba[2];
-        rgba[2] = tmp;
+        ctx_save (ctx);
+        did_save = 1;
       }
 
-      switch (gstate->blend_mode)
-      {
-        case CTX_BLEND_NORMAL:
-          if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-          {
-            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_copy_normal);
-            return;
-          }
-          else if (gstate->global_alpha_u8 == 0)
-          {
-            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-          }
-          else if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-          {
-             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_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color_solid);
-             else
-                 rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color);
-         }
-         break;
-      default:
-         rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_color);
-         break;
+      ctx_translate (ctx, x, y);
+      ctx_scale (ctx, scale_x, scale_y);
+      ctx_translate (ctx, -x, -y);
     }
-    //rasterizer->comp_op = ctx_RGBA8_porter_duff_color; // XXX overide to make all go
-                                                       // through generic code path
-    rasterizer->fragment = NULL;
-    return;
-  }
-
-
-#if 1
-  if (gstate->blend_mode == CTX_BLEND_NORMAL &&
-      gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER &&
-      rasterizer->fragment)
+  if (offset_y != 0.0f)
   {
-          // only really valid for image sources
-     rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_fragment);
-     return;
+    if (!did_save)
+    {
+      ctx_save (ctx);
+      did_save = 1;
+    }
+    ctx_translate (ctx, 0, vt->font_size * offset_y);
   }
-#endif
+  y -= vt->font_size * 0.22;
+  if (bold
+      && !ctx_renderer_is_term (ctx)
+     )
+    {
+      ctx_move_to (ctx, x - vt->font_size/30.0, y);
+      //ctx_line_width (ctx, vt->font_size/30.0);
+      ctx_glyph (ctx, unichar, 0);
+    }
+  ctx_move_to (ctx, x, y);
+  ctx_glyph (ctx, unichar, 0);
+  if (did_save)
+    ctx_restore (ctx);
 }
 
-/*
- * we could use this instead of NULL in the pixfmt table - but such dispatch
- * is slightly slower
+//static uint8_t palette[256][3];
+
+/* optimized for ANSI ART - and avoidance of human metamers
+ * among color deficient vision - by distributing and pertubating
+ * until all 64 combinations - sans self application, have
+ * likely to be discernable by humans.
  */
-inline static void
-ctx_composite_direct (CTX_COMPOSITE_ARGUMENTS)
+
+
+void vt_ctx_get_color (VT *vt, int no, int intensity, uint8_t *rgba)
 {
-  rasterizer->comp_op (rasterizer, dst, rasterizer->color, x0, coverage, count);
+  uint8_t r = 0, g = 0, b = 0;
+  if (no < 16 && no >= 0)
+    {
+      switch (intensity)
+        {
+          case 0:
+            no = 0;
+            break;
+          case 1:
+            // 15 becomes 7
+            if (no == 15) { no = 8; }
+            else if (no > 8) { no -= 8; }
+            break;
+          case 2:
+            /* give the normal color special treatment, and in really normal
+             * cirumstances it is the dim variant of foreground that is used
+             */
+            if (no == 15) { no = 7; }
+            break;
+          case 3:
+          case 4:
+            if (no < 8)
+              { no += 8; }
+            break;
+          default:
+            break;
+        }
+      r = palettes[vt->palette_no][no][0];
+      g = palettes[vt->palette_no][no][1];
+      b = palettes[vt->palette_no][no][2];
+    }
+  else if (no < 16 + 6*6*6)
+    {
+      no = no-16;
+      b = (no % 6) * 255 / 5;
+      no /= 6;
+      g = (no % 6) * 255 / 5;
+      no /= 6;
+      r = (no % 6) * 255 / 5;
+    }
+  else
+    {
+      int gray = no - (16 + 6*6*6);
+      float val = gray * 255 / 24;
+      r = g = b = val;
+    }
+  rgba[0]=r;
+  rgba[1]=g;
+  rgba[2]=b;
+  rgba[3]=255;
 }
 
-static void
-ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS)
+int vt_keyrepeat (VT *vt)
 {
-  uint8_t pixels[count * rasterizer->format->ebpp];
-  rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count);
+  return vt->keyrepeat;
 }
 
-#if CTX_ENABLE_FLOAT
-static void
-ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+static void vt_flush_bg (VT *vt, Ctx *ctx)
 {
-  float *dstf = (float*)dst;
-  float *srcf = (float*)src;
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
+  if (vt->bg_active)
+  {
+    ctx_rgba8 (ctx, vt->bg_rgba[0], vt->bg_rgba[1], vt->bg_rgba[2], vt->bg_rgba[3]);
+    ctx_rectangle (ctx, vt->bg_x0, vt->bg_y0, vt->bg_width, vt->bg_height);
+    ctx_fill (ctx);
+    vt->bg_active = 0;
+  }
+}
 
-  if (rasterizer->fragment)
-    {
-      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-    }
+static void vt_draw_bg (VT *vt, Ctx *ctx,
+                        float x0, float y0,
+                        float width, float height,
+                        uint8_t *rgba)
+{
+   int same_color = !memcmp(rgba, vt->bg_rgba, 4);
+   if (vt->bg_active && !same_color)
+   {
+     vt_flush_bg (vt, ctx);
+   }
 
-  while (count--)
-  {
-    int cov = *coverage;
-    if (cov == 0)
-    {
-      for (int c = 0; c < components; c++)
-        { dst[c] = 0; }
+   if (vt->bg_active && same_color)
+   {
+     vt->bg_width += width;
+   }
+   else
+   {
+     memcpy (vt->bg_rgba, rgba, 4);
+     vt->bg_active = 1;
+     vt->bg_x0 = x0;
+     vt->bg_y0 = y0;
+     vt->bg_width = width;
+     vt->bg_height = height;
+   }
+}
+
+float vt_draw_cell (VT      *vt, Ctx *ctx,
+                    int      row, int col, // pass 0 to force draw - like
+                    float    x0, float y0, // for scrollback visible
+                    uint64_t style,
+                    uint32_t unichar,
+                    int      dw, int dh,
+                    int      in_smooth_scroll,
+                    int      in_select,
+                    int      is_fg)
+// dw is 0 or 1
+// dh is 0 1 or -1  1 is upper -1 is lower
+{
+  int on_white = vt->reverse_video;
+  int color = 0;
+  int bold = (style & STYLE_BOLD) != 0;
+  int dim = (style & STYLE_DIM) != 0;
+  int is_hidden = (style & STYLE_HIDDEN) != 0;
+  int proportional = (style & STYLE_PROPORTIONAL) != 0;
+  int fg_set = (style & STYLE_FG_COLOR_SET) != 0;
+  int bg_intensity = 0;
+  int fg_intensity = 2;
+  int reverse = ( (style & STYLE_REVERSE) != 0) ^ in_select;
+  int blink = ( (style & STYLE_BLINK) != 0);
+  int blink_fast = ( (style & STYLE_BLINK_FAST) != 0);
+  int cw = vt->cw;
+  int ch = vt->ch;
+  if (proportional)
+    {
+      if (vt->font_is_mono)
+        {
+          ctx_font (ctx, "regular");
+          vt->font_is_mono = 0;
+        }
+      cw = ctx_glyph_width (ctx, unichar);
     }
-    else
-    {
-      if (rasterizer->fragment)
-      {
-        rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
-        u0+=ud;
-        v0+=vd;
-      }
-    if (cov == 255)
+  else
     {
-      for (int c = 0; c < components; c++)
-        dstf[c] = srcf[c];
+      if (vt->font_is_mono == 0)
+        {
+          ctx_font (ctx, "mono");
+          vt->font_is_mono = 1;
+          if (col > 1)
+            {
+              int x = x0;
+              int new_cw = cw - ( (x % cw) );
+              if (new_cw < cw*3/2)
+                { new_cw += cw; }
+              cw = new_cw;
+            }
+        }
     }
-    else
+  float scale_x  = 1.0f;
+  float scale_y  = 1.0f;
+  float offset_y = 0.0f;
+  if (dw)
     {
-      float covf = ctx_u8_to_float (cov);
-      for (int c = 0; c < components; c++)
-        dstf[c] = srcf[c]*covf;
+      scale_x = 2.0f;
     }
+  if (dh)
+    {
+      scale_y = 2.0f;
     }
-    dstf += components;
-    coverage ++;
-  }
-}
-
-static void
-ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float *dstf = (float*)dst;
-  while (count--)
-  {
-#if 0
-    int cov = *coverage;
-    if (cov == 0)
+  if (dh == 1)
     {
+      offset_y = 0.5f;
     }
-    else if (cov == 255)
+  else if (dh == -1)
     {
-#endif
-      switch (components)
-      {
-        case 2:
-          ((uint64_t*)(dst))[0] = 0;
-          break;
-        case 4:
-          ((uint64_t*)(dst))[0] = 0;
-          ((uint64_t*)(dst))[1] = 0;
-          break;
-        default:
-          for (int c = 0; c < components; c++)
-            dstf[c] = 0.0f;
-      }
-#if 0
+      offset_y =  0.0f;
     }
-    else
+  if (in_smooth_scroll)
     {
-      float ralpha = 1.0 - ctx_u8_to_float (cov);
-      for (int c = 0; c < components; c++)
-        { dstf[c] = (dstf[c] * ralpha); }
+      offset_y -= vt->scroll_offset / (dh?2:1);
     }
-    coverage ++;
-#endif
-    dstf += components;
-  }
-}
-
-static void
-ctx_float_source_over_normal_opaque_color (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float *dstf = (float*)dst;
-  float *srcf = (float*)src;
-
-  while (count--)
-  {
-    int cov = *coverage;
-    if (cov)
+  cw *= scale_x;
+  if (blink_fast)
     {
-      if (cov == 255)
-      {
-        for (int c = 0; c < components; c++)
-          dstf[c] = srcf[c];
-      }
+      if ( (vt->blink_state % 2) == 0)
+        { blink = 1; }
       else
-      {
-        float fcov = ctx_u8_to_float (cov);
-        float ralpha = 1.0f - fcov;
-        for (int c = 0; c < components-1; c++)
-          dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
-      }
+        { blink = 0; }
     }
-    coverage ++;
-    dstf+= components;
-  }
-}
-
-inline static void
-ctx_float_blend_normal (int components, float *dst, float *src, float *blended)
-{
-  float a = src[components-1];
-  for (int c = 0; c <  components - 1; c++)
-    blended[c] = src[c] * a;
-  blended[components-1]=a;
-}
-
-static float ctx_float_get_max (int components, float *c)
-{
-  float max = -1000.0f;
-  for (int i = 0; i < components - 1; i ++)
-  {
-    if (c[i] > max) max = c[i];
-  }
-  return max;
-}
-
-static float ctx_float_get_min (int components, float *c)
-{
-  float min = 400.0;
-  for (int i = 0; i < components - 1; i ++)
-  {
-    if (c[i] < min) min = c[i];
-  }
-  return min;
-}
-
-static float ctx_float_get_lum (int components, float *c)
-{
-  switch (components)
-  {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         float sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
-  }
-}
+  else if (blink)
+    {
+      if ( (vt->blink_state % 10) < 5)
+        { blink = 1; }
+      else
+        { blink = 0; }
+    }
+  /*
+     from the vt100 technical-manual:
+
+     "Reverse characters [..] normally have dim backgrounds with
+     black characters so that large white spaces have the same impact
+     on the viewer's eye as the smaller brighter white areas of
+     normal characters. Bold and reverse asserted together give a
+     background of normal intensity. Blink applied to nonreverse
+     characters causes them to alternate between their usual
+     intensity and the next lower intensity. (Normal characters vary
+     between normal and dim intensity. Bold characters vary between
+     bright and normal intensity.) Blink applied to a reverse
+     character causes that character to alternate between normal and
+     reverse video representations of that character."
+
+     This is in contrast with how the truth table appears to be
+     meant used, since it uses a reverse computed as the xor of
+     the global screen reverse and the reverse attribute of the
+     cell.
+
+     To fulfil the more asthethic resulting from implementing the
+     text, and would be useful to show how the on_bright background
+     mode of the vt100 actually displays the vttest.
 
-static float ctx_float_get_sat (int components, float *c)
-{
-  switch (components)
-  {
-    case 3:
-    case 4:
-            { float r = c[0];
-              float g = c[1];
-              float b = c[2];
-              return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b));
+     */
+  if (on_white)
+    {
+          if (bold)
+            {
+              bg_intensity =           2;
+              fg_intensity = blink?1:  0;
             }
-            break;
-    case 1:
-    case 2: return 0.0;
-            break;
-    default:
-       {
-         float min = 1000;
-         float max = -1000;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           if (c[i] < min) min = c[i];
-           if (c[i] > max) max = c[i];
-         }
-         return max-min;
-       }
-  }
-}
-
-static void ctx_float_set_lum (int components, float *c, float lum)
-{
-  float d = lum - ctx_float_get_lum (components, c);
-  float tc[components];
-  for (int i = 0; i < components - 1; i++)
-  {
-    tc[i] = c[i] + d;
-  }
-
-  float l = ctx_float_get_lum (components, tc);
-  float n = ctx_float_get_min (components, tc);
-  float x = ctx_float_get_max (components, tc);
-
-  if (n < 0.0f && l != n)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * l) / (l-n));
-  }
-
-  if (x > 1.0f && x != l)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l));
-  }
-  for (int i = 0; i < components - 1; i++)
-    c[i] = tc[i];
-}
-
-static void ctx_float_set_sat (int components, float *c, float sat)
-{
-  int max = 0, mid = 1, min = 2;
-  
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-
-  if (c[max] > c[min])
-  {
-    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
-    c[max] = sat;
-  }
-  else
-  {
-    c[mid] = c[max] = 0.0f;
-  }
-  c[min] = 0.0f;
-
-}
-
-#define ctx_float_blend_define(name, CODE) \
-static void \
-ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\
-{\
-  float *s = src; float b[components];\
-  ctx_float_deassociate_alpha (components, dst, b);\
-    CODE;\
-  blended[components-1] = s[components-1];\
-  ctx_float_associate_alpha (components, blended);\
-}
-
-#define ctx_float_blend_define_seperable(name, CODE) \
-        ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
-
-ctx_float_blend_define_seperable(multiply,    blended[c] = (b[c] * s[c]);)
-ctx_float_blend_define_seperable(screen,      blended[c] = b[c] + s[c] - (b[c] * s[c]);)
-ctx_float_blend_define_seperable(overlay,     blended[c] = b[c] < 0.5f ? (s[c] * b[c]) :
-                                                          s[c] + b[c] - (s[c] * b[c]);)
-ctx_float_blend_define_seperable(darken,      blended[c] = ctx_minf (b[c], s[c]))
-ctx_float_blend_define_seperable(lighten,     blended[c] = ctx_maxf (b[c], s[c]))
-ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f :
-                                     s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c])))
-ctx_float_blend_define_seperable(color_burn,  blended[c] = (b[c] == 1.0f) ? 1.0f :
-                                     s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c]))
-ctx_float_blend_define_seperable(hard_light,  blended[c] = s[c] < 0.f ? (b[c] * s[c]) :
-                                                          b[c] + s[c] - (b[c] * s[c]);)
-ctx_float_blend_define_seperable(difference,  blended[c] = (b[c] - s[c]))
-
-ctx_float_blend_define_seperable(divide,      blended[c] = s[c]?(b[c]) / s[c]:0.0f)
-ctx_float_blend_define_seperable(addition,    blended[c] = s[c]+b[c])
-ctx_float_blend_define_seperable(subtract,    blended[c] = s[c]-b[c])
-
-ctx_float_blend_define_seperable(exclusion,   blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c])
-ctx_float_blend_define_seperable(soft_light,
-  if (s[c] <= 0.5f)
+          else if (dim)
+            {
+              bg_intensity =           2;
+              fg_intensity = blink?3:  1;
+            }
+          else
+            {
+              bg_intensity =           2;
+              fg_intensity = blink?1:  0;
+            }
+          if (fg_set)
+            {
+              fg_intensity = blink?2:3;
+            }
+    }
+  else /* bright on dark */
+    {
+          if (bold)
+            {
+              bg_intensity =           0;
+              fg_intensity = blink?2:  3;
+            }
+          else if (dim)
+            {
+              bg_intensity =           0;
+              fg_intensity = blink?0:  1;
+            }
+          else
+            {
+              bg_intensity =           0;
+              fg_intensity = blink?1:  2;
+            }
+    }
+  uint8_t bg_rgb[4]= {0,0,0,255};
+  uint8_t fg_rgb[4]= {255,255,255,255};
   {
-    blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]);
+      //ctx_begin_path (ctx);
+      if (style &  STYLE_BG24_COLOR_SET)
+        {
+          uint64_t temp = style >> 40;
+          bg_rgb[0] = temp & 0xff;
+          temp >>= 8;
+          bg_rgb[1] = temp & 0xff;
+          temp >>= 8;
+          bg_rgb[2] = temp & 0xff;
+#if 0
+          if (dh)
+          {
+             bg_rgb[0] = 
+             bg_rgb[1] =
+             bg_rgb[2] = 30;
+          }
+#endif
+        }
+      else
+        {
+          if (style & STYLE_BG_COLOR_SET)
+            {
+              color = (style >> 40) & 255;
+              bg_intensity = -1;
+              vt_ctx_get_color (vt, color, bg_intensity, bg_rgb);
+            }
+          else
+            {
+              switch (bg_intensity)
+                {
+                  case 0:
+                    for (int i = 0; i <3 ; i++)
+                      { bg_rgb[i] = vt->bg_color[i]; }
+                    break;
+                  case 1:
+                    for (int i = 0; i <3 ; i++)
+                      { bg_rgb[i] = vt->bg_color[i] * 0.5 + vt->fg_color[i] * 0.5; }
+                    break;
+                  case 2:
+                    for (int i = 0; i <3 ; i++)
+                      { bg_rgb[i] = vt->bg_color[i] * 0.05 + vt->fg_color[i] * 0.95; }
+                    break;
+                  case 3:
+                    for (int i = 0; i <3 ; i++)
+                      { bg_rgb[i] = vt->fg_color[i]; }
+                    break;
+                }
+            }
+        }
   }
+  if (style & STYLE_FG24_COLOR_SET)
+    {
+      uint64_t temp = style >> 16;
+      fg_rgb[0] = temp & 0xff;
+      temp >>= 8;
+      fg_rgb[1] = temp & 0xff;
+      temp >>= 8;
+      fg_rgb[2] = temp & 0xff;
+    }
   else
-  {
-    int d;
-    if (b[c] <= 255/4)
-      d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]);
-    else
-      d = ctx_sqrtf(b[c]);
-    blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c]));
-  }
-)
-
-
-ctx_float_blend_define(color,
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s));
-)
-
-ctx_float_blend_define(hue,
-  float in_sat = ctx_float_get_sat(components, b);
-  float in_lum = ctx_float_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_float_set_sat(components, blended, in_sat);
-  ctx_float_set_lum(components, blended, in_lum);
-)
-
-ctx_float_blend_define(saturation,
-  float in_sat = ctx_float_get_sat(components, s);
-  float in_lum = ctx_float_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_float_set_sat(components, blended, in_sat);
-  ctx_float_set_lum(components, blended, in_lum);
-)
-
-ctx_float_blend_define(luminosity,
-  float in_lum = ctx_float_get_lum(components, s);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_float_set_lum(components, blended, in_lum);
-)
+    {
+      if ( (style & STYLE_FG_COLOR_SET) == 0)
+        {
+          switch (fg_intensity)
+            {
+              case 0:
+                for (int i = 0; i <3 ; i++)
+                  { fg_rgb[i] = vt->bg_color[i] * 0.7 + vt->fg_color[i] * 0.3; }
+                break;
+              case 1:
+                for (int i = 0; i <3 ; i++)
+                  { fg_rgb[i] = vt->bg_color[i] * 0.5 + vt->fg_color[i] * 0.5; }
+                break;
+              case 2:
+                for (int i = 0; i <3 ; i++)
+                  { fg_rgb[i] = vt->bg_color[i] * 0.20 + vt->fg_color[i] * 0.80; }
+                break;
+              case 3:
+                for (int i = 0; i <3 ; i++)
+                  { fg_rgb[i] = vt->fg_color[i]; }
+            }
+        }
+      else
+        {
+          color = (style >> 16) & 255;
+          vt_ctx_get_color (vt, color, fg_intensity, fg_rgb);
+        }
+  }
 
-inline static void
-ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended)
-{
-  switch (blend)
+  if (reverse)
   {
-    case CTX_BLEND_NORMAL:      ctx_float_blend_normal      (components, dst, src, blended); break;
-    case CTX_BLEND_MULTIPLY:    ctx_float_blend_multiply    (components, dst, src, blended); break;
-    case CTX_BLEND_SCREEN:      ctx_float_blend_screen      (components, dst, src, blended); break;
-    case CTX_BLEND_OVERLAY:     ctx_float_blend_overlay     (components, dst, src, blended); break;
-    case CTX_BLEND_DARKEN:      ctx_float_blend_darken      (components, dst, src, blended); break;
-    case CTX_BLEND_LIGHTEN:     ctx_float_blend_lighten     (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_BURN:  ctx_float_blend_color_burn  (components, dst, src, blended); break;
-    case CTX_BLEND_HARD_LIGHT:  ctx_float_blend_hard_light  (components, dst, src, blended); break;
-    case CTX_BLEND_SOFT_LIGHT:  ctx_float_blend_soft_light  (components, dst, src, blended); break;
-    case CTX_BLEND_DIFFERENCE:  ctx_float_blend_difference  (components, dst, src, blended); break;
-    case CTX_BLEND_EXCLUSION:   ctx_float_blend_exclusion   (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR:       ctx_float_blend_color       (components, dst, src, blended); break;
-    case CTX_BLEND_HUE:         ctx_float_blend_hue         (components, dst, src, blended); break;
-    case CTX_BLEND_SATURATION:  ctx_float_blend_saturation  (components, dst, src, blended); break;
-    case CTX_BLEND_LUMINOSITY:  ctx_float_blend_luminosity  (components, dst, src, blended); break;
-    case CTX_BLEND_ADDITION:    ctx_float_blend_addition    (components, dst, src, blended); break;
-    case CTX_BLEND_SUBTRACT:    ctx_float_blend_subtract    (components, dst, src, blended); break;
-    case CTX_BLEND_DIVIDE:      ctx_float_blend_divide      (components, dst, src, blended); break;
+    for (int c = 0; c < 3; c ++)
+    {
+      int t = bg_rgb[c];
+      bg_rgb[c] = fg_rgb[c];
+      fg_rgb[c] = t;
+    }
   }
-}
-
-/* this is the grunt working function, when inlined code-path elimination makes
- * it produce efficient code.
- */
-CTX_INLINE static void
-ctx_float_porter_duff (CtxRasterizer         *rasterizer,
-                       int                    components,
-                       uint8_t * __restrict__ dst,
-                       uint8_t * __restrict__ src,
-                       int                    x0,
-                       uint8_t * __restrict__ coverage,
-                       int                    count,
-                       CtxCompositingMode     compositing_mode,
-                       CtxFragment            fragment,
-                       CtxBlend               blend)
-{
-  float *dstf = (float*)dst;
 
-  CtxPorterDuffFactor f_s, f_d;
-  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
-  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
-  float   global_alpha_f = rasterizer->state->gstate.global_alpha_f;
-  
+  if (is_fg ||
+      ((!on_white) && bg_rgb[0]==0 && bg_rgb[1]==0 && bg_rgb[2]==0) ||
+      ((on_white) && bg_rgb[0]==255 && bg_rgb[1]==255 && bg_rgb[2]==255))
+          /* these comparisons are not entirely correct, when on dark background we assume black to
+           * be default and non-set, even when theme might differ
+           */
   {
-    float tsrc[components];
-    float u0 = 0; float v0 = 0;
-    float ud = 0; float vd = 0;
+    /* skipping draw of background */
+  }
+  else
+  {
+    if (dh)
+    {
+      vt_draw_bg (vt, ctx, ctx_floorf(x0),
+         ctx_floorf(y0 - ch - ch * (vt->scroll_offset)), cw, ch, bg_rgb);
+    }
+    else
+    {
+      vt_draw_bg (vt, ctx, x0, y0 - ch + ch * offset_y, cw, ch, bg_rgb);
+    }
+  }
 
-    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  if (!is_fg)
+    return cw;
 
-    while (count--)
+  int italic        = (style & STYLE_ITALIC) != 0;
+  int strikethrough = (style & STYLE_STRIKETHROUGH) != 0;
+  int overline      = (style & STYLE_OVERLINE) != 0;
+  int underline     = (style & STYLE_UNDERLINE) != 0;
+  int underline_var = (style & STYLE_UNDERLINE_VAR) != 0;
+  if (dh == 1)
     {
-      int cov = *coverage;
-#if 1
-      if (
-        CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)||
-        (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER ||
-        compositing_mode == CTX_COMPOSITE_XOR               ||
-        compositing_mode == CTX_COMPOSITE_DESTINATION_OUT   ||
-        compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      
-        ))))
-      {
-        u0 += ud;
-        v0 += vd;
-        coverage ++;
-        dstf+=components;
-        continue;
-      }
-#endif
+      underline = underline_var = 0;
+    }
+  int double_underline = 0;
+  int curved_underline = 0;
+  if (underline_var)
+    {
+      if (underline)
+        {
+          double_underline = 1;
+        }
+      else
+        {
+          curved_underline = 1;
+        }
+    }
 
-      fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
-      if (blend != CTX_BLEND_NORMAL)
-        ctx_float_blend (components, blend, dstf, tsrc, tsrc);
-      u0 += ud;
-      v0 += vd;
-      float covf = ctx_u8_to_float (cov);
+  int has_underline = (underline || double_underline || curved_underline);
 
-      if (global_alpha_u8 != 255)
-        covf = covf * global_alpha_f;
+  if (unichar == ' ' && !has_underline)
+    is_hidden = 1;
 
-      if (covf != 1.0f)
-      {
-        for (int c = 0; c < components; c++)
-          tsrc[c] *= covf;
-      }
+  if (!is_hidden)
+    {
 
-      for (int c = 0; c < components; c++)
-      {
-        float res;
-        /* these switches and this whole function is written to be
-         * inlined when compiled when the enum values passed in are
-         * constants.
-         */
-        switch (f_s)
+      ctx_rgba8 (ctx, fg_rgb[0], fg_rgb[1], fg_rgb[2], 255);
+
+
+      if (italic)
         {
-          case CTX_PORTER_DUFF_0: res = 0.0f; break;
-          case CTX_PORTER_DUFF_1:             res = (tsrc[c]); break;
-          case CTX_PORTER_DUFF_ALPHA:         res = (tsrc[c] *       dstf[components-1]); break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break;
+          ctx_save (ctx);
+          ctx_translate (ctx, (x0 + cw/3), (y0 + vt->ch/2) );
+          ctx_scale (ctx, 0.9, 0.9);
+          ctx_rotate (ctx, 0.15);
+          ctx_translate (ctx, - (x0 + cw/3), - (y0 + vt->ch/2) );
         }
-        switch (f_d)
+      vt_ctx_glyph (ctx, vt, x0, y0, unichar, bold, scale_x, scale_y, offset_y);
+      if (italic)
         {
-          case CTX_PORTER_DUFF_0: dstf[c] = res; break;
-          case CTX_PORTER_DUFF_1:             dstf[c] = res + (dstf[c]); break;
-          case CTX_PORTER_DUFF_ALPHA:         dstf[c] = res + (dstf[c] *       tsrc[components-1]); break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break;
+          ctx_restore (ctx);
+        }
+      if (curved_underline)
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.07 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, (cw+2) /3, -vt->ch * 0.05);
+          ctx_rel_line_to (ctx, (cw+2) /3, vt->ch * 0.1);
+          ctx_rel_line_to (ctx, (cw+2) /3, -vt->ch * 0.05);
+          //ctx_rel_line_to (ctx, cw, 0);
+          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.050:0.04) );
+          ctx_stroke (ctx);
+        }
+      else if (double_underline)
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.130 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.030 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.050:0.04) );
+          ctx_stroke (ctx);
+        }
+      else if (underline)
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.07 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
+          ctx_stroke (ctx);
+        }
+      if (overline)
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.94 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
+          ctx_stroke (ctx);
+        }
+      if (strikethrough)
+        {
+          ctx_begin_path (ctx);
+          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.43 - vt->ch * vt->scroll_offset);
+          ctx_rel_line_to (ctx, cw, 0);
+          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
+          ctx_stroke (ctx);
         }
-      }
-      coverage ++;
-      dstf+=components;
     }
-  }
+  return cw;
 }
 
-/* generating one function per compositing_mode would be slightly more efficient,
- * but on embedded targets leads to slightly more code bloat,
- * here we trade off a slight amount of performance
- */
-#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \
-static void \
-ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
-{ \
-   switch (rasterizer->state->gstate.compositing_mode) \
-   { \
-     case CTX_COMPOSITE_SOURCE_ATOP: \
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
-        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_XOR, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_COPY, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_CLEAR:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_CLEAR, fragment, blend);\
-       break;\
-   }\
+int vt_has_blink (VT *vt)
+{
+  if (!vt) return 0;
+  return (vt->in_smooth_scroll ?  10 : 0);
+  //return vt->has_blink + (vt->in_smooth_scroll ?  10 : 0);
 }
-#endif
-
-#if CTX_ENABLE_RGBAF
-
-ctx_float_porter_duff(RGBAF, 4,color,           rasterizer->fragment,                               
rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(RGBAF, 4,generic,         rasterizer->fragment,               
rasterizer->state->gstate.blend_mode)
 
-#if CTX_INLINED_NORMAL
-#if CTX_GRADIENTS
-ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
-#endif
-ctx_float_porter_duff(RGBAF, 4,image,           ctx_fragment_image_RGBAF,           
rasterizer->state->gstate.blend_mode)
+//extern int enable_terminal_menu;
+//
 
+//void ctx_set_popup (Ctx *ctx, void (*popup)(Ctx *ctx, void *data), void *popup_data);
+
+static char *primary = NULL;
+static void scrollbar_drag (CtxEvent *event, void *data, void *data2);
+static int scrollbar_down = 0;
+
+void vt_mouse_event (CtxEvent *event, void *data, void *data2)
+{
+  VT   *vt = data;
+  CtxClient *client = vt_get_client (vt);
+  float  x = event->x;
+  float  y = event->y;
+  int device_no = event->device_no;
+  char buf[128]="";
+  if ((!vt->in_alt_screen) &&
+      (event->x > vt->width - vt->cw * 1.5 || scrollbar_down) &&
+      (event->type == CTX_DRAG_MOTION ||
+      event->type == CTX_DRAG_PRESS ||
+      event->type == CTX_DRAG_RELEASE))
+    return scrollbar_drag (event, data, data2);
+  switch (event->type)
+  {
+    case CTX_MOTION:
+    case CTX_DRAG_MOTION:
+      //if (event->device_no==1)
+      {
+        sprintf (buf, "mouse-motion %.0f %.0f %i", x, y, device_no);
+//      ctx_set_dirty (event->ctx, 1);
+        ctx_client_lock (client);
+        vt_feed_keystring (vt, event, buf);
+        ctx_client_unlock (client);
+//      vt->rev++;
+      }
+      break;
+    case CTX_DRAG_PRESS:
+      if (event->device_no==2)
+      {
+        if (primary)
+        {
+          if (vt)
+            vt_paste (vt, primary);
+        }
+      }
+      else if (event->device_no==3 && !vt->in_alt_screen)
+      {
+        vt->popped = 1;
+      }
+      else
+      {
+        sprintf (buf, "mouse-press %.0f %.0f %i", x, y, device_no);
+        ctx_client_lock (client);
+        vt_feed_keystring (vt, event, buf);
+        ctx_client_unlock (client);
+//      ctx_set_dirty (event->ctx, 1);
+//      vt->rev++;
+      }
+      break;
+    case CTX_DRAG_RELEASE:
+      if (event->device_no==3 && !vt->in_alt_screen)
+      {
+        vt->popped = 0;
+      }
+        ctx_set_dirty (event->ctx, 1);
+        sprintf (buf, "mouse-release %.0f %.0f %i", x, y, device_no);
+        ctx_client_lock (client);
+        vt_feed_keystring (vt, event, buf);
+        ctx_client_unlock (client);
+      break;
+    default:
+      // we should not stop propagation
+      return;
+      break;
+  }
+  event->stop_propagate = 1;
+//vt->rev++;
+}
+static int scrollbar_focused = 0;
+#if 0
+static void scrollbar_enter (CtxEvent *event, void *data, void *data2)
+{
+  VT *vt = data;
+  vt->rev++;
+  scrollbar_focused = 1;
+}
 
-#if CTX_GRADIENTS
-#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
-ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
-ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
-ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name,  
ctx_fragment_linear_gradient_RGBA8, blend_mode)\
-ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name,  
ctx_fragment_radial_gradient_RGBA8, blend_mode)\
-ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
-#else
-#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
-ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
-ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
-ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
+static void scrollbar_leave (CtxEvent *event, void *data, void *data2)
+{
+  VT *vt = data;
+  vt->rev++;
+  scrollbar_focused = 0;
+}
 #endif
 
-ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal)
+static void scrollbar_drag (CtxEvent *event, void *data, void *data2)
+{
+  VT *vt = data;
+  float disp_lines = vt->rows;
+  float tot_lines = vt->line_count + vt->scrollback_count;
 
+  vt->scroll = tot_lines - disp_lines - (event->y*1.0/ ctx_client_height (vt->id)) * tot_lines + 
disp_lines/2;
+  if (vt->scroll < 0) { vt->scroll = 0.0; }
+  if (vt->scroll > vt->scrollback_count) { vt->scroll = vt->scrollback_count; }
+  vt->rev++;
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 
-static void
-ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
+  switch (event->type)
+  {
+    case CTX_DRAG_PRESS:
+      scrollbar_down = 1;
+      break;
+    case CTX_DRAG_RELEASE:
+      scrollbar_down = 0;
+      break;
+    default:
+      break;
+  }
 }
 
-static void
-ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+#if 0
+static void scroll_handle_drag (CtxEvent *event, void *data, void *data2)
 {
-  ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
+  VT *vt = data;
+  float tot_lines = vt->line_count + vt->scrollback_count;
+  if (event->type == CTX_DRAG_MOTION)
+  {
+    vt->scroll -= (event->delta_y * tot_lines) / (vt->rows * vt->ch);
+  }
+  if (vt->scroll < 0) { vt->scroll = 0.0; }
+  if (vt->scroll > vt->scrollback_count) { vt->scroll = vt->scrollback_count; }
+  vt->rev++;
+  event->stop_propagate = 1;
 }
+#endif
 
 #if 0
-static void
-ctx_RGBAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
+static void test_popup (Ctx *ctx, void *data)
 {
-  ctx_float_source_over_normal_opaque_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count);
+  VT *vt = data;
+
+  float x = ctx_client_x (vt->id);
+  float y = ctx_client_y (vt->id);
+  ctx_rectangle (ctx, x, y, 100, 100);
+  ctx_rgb (ctx, 1,0,0);
+  ctx_fill (ctx);
 }
 #endif
-#endif
 
-static void
-ctx_setup_RGBAF (CtxRasterizer *rasterizer)
+void itk_style_color (Ctx *ctx, const char *name); // only itk fun used in vt
+
+void vt_use_images (VT *vt, Ctx *ctx)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 4;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer);
-#if 1
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  /*  this is a call intended for minimized/shaded fully obscured
+   *  clients to make sure their textures are kept alive
+   *  in the server
+   */
+  //float x0=0;
+  float y0=0;
+  //vt->has_blink = 0;
+  //vt->blink_state++;
+
+  ctx_save (ctx);
+
+  {
+    /* draw graphics */
+    for (int row = ((vt->scroll!=0.0f)?vt->scroll:0);
+         row < (vt->scroll) + vt->rows * 4;
+         row ++)
     {
-      rasterizer->comp_op = ctx_RGBAF_porter_duff_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;
+       CtxList *l = ctx_list_nth (vt->lines, row);
+       float y = y0 + vt->ch * (vt->rows - row);
+
+       if (row >= vt->rows && !vt->in_alt_screen)
+         {
+           l = ctx_list_nth (vt->scrollback, row-vt->rows);
+         }
+
+       if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
+         {
+           VtLine *line = l->data;
+           if (line->ctx_copy)
+             {
+               ctx_render_ctx_textures (line->ctx_copy, ctx);
+             }
+         }
     }
-  else
-#endif
-  {
-    rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
   }
+  ctx_restore (ctx);
+}
 
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_RGBAF_clear_normal;
-  else
-    switch (gstate->blend_mode)
+
+void vt_register_events (VT *vt, Ctx *ctx, double x0, double y0)
+{
+  ctx_begin_path (ctx);
+  ctx_save (ctx);
+  ctx_translate (ctx, x0, y0);
+  ctx_rectangle (ctx, 0, 0, vt->cols * vt->cw, vt->rows * vt->ch);
+  ctx_listen (ctx, CTX_DRAG,   vt_mouse_event, vt, NULL);
+  ctx_listen (ctx, CTX_MOTION, vt_mouse_event, vt, NULL);
+  ctx_begin_path (ctx);
+  ctx_restore (ctx);
+}
+
+void vt_draw (VT *vt, Ctx *ctx, double x0, double y0)
+{
+  ctx_begin_path (ctx);
+  ctx_save (ctx);
+  ctx_translate (ctx, x0, y0);
+  x0 = 0;
+  y0 = 0;
+  ctx_font (ctx, "mono");
+  vt->font_is_mono = 0;
+  ctx_font_size (ctx, vt->font_size * vt->font_to_cell_scale);
+  vt->has_blink = 0;
+  vt->blink_state++;
+#if 0
+  int cursor_x_px = 0;
+  int cursor_y_px = 0;
+  int cursor_w = vt->cw;
+  int cursor_h = vt->ch;
+  cursor_x_px = x0 + (vt->cursor_x - 1) * vt->cw;
+  cursor_y_px = y0 + (vt->cursor_y - 1) * vt->ch;
+  cursor_w = vt->cw;
+  cursor_h = vt->ch;
+#endif
+  ctx_save (ctx);
+  //if (vt->scroll || full)
     {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_RGBAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
+      ctx_begin_path (ctx);
+#if 1
+      ctx_rectangle (ctx, 0, 0, vt->width, //(vt->cols) * vt->cw,
+                     (vt->rows) * vt->ch);
+      if (vt->reverse_video)
         {
-          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+          itk_style_color (ctx, "terminal-bg-reverse");
+          ctx_fill  (ctx);
         }
-        else
-        switch (gstate->source_fill.type)
+      else
         {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)(rasterizer->color))[components-1] == 0.0f)
-                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal;
-            }
-            break;
-#if CTX_GRADIENTS
-          case CTX_SOURCE_LINEAR_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal;
-            break;
-          case CTX_SOURCE_RADIAL_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal;
-            break;
-#endif
-          case CTX_SOURCE_TEXTURE:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal;
-            break;
-          default:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal;
-            break;
+          itk_style_color (ctx, "terminal-bg");
+          ctx_fill  (ctx);
         }
-        break;
-      default:
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
-//          rasterizer->fragment = NULL;
-            break;
-#if CTX_GRADIENTS
-          case CTX_SOURCE_LINEAR_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient;
-            break;
-          case CTX_SOURCE_RADIAL_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient;
-            break;
 #endif
-          case CTX_SOURCE_TEXTURE:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_image;
-            break;
-          default:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
-            break;
-        }
-        break;
+      if (vt->scroll != 0.0f)
+        ctx_translate (ctx, 0.0, vt->ch * vt->scroll);
     }
-#endif
-}
+  /* draw terminal lines */
+   {
+     for (int row = (vt->scroll!=0.0f)?vt->scroll:0; row < (vt->scroll) + vt->rows; row ++)
+       {
+         CtxList *l = ctx_list_nth (vt->lines, row);
+         float y = y0 + vt->ch * (vt->rows - row);
+         if (row >= vt->rows)
+           {
+             l = ctx_list_nth (vt->scrollback, row-vt->rows);
+           }
+         if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
+           {
+             VtLine *line = l->data;
+             int r = vt->rows - row;
+             const char *data = line->string.str;
 
+             vt->bg_active = 0;
+             for (int is_fg = 0; is_fg < 2; is_fg++)
+             {
+               const char *d = data;
+               float x = x0;
+               uint64_t style = 0;
+               uint32_t unichar = 0;
+               int in_scrolling_region = vt->in_smooth_scroll &&
+                   ((r >= vt->margin_top && r <= vt->margin_bottom) || r <= 0);
+               if (is_fg)
+                  vt_flush_bg (vt, ctx);
+  
+               for (int col = 1; col <= vt->cols * 1.33 && x < vt->cols * vt->cw; col++)
+                 {
+                   int c = col;
+                   int real_cw;
+                   int in_selected_region = 0;
+                   //if (vt->in_alt_screen == 0)
+                   {
+                   if (r > vt->select_start_row && r < vt->select_end_row)
+                     {
+                       in_selected_region = 1;
+                     }
+                   else if (r == vt->select_start_row)
+                     {
+                       if (col >= vt->select_start_col) { in_selected_region = 1; }
+                       if (r == vt->select_end_row)
+                         {
+                           if (col > vt->select_end_col) { in_selected_region = 0; }
+                         }
+                     }
+                   else if (r == vt->select_end_row)
+                     {
+                       in_selected_region = 1;
+                       if (col > vt->select_end_col) { in_selected_region = 0; }
+                     }
+                   }
+                   if (vt->select_active == 0) in_selected_region = 0;
+                   style = vt_line_get_style (line, col-1);
+                   unichar = d?ctx_utf8_to_unichar (d) :' ';
+  
+                   int is_cursor = 0;
+                   if (vt->cursor_x == col && vt->cursor_y == vt->rows - row && vt->cursor_visible)
+                      is_cursor = 1;
+  
+                   real_cw=vt_draw_cell (vt, ctx, r, c, x, y, style, unichar,
+                                         line->double_width,
+                                         line->double_height_top?1:
+                                         line->double_height_bottom?-1:0,
+                                         in_scrolling_region,
+                                         in_selected_region ^ is_cursor, is_fg);
+                   if (r == vt->cursor_y && col == vt->cursor_x)
+                     {
+#if 0
+                       cursor_x_px = x;
+#endif
+                     }
+                   x+=real_cw;
+                   if (style & STYLE_BLINK ||
+                       style & STYLE_BLINK_FAST)
+                     {
+                       vt->has_blink = 1;
+                     }
+                   if (d)
+                     {
+                       d = mrg_utf8_skip (d, 1);
+                       if (!*d) { d = NULL; }
+                     }
+                 }
+             }
+#if 0
+             if (line->wrapped)
+             {
+               ctx_rectangle (ctx, x0, y, 10, 10);
+               ctx_rgb (ctx, 1,0,0);
+               ctx_fill (ctx);
+             }
 #endif
-#if CTX_ENABLE_GRAYAF
-
-#if CTX_GRADIENTS
-static void
-ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0 ; i < count; i++)
-  {
-  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                g->linear_gradient.length) -
-              g->linear_gradient.start) * (g->linear_gradient.rdelta);
-  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0, rgba);
-  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
-  ((float*)out)[1] = rgba[3];
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
+          }
+      }
   }
-}
 
-static void
-ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i ++)
-  {
-  float v = 0.0f;
-  if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f)
-    {
-      v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
-      v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta);
+#if 0
+  /* draw cursor (done inline with fg/bg reversing, some cursor styles might need
+   * additional drawing though
+   */
+  if (vt->cursor_visible)
+    {
+    //  ctx_rgba (ctx, 0.9, 0.8, 0.0, 0.5333);
+      ctx_rgba (ctx, 1.0,1.0,1.0,1.0);
+      ctx_begin_path (ctx);
+      ctx_rectangle (ctx,
+                     cursor_x_px, cursor_y_px,
+                     cursor_w, cursor_h);
+      ctx_fill (ctx);
     }
-  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba);
-  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
-  ((float*)out)[1] = rgba[3];
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
-  }
-}
 #endif
 
-static void
-ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
-{
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
+
   {
-     ctx_color_get_graya (rasterizer->state, &g->color, (float*)out);
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
+    /* draw graphics */
+     for (int row = ((vt->scroll!=0.0f)?vt->scroll:0); row < (vt->scroll) + vt->rows * 4; row ++)
+      {
+        CtxList *l = ctx_list_nth (vt->lines, row);
+        float y = y0 + vt->ch * (vt->rows - row);
+
+        if (row >= vt->rows && !vt->in_alt_screen)
+          {
+            l = ctx_list_nth (vt->scrollback, row-vt->rows);
+          }
+
+        if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
+          {
+            VtLine *line = l->data;
+            {
+            for (int i = 0; i < 4; i++)
+              {
+                Image *image = line->images[i];
+                if (image)
+                  {
+                    int u = (line->image_col[i]-1) * vt->cw + (line->image_X[i] * vt->cw);
+                    int v = y - vt->ch + (line->image_Y[i] * vt->ch);
+                //  int rows = (image->height + (vt->ch-1) ) /vt->ch;
+                //
+                //
+                    if (v + image->height +vt->scroll * vt->ch > 0.0 &&
+                        image->width && image->height /* some ghost images appear with these */
+                        )
+                    {
+                    ctx_save (ctx);
+                    ctx_rectangle (ctx, x0, y0 - vt->scroll * vt->ch, vt->cw * vt->cols,
+                                    vt->ch * vt->rows);
+                    ctx_clip (ctx);
+                    char texture_n[65]; 
+
+                    sprintf (texture_n, "vtimg%i", image->eid_no);
+                    ctx_rectangle (ctx, u, v, image->width, image->height);
+                    ctx_translate (ctx, u, v);
+
+                    //replace this texture_n with NULL to
+                    // be content addressed - but bit slower
+                    ctx_define_texture (ctx, texture_n, image->width,
+                                        image->height,
+                                        0,
+                                        image->kitty_format == 32 ?
+                                                 CTX_FORMAT_RGBA8 :
+                                                 CTX_FORMAT_RGB8,
+                                        image->data, texture_n);
+                    ctx_fill (ctx);
+
+                    ctx_restore (ctx);
+                    }
+                  }
+              }
+
+            if (line->ctx_copy)
+              {
+                //fprintf (stderr, " [%i]\n", _ctx_frame (ctx));
+                //ctx_render_stream (line->ctx_copy, stderr, 1);
+
+                ctx_begin_path (ctx);
+                ctx_save (ctx);
+                ctx_rectangle (ctx, x0, y0 - vt->scroll * vt->ch, vt->cw * vt->cols,
+                                    vt->ch * vt->rows);
+                ctx_clip (ctx);
+                ctx_translate (ctx, 0.0, y - vt->ch);
+
+                //(vt->rows-row-1) * (vt->ch) );
+                //float factor = vt->cols * vt->cw / 1000.0;
+                //ctx_scale (ctx, factor, factor);
+                //
+                ctx_render_ctx (line->ctx_copy, ctx);
+                ctx_restore (ctx);
+              }
+            }
+          }
+    //  y -= vt->ch;
+      }
   }
-}
 
-static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  uint8_t rgba[4];
-  float rgbaf[4];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
+
+  for (int i = 0; i < 4; i++)
     {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+      if (vt->leds[i])
+        {
+          ctx_rgba (ctx, .5,1,.5,0.8);
+          ctx_rectangle (ctx, vt->cw * i + vt->cw * 0.25, vt->ch * 0.25, vt->cw/2, vt->ch/2);
+          ctx_fill (ctx);
+        }
     }
-  for (int c = 0; c < 2 * count; c ++) { 
-    rgbaf[c] = ctx_u8_to_float (rgba[c]);
-    ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf);
-    ((float*)out)[1] = rgbaf[3];
-    out = ((float*)out) + 2;
-  }
-}
+  ctx_restore (ctx);
+//#define SCROLL_SPEED 0.25;
+#define SCROLL_SPEED 0.001;
+  if (vt->in_smooth_scroll)
+   {
+     if (vt->in_smooth_scroll<0)
+       {
+         vt->scroll_offset += SCROLL_SPEED;
+         if (vt->scroll_offset >= 0.0)
+           {
+             vt->scroll_offset = 0;
+             vt->in_smooth_scroll = 0;
+             vt->rev++;
+           }
+       }
+     else
+       {
+         vt->scroll_offset -= SCROLL_SPEED;
+         if (vt->scroll_offset <= 0.0)
+           {
+             vt->scroll_offset = 0;
+             vt->in_smooth_scroll = 0;
+             vt->rev++;
+           }
+       }
+   }
 
-static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+    /* scrollbar */
+    if (!vt->in_alt_screen)
     {
-      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;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF;
+      float disp_lines = vt->rows;
+      float tot_lines = vt->line_count + vt->scrollback_count;
+      float offset = (tot_lines - disp_lines - vt->scroll) / tot_lines;
+      float win_len = disp_lines / tot_lines;
+
+#if 0
+      ctx_rectangle (ctx, (vt->cols *vt->cw), 0, 
+                       (vt->width) - (vt->cols * vt->cw),
+                       vt->rows *  vt->ch);
+      ctx_rgb (ctx,1,0,0);
+      ctx_fill (ctx);
 #endif
+
+      ctx_rectangle (ctx, (vt->width) - vt->cw * 1.5,
+                     0, 1.5 * vt->cw,
+                     vt->rows * vt->ch);
+      //ctx_listen (ctx, CTX_DRAG,  scrollbar_drag, vt, NULL);
+      //ctx_listen (ctx, CTX_ENTER, scrollbar_enter, vt, NULL);
+      //ctx_listen (ctx, CTX_LEAVE, scrollbar_leave, vt, NULL);
+      if (vt->scroll != 0 || scrollbar_focused)
+        ctx_rgba (ctx, 0.5, 0.5, 0.5, .25);
+      else
+        ctx_rgba (ctx, 0.5, 0.5, 0.5, .10);
+      ctx_fill (ctx);
+      ctx_round_rectangle (ctx, (vt->width) - vt->cw * 1.5,
+                           offset * vt->rows * vt->ch, (1.5-0.2) * vt->cw,
+                           win_len * vt->rows * vt->ch,
+                           vt->cw * 1.5 /2);
+      //ctx_listen (ctx, CTX_DRAG, scroll_handle_drag, vt, NULL);
+      if (vt->scroll != 0 || scrollbar_focused)
+        ctx_rgba (ctx, 1, 1, 1, .25);
+      else
+        ctx_rgba (ctx, 1, 1, 1, .10);
+      ctx_fill (ctx);
     }
-  return ctx_fragment_color_GRAYAF;
-}
 
-ctx_float_porter_duff(GRAYAF, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+    ctx_rectangle (ctx, 0, 0, vt->cols * vt->cw, vt->rows * vt->ch);
+    ctx_listen (ctx, CTX_DRAG,   vt_mouse_event, vt, NULL);
+    ctx_listen (ctx, CTX_MOTION, vt_mouse_event, vt, NULL);
+    ctx_begin_path (ctx);
 
-#if CTX_INLINED_NORMAL
-ctx_float_porter_duff(GRAYAF, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
+    ctx_restore (ctx);
 
-static void
-ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
+    if (vt->popped)
+    {
+       //ctx_set_popup (ctx, test_popup, vt);
+    }
 }
 
-static void
-ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+
+int vt_is_done (VT *vt)
 {
-  ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
+  return vt->vtpty.done;
 }
 
-static void
-ctx_GRAYAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
+int vt_get_result (VT *vt)
 {
-  ctx_float_source_over_normal_opaque_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
+  /* we could block - at least for a while, here..? */
+  return vt->result;
 }
-#endif
 
-static void
-ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
+void vt_set_scrollback_lines (VT *vt, int scrollback_lines)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 2;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer);
-  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_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;
-    }
-  else
-  {
-    rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
-  }
-
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_GRAYAF_clear_normal;
-  else
-    switch (gstate->blend_mode)
-    {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_GRAYAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-        else
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-#if 0
-              else if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = ctx_GRAYAF_source_over_normal_opaque_color;
-#endif
-              else
-                rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
-//            rasterizer->fragment = NULL;
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
-//            rasterizer->fragment = NULL;
-            }
-            break;
-          default:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal;
-            break;
-        }
-        break;
-      default:
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
-//          rasterizer->fragment = NULL;
-            break;
-          default:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
-            break;
-        }
-        break;
-    }
-#endif
+  vt->scrollback_limit = scrollback_lines;
 }
 
-#endif
-#if CTX_ENABLE_GRAYF
-
-static void
-ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS)
+int  vt_get_scrollback_lines (VT *vt)
 {
-  float *dstf = (float*)dst;
-
-  float temp[count*2];
-  for (int i = 0; i < count; i++)
-  {
-    temp[i*2] = dstf[i];
-    temp[i*2+1] = 1.0f;
-  }
-  rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count);
-  for (int i = 0; i < count; i++)
-  {
-    dstf[i] = temp[i*2];
-  }
+  return vt->scrollback_limit;
 }
 
-#endif
-#if CTX_ENABLE_BGRA8
+void vt_set_scroll (VT *vt, int scroll)
+{
+  if (vt->scroll == scroll)
+    return;
+  vt->scroll = scroll;
+  if (vt->scroll > ctx_list_length (vt->scrollback) )
+    { vt->scroll = ctx_list_length (vt->scrollback); }
+  if (vt->scroll < 0)
+    { vt->scroll = 0; }
+}
 
-inline static void
-ctx_swap_red_green (uint8_t *rgba)
+int vt_get_scroll (VT *vt)
 {
-  uint32_t *buf  = (uint32_t *) rgba;
-  uint32_t  orig = *buf;
-  uint32_t  green_alpha = (orig & 0xff00ff00);
-  uint32_t  red_blue    = (orig & 0x00ff00ff);
-  uint32_t  red         = red_blue << 16;
-  uint32_t  blue        = red_blue >> 16;
-  *buf = green_alpha | red | blue;
+  return vt->scroll;
 }
 
-static void
-ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+char *
+vt_get_selection (VT *vt)
 {
-  uint32_t *srci = (uint32_t *) buf;
-  uint32_t *dsti = (uint32_t *) rgba;
-  while (count--)
+  CtxString *str = ctx_string_new ("");
+  char *ret;
+  for (int row = vt->select_start_row; row <= vt->select_end_row; row++)
     {
-      uint32_t val = *srci++;
-      ctx_swap_red_green ( (uint8_t *) &val);
-      *dsti++      = val;
+      const char *line_str = vt_get_line (vt, vt->rows - row);
+      int col = 1;
+      for (const char *c = line_str; *c; c = mrg_utf8_skip (c, 1), col ++)
+        {
+          if (row == vt->select_end_row && col > vt->select_end_col)
+            { continue; }
+          if (row == vt->select_start_row && col < vt->select_start_col)
+            { continue; }
+          ctx_string_append_utf8char (str, c);
+        }
+      if (row < vt->select_end_row && !vt_line_is_continuation (vt, vt->rows-row-1))
+      {
+        _ctx_string_append_byte (str, '\n');
+      }
     }
+  ret = str->str;
+  ctx_string_free (str, 0);
+  return ret;
 }
 
-static void
-ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int vt_get_local (VT *vt)
 {
-  ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count);
+  return vt->local_editing;
 }
 
-static void
-ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS)
+void vt_set_local (VT *vt, int local)
 {
-  // for better performance, this could be done without a pre/post conversion,
-  // by swapping R and B of source instead... as long as it is a color instead
-  // of gradient or image
-  //
-  //
-  uint8_t pixels[count * 4];
-  ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_BGRA8_to_RGBA8  (rasterizer, x0, &pixels[0], dst, count);
+  vt->local_editing = local;
 }
 
+static unsigned long prev_press_time = 0;
+static int short_count = 0;
 
-#endif
-#if CTX_ENABLE_CMYKAF
 
-static void
-ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+void terminal_set_primary (const char *text)
 {
-  float *cmyka = (float*)out;
-  float _rgba[4 * count];
-  float *rgba = &_rgba[0];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
-    {
-      case CTX_SOURCE_TEXTURE:
-        ctx_fragment_image_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
-        break;
-      case CTX_SOURCE_COLOR:
-        ctx_fragment_color_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
-        break;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT:
-        ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
-        break;
-      case CTX_SOURCE_RADIAL_GRADIENT:
-        ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
-        break;
-#endif
-      default:
-        rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f;
-        break;
-    }
-  for (int i = 0; i < count; i++)
-  {
-    cmyka[4]=rgba[3];
-    ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]);
-    cmyka += 5;
-    rgba += 4;
-  }
+  if (primary) free (primary);
+  primary = NULL;
+  if (text) primary = strdup (text);
 }
 
-static void
-ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+void terminal_long_tap (Ctx *ctx, VT *vt);
+static int long_tap_cb_id = 0;
+static int single_tap (Ctx *ctx, void *data)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  float *cmyka = (float*)out;
-  float cmyka_in[5];
-  ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in);
-  for (int i = 0; i < count; i++)
-  {
-    for (int c = 0; c < 4; c ++)
-    {
-      cmyka[c] = (1.0f - cmyka_in[c]);
-    }
-    cmyka[4] = cmyka_in[4];
-    cmyka += 5;
-  }
+#if 0 // XXX
+  VT *vt = data;
+  if (short_count == 0 && !vt->select_active)
+    terminal_long_tap (ctx, vt);
+#endif
+  return 0;
 }
 
-static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer)
+void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, int y, int px_x, int px_y)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
-    {
-      case CTX_SOURCE_COLOR:
-        return ctx_fragment_color_CMYKAF;
-    }
-  return ctx_fragment_other_CMYKAF;
-}
-
-ctx_float_porter_duff (CMYKAF, 5,color,           rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff (CMYKAF, 5,generic,         rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ char buf[64]="";
+ int button_state = 0;
+ vt->rev++;
+ ctx_ticks();
+ if ((! (vt->mouse | vt->mouse_all | vt->mouse_drag)) ||
+     (event && (event->state & CTX_MODIFIER_STATE_SHIFT)))
+   {
+     // regular mouse select, this is incomplete
+     // fully ignorant of scrollback for now
+     //
+     if (type == VT_MOUSE_PRESS)
+       {
+         vt->cursor_down = 1;
+         vt->select_begin_col = x;
+         vt->select_begin_row = y - (int)vt->scroll;
+         vt->select_start_col = x;
+         vt->select_start_row = y - (int)vt->scroll;
+         vt->select_end_col = x;
+         vt->select_end_row = y - (int)vt->scroll;
+         vt->select_active = 0;
+         if (long_tap_cb_id)
+           {
+             ctx_remove_idle (vt->root_ctx, long_tap_cb_id);
+             long_tap_cb_id = 0;
+           }
+         
+         if ((ctx_ticks () - prev_press_time) < 1000*300 &&
+             abs(px_x - vt->select_begin_x) + 
+             abs(px_y - vt->select_begin_y) < 8)
+         {
+           short_count++;
+           switch (short_count)
+           {
+             case 1:
+             {
+               /* extend selection until space, XXX  should handle utf8 instead of ascii here!  */
+
+               int hit_space = 0;
+           
+               while (vt->select_start_col > 1 && !hit_space)
+               {
+                 vt->select_start_col --;
+                 char *sel = vt_get_selection (vt);
+                 if (sel[0] == ' ' || sel[0] == '\0')
+                   hit_space = 1;
+                 free (sel);
+               }
+               if (hit_space)
+                 vt->select_start_col++;
+
+               hit_space = 0;
+               while ((hit_space == 0) &&
+                      (vt->select_end_col < vt->cols))
+               {
+                 vt->select_end_col ++;
+                 char *sel = vt_get_selection (vt);
+                 int len = strlen(sel);
+                 if (sel[len-1]==' ')
+                   hit_space = 1;
+                 free (sel);
+               }
+               if (hit_space)
+                 vt->select_end_col--;
+
+               vt->select_active = 1;
+
+               { char *sel = vt_get_selection (vt);
+                 if (sel)
+                 {
+                    terminal_set_primary (sel);
+                    free (sel);
+                 }
+               }
+               }
+               break;
+             case 2:
+               vt->select_start_col = 1;
+               vt->select_end_col = vt->cols;
+               vt->select_active = 1;
+               {
+                 char *sel = vt_get_selection (vt);
+                 if (sel){
+                    terminal_set_primary (sel);
+                    free (sel);
+                 }
+               }
+               break;
+             case 3:
+               short_count = 0;
+               vt->select_start_col = 
+               vt->select_end_col = vt->select_begin_col;
+               vt->select_active = 0;
+               terminal_set_primary ("");
+               break;
+           }
+         }
+         else
+         {
+           if (vt->root_ctx && short_count == 0)
+             long_tap_cb_id = ctx_add_timeout (vt->root_ctx, 1000, single_tap, vt);
+           short_count = 0;
+           //vt->select_start_col = 
+           //vt->select_end_col = vt->select_begin_col;
+         }
+         vt->select_begin_x = px_x;
+         vt->select_begin_y = px_y;
+         prev_press_time = ctx_ticks ();
+         vt->rev++;
+       }
+     else if (type == VT_MOUSE_RELEASE)
+       {
+         if (long_tap_cb_id)
+           {
+             ctx_remove_idle (vt->root_ctx, long_tap_cb_id);
+             long_tap_cb_id = 0;
+           }
+         vt->cursor_down = 0;
+       }
+     else if (type == VT_MOUSE_MOTION && vt->cursor_down)
+       {
+         int row = y - (int)vt->scroll;
+         int col = x;
+         if ((row > vt->select_begin_row) ||
+             ((row == vt->select_begin_row) && (col >= vt->select_begin_col)))
+         {
+           vt->select_start_col = vt->select_begin_col;
+           vt->select_start_row = vt->select_begin_row;
+           vt->select_end_col = col;
+           vt->select_end_row = row;
+         }
+         else
+         {
+           vt->select_start_col = col;
+           vt->select_start_row = row;
+           vt->select_end_col = vt->select_begin_col;
+           vt->select_end_row = vt->select_begin_row;
+         }
+         if (vt->select_end_row == vt->select_start_row &&
+             abs (vt->select_begin_x - px_x) < vt->cw/2)
+         {
+           vt->select_active = 0;
+         }
+         else
+         {
+           vt->select_active = 1;
+           char *selection = vt_get_selection (vt);
+           if (selection)
+           {
+             terminal_set_primary (selection);
+             free (selection);
+           }
+         }
 
-#if CTX_INLINED_NORMAL
+         if (y < 1)
+         {
+           vt->scroll += 1.0f;
+           if (vt->scroll > vt->scrollback_count)
+             vt->scroll = vt->scrollback_count;
+         }
+         else if (y > vt->rows)
+         {
+           vt->scroll -= 1.0f;
+           if (vt->scroll < 0)
+             vt->scroll = 0.0f;
+         }
 
-ctx_float_porter_duff (CMYKAF, 5,color_normal,            rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_float_porter_duff (CMYKAF, 5,generic_normal,          rasterizer->fragment, CTX_BLEND_NORMAL)
+         vt->rev++;
+       }
+     return;
+   }
+ if (type == VT_MOUSE_MOTION)
+   { button_state = 3; }
 
-static void
-ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count);
+ if (vt->unit_pixels && vt->mouse_decimal)
+   {
+     x = px_x;
+     y = px_y;
+   }
+ switch (type)
+   {
+     case VT_MOUSE_MOTION:
+       if (!vt->mouse_all)
+         return;
+       if (x==vt->lastx && y==vt->lasty)
+         return;
+       vt->lastx = x;
+       vt->lasty = y;
+   //  sprintf (buf, "\033[<35;%i;%iM", x, y);
+       break;
+     case VT_MOUSE_RELEASE:
+       if (vt->mouse_decimal == 0)
+         button_state = 3;
+       break;
+     case VT_MOUSE_PRESS:
+       button_state = 0;
+       break;
+     case VT_MOUSE_DRAG: // XXX not really used - remove
+       if (! (vt->mouse_all || vt->mouse_drag) )
+         return;
+       button_state = 32;
+       break;
+   }
+ // todo : mix in ctrl/meta state
+ if (vt->mouse_decimal)
+   {
+     sprintf (buf, "\033[<%i;%i;%i%c", button_state, x, y, type == VT_MOUSE_RELEASE?'m':'M');
+   }
+ else
+   { 
+     sprintf (buf, "\033[M%c%c%c", button_state + 32, x + 32, y + 32);
+   }
+ if (buf[0])
+   {
+     vt_write (vt, buf, strlen (buf) );
+     fflush (NULL);
+   }
 }
 
-static void
-ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+pid_t vt_get_pid (VT *vt)
 {
-  ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count);
+  return vt->vtpty.pid;
 }
 
-static void
-ctx_CMYKAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
+void vt_set_ctx (VT *vt, Ctx *ctx)
 {
-  ctx_float_source_over_normal_opaque_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count);
+  vt->root_ctx = ctx;
 }
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
 #endif
 
-static void
-ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 5;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer);
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-    {
-      rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
-      rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
- //     rasterizer->fragment = NULL;
-      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;
-    }
-  else
-  {
-    rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
-  }
-
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_CMYKAF_clear_normal;
-#if 0
-  else
-    switch (gstate->blend_mode)
-    {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_CMYKAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-        else
-        switch (gstate->source_fill.type)
+#if !__COSMOPOLITAN__
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <pty.h>
+#include <math.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#define VT_RECORD 0
+extern Ctx *ctx;
+#define flag_is_set(a, f) (((a) & (f))!=0)
+#define flag_set(a, f)    ((a) |= (f));
+#define flag_unset(a, f)  ((a) &= ~(f));
+
+//#define CTX_x            CTX_STRH('x',0,0,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_lower_bottom CTX_STRH('l','o','w','e','r','-','b','o','t','t','o','m',0,0)
+#define CTX_lower        CTX_STRH('l','o','w','e','r',0,0,0,0,0,0,0,0,0)
+#define CTX_raise        CTX_STRH('r','a','i','s','e',0,0,0,0,0,0,0,0,0)
+#define CTX_raise_top    CTX_STRH('r','a','i','s','e','-','t','o','p',0,0,0,0,0)
+#define CTX_terminate    CTX_STRH('t','e','r','m','i','n','a','t','e',0,0,0,0,0)
+#define CTX_maximize     CTX_STRH('m','a','x','i','m','i','z','e',0,0,0,0,0,0)
+#define CTX_unmaximize   CTX_STRH('u','n','m','a','x','i','m','i','z','e',0,0,0,0)
+//#define CTX_width        CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0)
+//#define CTX_title        CTX_STRH('t','i','t','l','e',0,0,0,0,0,0,0,0,0)
+#define CTX_title        15643372
+#define CTX_action       CTX_STRH('a','c','t','i','o','n',0,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)
+
+void terminal_update_title  (const char *title);
+int  ctx_renderer_is_sdl    (Ctx *ctx);
+int  ctx_renderer_is_fb     (Ctx *ctx);
+int  ctx_renderer_is_term   (Ctx *ctx);
+void ctx_sdl_set_fullscreen (Ctx *ctx, int val);
+int  ctx_sdl_get_fullscreen (Ctx *ctx);
+float ctx_target_fps = 25.0;
+static int ctx_fetched_bytes = 1;
+
+CtxClient *vt_get_client (VT *vt);
+
+CtxList *vts = NULL;
+
+void ctx_clients_signal_child (int signum)
+{
+  pid_t pid;
+  int   status;
+  if ( (pid = waitpid (-1, &status, WNOHANG) ) != -1)
+    {
+      if (pid)
         {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-#if 0
-              else if (((float*)rasterizer->color)[components-1] == 1.0f)
-                rasterizer->comp_op = ctx_CMYKAF_source_over_normal_opaque_color;
-              else
-                rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
-              rasterizer->fragment = NULL;
-#endif
-            }
-            else
+          for (CtxList *l = vts; l; l=l->next)
             {
-              rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
-   //         rasterizer->fragment = NULL;
+              VtPty *vt = l->data;
+              if (vt->pid == pid)
+                {
+                  vt->done = 1;
+                  //vt->result = status;
+                }
             }
-            break;
-          default:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal;
-            break;
-        }
-        break;
-      default:
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
-    //      rasterizer->fragment = NULL;
-            break;
-          default:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
-            break;
         }
-        break;
     }
-#endif
-#endif
 }
 
-#endif
-#if CTX_ENABLE_CMYKA8
 
-static void
-ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
-{
-  for (int i = 0; i < count; i ++)
-    {
-      for (int c = 0; c < 4; c ++)
-        { dst[c] = ctx_u8_to_float ( (255-src[c]) ); }
-      dst[4] = ctx_u8_to_float (src[4]);
-      for (int c = 0; c < 4; c++)
-        { dst[c] *= dst[4]; }
-      src += 5;
-      dst += 5;
-    }
-}
-static void
-ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+
+int vt_set_prop (VT *vt, uint32_t key_hash, const char *val)
 {
-  for (int i = 0; i < count; i ++)
-    {
-      int a = ctx_float_to_u8 (src[4]);
-      if (a != 0 && a != 255)
-      {
-        float recip = 1.0f/src[4];
-        for (int c = 0; c < 4; c++)
-        {
-          dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip);
-        }
-      }
-      else
+#if 1
+  switch (key_hash)
+  {
+    case CTX_title:  
+     {
+       CtxClient *client = vt_get_client (vt);
+       if (client)
+       {
+         if (client->title) free (client->title);
+         client->title = strdup (val);
+       }
+     }
+
+     break;
+  }
+#else
+  float fval = strtod (val, NULL);
+  CtxClient *client = ctx_client_by_id (ct->id);
+  uint32_t val_hash = ctx_strhash (val);
+  if (!client)
+    return 0;
+
+  if (key_hash == ctx_strhash("start_move"))
+  {
+    start_moving (client);
+    moving_client = 1;
+    return 0;
+  }
+
+// set "pcm-hz"       "8000"
+// set "pcm-bits"     "8"
+// set "pcm-encoding" "ulaw"
+// set "play-pcm"     "d41ata312313"
+// set "play-pcm-ref" "foo.wav"
+
+// get "free"
+// storage of blobs for referencing when drawing or for playback
+// set "foo.wav"      "\3\1\1\4\"
+// set "fnord.png"    "PNG12.4a312"
+
+  switch (key_hash)
+  {
+    case CTX_title:  ctx_client_set_title (ct->id, val); break;
+    case CTX_x:      client->x = fval; break;
+    case CTX_y:      client->y = fval; break;
+    case CTX_width:  ctx_client_resize (ct->id, fval, client->height); break;
+    case CTX_height: ctx_client_resize (ct->id, client->width, fval); break;
+    case CTX_action:
+      switch (val_hash)
       {
-        for (int c = 0; c < 4; c++)
-          dst[c] = 255 - ctx_float_to_u8 (src[c]);
+        case CTX_maximize:     ctx_client_maximize (client); break;
+        case CTX_unmaximize:   ctx_client_unmaximize (client); break;
+        case CTX_lower:        ctx_client_lower (client); break;
+        case CTX_lower_bottom: ctx_client_lower_bottom (client);  break;
+        case CTX_raise:        ctx_client_raise (client); break;
+        case CTX_raise_top:    ctx_client_raise_top (client); break;
       }
-      dst[4]=a;
-
-      src += 5;
-      dst += 5;
-    }
+      break;
+  }
+  ct->rev++;
+#endif
+  return 0;
 }
 
-static void
-ctx_composite_CMYKA8 (CTX_COMPOSITE_ARGUMENTS)
+static float _ctx_font_size = 10.0;
+
+CtxList *clients = NULL;
+CtxClient *active = NULL;
+CtxClient *active_tab = NULL;
+
+static CtxClient *ctx_client_by_id (int id);
+
+int ctx_client_resize (int id, int width, int height);
+void ctx_client_maximize (int id);
+
+CtxClient *vt_get_client (VT *vt)
 {
-  float pixels[count * 5];
-  ctx_CMYKA8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count);
+  for (CtxList *l = clients; l; l =l->next)
+  {
+    CtxClient *client = l->data;
+    if (client->vt == vt)
+            return client;
+  }
+  return NULL;
 }
 
+CtxClient *ctx_client_new (Ctx *ctx,
+                           const char *commandline,
+                           int x, int y, int width, int height,
+                           CtxClientFlags flags)
+{
+  static int global_id = 0;
+  float font_size = ctx_get_font_size (ctx);
+  CtxClient *client = calloc (sizeof (CtxClient), 1);
+  ctx_list_append (&clients, client);
+  client->id = global_id++;
+  client->x = x;
+  client->y = y;
+  client->flags = flags;
+  client->ctx = ctx;
+  client->width = width;
+  client->height = height;
+
+  if (ctx_renderer_is_term (ctx))
+  {
+    font_size = 3;
+  }
+
+      //fprintf (stderr, "client new:%f\n", font_size);
+#if CTX_THREADS
+  mtx_init (&client->mtx, mtx_plain);
 #endif
-#if CTX_ENABLE_CMYK8
+  float line_spacing = 2.0f;
+  client->vt = vt_new (commandline, width, height, font_size,line_spacing, client->id, (flags & 
ITK_CLIENT_CAN_LAUNCH)!=0);
+  vt_set_ctx (client->vt, ctx);
+  return client;
+}
 
-static void
-ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
+CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int width, int height, 
CtxClientFlags flags)
 {
-  for (int i = 0; i < count; i ++)
+  CtxString *string = ctx_string_new ("");
+  for (int i = 0; argv[i]; i++)
+  {
+    char space = ' ';
+    if (i > 0)
+      ctx_string_append_data (string, &space, 1);
+    for (int c = 0; argv[i][c]; c++)
     {
-      dst[0] = ctx_u8_to_float (255-src[0]);
-      dst[1] = ctx_u8_to_float (255-src[1]);
-      dst[2] = ctx_u8_to_float (255-src[2]);
-      dst[3] = ctx_u8_to_float (255-src[3]);
-      dst[4] = 1.0f;
-      src += 4;
-      dst += 5;
+       switch (argv[i][c])
+       {
+         case '"':ctx_string_append_str (string, "\\\"");break;
+         case '\'':ctx_string_append_str (string, "\\\'");break;
+         default:ctx_string_append_data (string, &argv[i][c], 1);break;
+       }
     }
+  }
+  CtxClient *ret = ctx_client_new (ctx, string->str, x, y, width, height, flags);
+  ctx_string_free (string, 1);
+  return ret;
 }
-static void
-ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+
+extern float ctx_shape_cache_rate;
+extern int _ctx_max_threads;
+
+static int focus_follows_mouse = 0;
+
+static CtxClient *find_active (int x, int y)
 {
-  for (int i = 0; i < count; i ++)
-    {
-      float c = src[0];
-      float m = src[1];
-      float y = src[2];
-      float k = src[3];
-      float a = src[4];
-      if (a != 0.0f && a != 1.0f)
-        {
-          float recip = 1.0f/a;
-          c *= recip;
-          m *= recip;
-          y *= recip;
-          k *= recip;
-        }
-      c = 1.0 - c;
-      m = 1.0 - m;
-      y = 1.0 - y;
-      k = 1.0 - k;
-      dst[0] = ctx_float_to_u8 (c);
-      dst[1] = ctx_float_to_u8 (m);
-      dst[2] = ctx_float_to_u8 (y);
-      dst[3] = ctx_float_to_u8 (k);
-      src += 5;
-      dst += 4;
-    }
+  CtxClient *ret = NULL;
+  float titlebar_height = _ctx_font_size;
+  int resize_border = titlebar_height/2;
+
+  for (CtxList *l = clients; l; l = l->next)
+  {
+     CtxClient *c = l->data;
+     if ((c->flags & ITK_CLIENT_MAXIMIZED) && c == active_tab)
+     if (x > c->x - resize_border && x < c->x+c->width + resize_border &&
+         y > c->y - titlebar_height && y < c->y+c->height + resize_border)
+     {
+       ret = c;
+     }
+  }
+
+  for (CtxList *l = clients; l; l = l->next)
+  {
+     CtxClient *c = l->data;
+     if (!(c->flags &  ITK_CLIENT_MAXIMIZED))
+     if (x > c->x - resize_border && x < c->x+c->width + resize_border &&
+         y > c->y - titlebar_height && y < c->y+c->height + resize_border)
+     {
+       ret = c;
+     }
+  }
+  return ret;
 }
 
-static void
-ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS)
+int id_to_no (int id)
 {
-  float pixels[count * 5];
-  ctx_CMYK8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], src, x0, coverage, count);
-  ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count);
+  CtxList *l;
+  int no = 0;
+
+  for (l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    if (client->id == id)
+      return no;
+    no++;
+  }
+  return -1;
 }
-#endif
 
-#if CTX_ENABLE_RGB8
+void ctx_client_move (int id, int x, int y);
+int ctx_client_resize (int id, int w, int h);
+void ctx_client_shade_toggle (int id);
+float ctx_client_min_y_pos (Ctx *ctx);
+float ctx_client_max_y_pos (Ctx *ctx);
 
-inline static void
-ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+#if 0
+void ensure_layout ()
 {
-  const uint8_t *pixel = (const uint8_t *) buf;
-  while (count--)
+  int n_clients = ctx_list_length (clients);
+  if (n_clients == 1)
+  {
+    CtxClient *client = clients->data;
+    if (client->flags & ITK_CLIENT_MAXIMIZED)
     {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[1];
-      rgba[2] = pixel[2];
-      rgba[3] = 255;
-      pixel+=3;
-      rgba +=4;
+      ctx_client_move (client->id, 0, 0);
+      ctx_client_resize (client->id, ctx_width (ctx), ctx_height(ctx));
+      if (active_tab == NULL)
+        active_tab = client;
     }
-}
-
-inline static void
-ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  }
+  else
+  for (CtxList *l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    if (client->flags & ITK_CLIENT_MAXIMIZED)
     {
-      pixel[0] = rgba[0];
-      pixel[1] = rgba[1];
-      pixel[2] = rgba[2];
-      pixel+=3;
-      rgba +=4;
+      ctx_client_move   (client->id, 0, client_min_y_pos (ctx));
+      ctx_client_resize (client->id, ctx_width (ctx), ctx_height(ctx) -
+                      ctx_client_min_y_pos (ctx) / 2);   // /2 to counter the double titlebar of 
non-maximized
+      if (active_tab == NULL)
+        active_tab = client;
     }
+  }
 }
-
 #endif
-#if CTX_ENABLE_GRAY1
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+static CtxClient *ctx_client_by_id (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      if (*pixel & (1<< (x&7) ) )
-        {
-          rgba[0] = 255;
-          rgba[1] = 255;
-        }
-      else
-        {
-          rgba[0] = 0;
-          rgba[1] = 255;
-        }
-      if ( (x&7) ==7)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+  for (CtxList *l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    if (client->id == id)
+      return client;
+  }
+  return NULL;
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+void ctx_client_remove (Ctx *ctx, CtxClient *client)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int gray = rgba[0];
-      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
-      if (gray < 127)
-        {
-          *pixel = *pixel & (~ (1<< (x&7) ) );
-        }
-      else
-        {
-          *pixel = *pixel | (1<< (x&7) );
-        }
-      if ( (x&7) ==7)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+  ctx_client_lock (client);
+  if (!client->internal)
+  {
+
+    if (client->vt)
+      vt_destroy (client->vt);
+  }
+
+  if (client->title)
+    free (client->title);
+
+#if VT_RECORD
+  if (client->recording)
+    ctx_free (client->recording);
+#endif
+
+  ctx_list_remove (&clients, client);
+
+  if (client == active_tab)
+  {
+    active_tab = NULL;
+  }
+
+  if (ctx)
+  if (client == active)
+  {
+    active = find_active (ctx_pointer_x (ctx), ctx_pointer_y (ctx));
+    if (!active) active = clients?clients->data:NULL;
+  }
+
+  ctx_client_unlock (client);
+  free (client);
+  //ensure_layout();
 }
 
-#else
+#if 0
+void ctx_client_remove_by_id (int id)
+{
+  int no = id_to_no (id);
+  if (no>=0)
+    ctx_client_remove (no);
+}
+#endif
 
-inline static void
-ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+int ctx_client_height (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      if (*pixel & (1<< (x&7) ) )
-        {
-          rgba[0] = 255;
-          rgba[1] = 255;
-          rgba[2] = 255;
-          rgba[3] = 255;
-        }
-      else
-        {
-          rgba[0] = 0;
-          rgba[1] = 0;
-          rgba[2] = 0;
-          rgba[3] = 255;
-        }
-      if ( (x&7) ==7)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+  CtxClient *client = ctx_client_by_id (id);
+  if (!client) return 0;
+  return client->height;
 }
 
-inline static void
-ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_client_x (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
-      if (gray < 127)
-        {
-          *pixel = *pixel & (~ (1<< (x&7) ) );
-        }
-      else
-        {
-          *pixel = *pixel | (1<< (x&7) );
-        }
-      if ( (x&7) ==7)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+  CtxClient *client = ctx_client_by_id (id);
+  if (!client) return 0;
+  return client->x;
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAY2
+int ctx_client_y (int id)
+{
+  CtxClient *client = ctx_client_by_id (id);
+  if (!client) return 0;
+  return client->y;
+}
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+void ctx_client_raise_top (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
-      val <<= 6;
-      rgba[0] = val;
-      rgba[1] = 255;
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+  CtxClient *client = ctx_client_by_id (id);
+  if (!client) return;
+  ctx_list_remove (&clients, client);
+  ctx_list_append (&clients, client);
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+void ctx_client_lower_bottom (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = rgba[0];
-      val >>= 6;
-      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
-      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+  CtxClient *client = ctx_client_by_id (id);
+  if (!client) return;
+  ctx_list_remove (&clients, client);
+  ctx_list_prepend (&clients, client);
 }
-#else
 
-inline static void
-ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+
+void ctx_client_iconify (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
-      val <<= 6;
-      rgba[0] = val;
-      rgba[1] = val;
-      rgba[2] = val;
-      rgba[3] = 255;
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   client->flags |= ITK_CLIENT_ICONIFIED;
 }
 
-inline static void
-ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_client_is_iconified (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      val >>= 6;
-      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
-      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return -1;
+   return (client->flags & ITK_CLIENT_ICONIFIED) != 0;
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAY4
+void ctx_client_uniconify (int id)
+{
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   client->flags &= ~ITK_CLIENT_ICONIFIED;
+}
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+void ctx_client_maximize (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
-      val <<= 4;
-      rgba[0] = val;
-      rgba[1] = 255;
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   if (client->flags &  ITK_CLIENT_MAXIMIZED)
+     return;
+   client->flags |= ITK_CLIENT_MAXIMIZED;
+   client->unmaximized_x = client->x;
+   client->unmaximized_y = client->y;
+   client->unmaximized_width  = client->width;
+   client->unmaximized_height = client->height;
+
+   // enforce_layout does the size
+   //client_resize (id, ctx_width (ctx), ctx_height(ctx) - ctx_client_min_y_pos (ctx));
+   
+   ctx_client_move (id, 0, ctx_client_min_y_pos (client->ctx));
+   active_tab = client;
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_client_is_maximized (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = rgba[0];
-      val >>= 4;
-      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
-      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return -1;
+   return (client->flags & ITK_CLIENT_MAXIMIZED) != 0;
 }
-#else
-inline static void
-ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+
+void ctx_client_unmaximize (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
-      val <<= 4;
-      rgba[0] = val;
-      rgba[1] = val;
-      rgba[2] = val;
-      rgba[3] = 255;
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   if ((client->flags & ITK_CLIENT_MAXIMIZED) == 0)
+     return;
+   client->flags &= ~ITK_CLIENT_MAXIMIZED;
+   ctx_client_resize (id, client->unmaximized_width, client->unmaximized_height);
+   ctx_client_move (id, client->unmaximized_x, client->unmaximized_y);
+   active_tab = NULL;
 }
 
-inline static void
-ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+void ctx_client_maximized_toggle (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      val >>= 4;
-      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
-      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+  if (ctx_client_is_maximized (id))
+    ctx_client_unmaximize (id);
+  else
+    ctx_client_maximize (id);
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAY8
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+void ctx_client_shade (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = 255;
-      pixel+=1;
-      rgba +=2;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   client->flags |= ITK_CLIENT_SHADED;
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_client_is_shaded (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      pixel[0] = rgba[0];
-      pixel+=1;
-      rgba +=2;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return -1;
+   return (client->flags & ITK_CLIENT_SHADED) != 0;
 }
-#else
-inline static void
-ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+
+void ctx_client_unshade (int id)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[0];
-      rgba[2] = pixel[0];
-      rgba[3] = 255;
-      pixel+=1;
-      rgba +=4;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   client->flags &= ~ITK_CLIENT_SHADED;
 }
 
-inline static void
-ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+void ctx_client_toggle_maximized (int id)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  for (int i = 0; i < count; i ++)
-    {
-      pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4);
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   if (ctx_client_is_maximized (id))
+     ctx_client_unmaximize (id);
+   else
+     ctx_client_maximize (id);
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAYA8
+void ctx_client_shade_toggle (int id)
+{
+   CtxClient *client = ctx_client_by_id (id);
+   if (!client) return;
+   if (ctx_client_is_shaded (id))
+    ctx_client_shade (id);
+   else
+    ctx_client_unshade (id);
+}
 
-inline static void
-ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+void ctx_client_move (int id, int x, int y)
 {
-  const uint8_t *pixel = (const uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[0];
-      rgba[2] = pixel[0];
-      rgba[3] = pixel[1];
-      pixel+=2;
-      rgba +=4;
-    }
+   CtxClient *client = ctx_client_by_id (id);
+   if (client && (client->x != x || client->y != y))
+   {
+     client->x = x;
+     client->y = y;
+     vt_rev_inc (client->vt);
+   }
 }
 
-inline static void
-ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_client_resize (int id, int width, int height)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+   CtxClient *client = ctx_client_by_id (id);
+
+   if (client && ((height != client->height) || (width != client->width) ))
+   {
+     client->width  = width;
+     client->height = height;
+     if (client->vt)
+       vt_set_px_size (client->vt, width, height);
+     return 1;
+   }
+   return 0;
+}
+
+static void ctx_client_titlebar_drag (CtxEvent *event, void *data, void *data2)
+{
+  CtxClient *client = data;
+
+  if (event->type == CTX_DRAG_RELEASE)
+  {
+    static int prev_drag_end_time = 0;
+    if (event->time - prev_drag_end_time < 500)
     {
-      pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      pixel[1] = rgba[3];
-      pixel+=2;
-      rgba +=4;
+      //client_shade_toggle (client->id);
+      ctx_client_maximized_toggle (client->id);
     }
+    prev_drag_end_time = event->time;
+  }
+
+  float new_x = client->x +  event->delta_x;
+  float new_y = client->y +  event->delta_y;
+
+  float snap_threshold = 8;
+
+  if (ctx_renderer_is_term (event->ctx))
+     snap_threshold = 1;
+
+  if (new_y < ctx_client_min_y_pos (event->ctx)) new_y = ctx_client_min_y_pos (event->ctx);
+  if (new_y > ctx_client_max_y_pos (event->ctx)) new_y = ctx_client_max_y_pos (event->ctx);
+
+  if (fabs (new_x - 0) < snap_threshold) new_x = 0.0;
+  if (fabs (ctx_width (event->ctx) - (new_x + client->width)) < snap_threshold) new_x = ctx_width 
(event->ctx) - client->width;
+
+  ctx_client_move (client->id, new_x, new_y);
+
+  //vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+
+  event->stop_propagate = 1;
 }
 
-#if CTX_NATIVE_GRAYA8
-CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out)
+static float min_win_dim = 32;
+
+static void ctx_client_resize_se (CtxEvent *event, void *data, void *data2)
 {
-  out[0] = ctx_u8_color_rgb_to_gray (state, in);
-  out[1] = in[3];
+  CtxClient *client = data;
+  int new_w = client->width + event->delta_x;
+  int new_h = client->height + event->delta_y;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  ctx_client_resize (client->id, new_w, new_h);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
 
-#if CTX_GRADIENTS
-static void
-ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+static void ctx_client_resize_e (CtxEvent *event, void *data, void *data2)
 {
-  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);
-  ctx_fragment_gradient_1d_GRAYA8 (rasterizer, v, 1.0, (uint8_t*)out);
-#if CTX_DITHER
-  ctx_dither_graya_u8 ((uint8_t*)out, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
+  CtxClient *client = data;
+  int new_w = client->width + event->delta_x;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  ctx_client_resize (client->id, new_w, client->height);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
 
-#if 0
-static void
-ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
+static void ctx_client_resize_s (CtxEvent *event, void *data, void *data2)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  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);
-#if CTX_DITHER
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
+  CtxClient *client = data;
+  int new_h = client->height + event->delta_y;
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  ctx_client_resize (client->id, client->width, new_h);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
-#endif
 
+static void ctx_client_resize_n (CtxEvent *event, void *data, void *data2)
+{
+  CtxClient *client = data;
+  float new_y = client->y +  event->delta_y;
+  int new_h = client->height - event->delta_y;
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  ctx_client_resize (client->id, client->width, new_h);
+  ctx_client_move (client->id, client->x, new_y);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
+}
 
-static void
-ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+static void ctx_client_resize_ne (CtxEvent *event, void *data, void *data2)
 {
-  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);
-#if CTX_DITHER
-  ctx_dither_graya_u8 ((uint8_t*)out, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
+  CtxClient *client = data;
+  float new_y = client->y +  event->delta_y;
+  int new_h = client->height - event->delta_y;
+  int new_w = client->width + event->delta_x;
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  ctx_client_resize (client->id, new_w, new_h);
+  ctx_client_move (client->id, client->x, new_y);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
-#endif
 
-static void
-ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+static void ctx_client_resize_sw (CtxEvent *event, void *data, void *data2)
 {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  ctx_color_get_graya_u8 (rasterizer->state, &g->color, out);
+  CtxClient *client = data;
+
+  float new_x = client->x +  event->delta_x;
+  int new_w = client->width - event->delta_x;
+  int new_h = client->height + event->delta_y;
+
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  ctx_client_resize (client->id, new_w, new_h);
+  ctx_client_move (client->id, new_x, client->y);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
 
-static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+static void ctx_client_resize_nw (CtxEvent *event, void *data, void *data2)
 {
-  uint8_t rgba[4];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
-    {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
-    }
-  ctx_rgba_to_graya_u8 (rasterizer->state, rgba, (uint8_t*)out);
+  CtxClient *client = data;
+  float new_x = client->x +  event->delta_x;
+  float new_y = client->y +  event->delta_y;
+  int new_w = client->width - event->delta_x;
+  int new_h = client->height - event->delta_y;
+  if (new_h <= min_win_dim) new_h = min_win_dim;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  ctx_client_resize (client->id, new_w, new_h);
+  ctx_client_move (client->id, new_x, new_y);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
+}
+
+static void ctx_client_resize_w (CtxEvent *event, void *data, void *data2)
+{
+  CtxClient *client = data;
+
+  float new_x = client->x +  event->delta_x;
+  int new_w = client->width - event->delta_x;
+  if (new_w <= min_win_dim) new_w = min_win_dim;
+  ctx_client_resize (client->id, new_w, client->height);
+  ctx_client_move (client->id, new_x, client->y);
+  if (client->vt) // force redraw
+    vt_rev_inc (client->vt);
+  ctx_set_dirty (event->ctx, 1);
+
+  event->stop_propagate = 1;
+}
+
+static void ctx_client_close (CtxEvent *event, void *data, void *data2)
+{
+  //Ctx *ctx = event->ctx;
+  CtxClient *client = data;
+
+ // client->do_quit = 1;
+  
+  ctx_client_remove (event->ctx, client);
+
+  ctx_set_dirty (event->ctx, 1);
+  event->stop_propagate = 1;
 }
 
-static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer)
+/********************/
+void vt_use_images (VT *vt, Ctx *ctx);
+float _ctx_green = 0.5;
+
+static void ctx_client_draw (Ctx *ctx, CtxClient *client, float x, float y)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+    if (client->internal)
     {
-      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;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8;
+#if 0
+      ctx_save (ctx);
+
+      ctx_translate (ctx, x, y);
+      int width = client->width;
+      int height = client->height;
+
+      itk_panel_start (itk, "", 0, 0, width, height);
+      //itk_seperator (itk);
+#if 0
+      if (itk_button (itk, "add tab"))
+      {
+        add_tab (vt_find_shell_command(), 1);
+      }
+#endif
+      //itk_sameline (itk);
+      //itk_toggle (itk, "on screen keyboard", &on_screen_keyboard);
+      //itk_toggle (itk, "focus follows mouse", &focus_follows_mouse);
+      itk_slider_float (itk, "CTX_GREEN", &_ctx_green, 0.0, 1.0, 0.5);
+      itk_ctx_settings (itk);
+      itk_itk_settings (itk);
+
+      itk_panel_end (itk);
+      itk_done (itk);
+      //itk_key_bindings (itk);
+
+      ctx_restore (ctx);
 #endif
     }
-  return ctx_fragment_color_GRAYA8;
-}
+    else
+    {
+       ctx_client_lock (client);
 
-ctx_u8_porter_duff(GRAYA8, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+          int found = 0;
+          for (CtxList *l2 = clients; l2; l2 = l2->next)
+            if (l2->data == client) found = 1;
+          if (found)
+          {
 
-#if CTX_INLINED_NORMAL
+      int rev = vt_rev (client->vt);
+#if VT_RECORD
+      if (client->drawn_rev != rev)
+      {
+        if (!client->recording)
+          client->recording = ctx_new ();
+        else
+          ctx_reset (client->recording);
+        vt_draw (client->vt, client->recording, 0.0, 0.0);
+      }
 
-ctx_u8_porter_duff(GRAYA8, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
+      if (client->recording)
+      {
+        ctx_save (ctx);
+        ctx_translate (ctx, x, y);
+        ctx_render_ctx (client->recording, ctx);
+        vt_register_events (client->vt, ctx, 0.0, 0.0);
+        ctx_restore (ctx);
+      }
+#else
 
-static void
-ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
+      vt_draw (client->vt, ctx, x, y);
+      vt_register_events (client->vt, ctx, x, y);
+#endif
+      client->drawn_rev = rev;
+      ctx_client_unlock (client);
+          }
+    }
 }
 
-static void
-ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+static void ctx_client_use_images (Ctx *ctx, CtxClient *client)
 {
-  ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
-}
+  if (!client->internal)
+  {
+      uint32_t rev = vt_rev (client->vt);
+#if VT_RECORD
+      if (client->drawn_rev != rev)
+      {
+        if (!client->recording)
+          client->recording = ctx_new ();
+        else
+          ctx_reset (client->recording);
+        vt_draw (client->vt, client->recording, 0.0, 0.0);
+      }
 
-static void
-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);
+      if (client->recording)
+      {
+        ctx_save (ctx);
+        ctx_render_ctx_textures (client->recording, ctx);
+        ctx_restore (ctx);
+      }
+#else
+    vt_use_images (client->vt, ctx);
+#endif
+    client->drawn_rev = rev;
+  }
 }
 
-static void
-CTX_COMPOSITE_SUFFIX (ctx_GRAYA8_source_over_normal_opaque_color) (CTX_COMPOSITE_ARGUMENTS)
+void ctx_client_lock (CtxClient *client)
 {
-  ctx_u8_source_over_normal_opaque_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
-}
+#if CTX_THREADS
+    mtx_lock (&client->mtx);
 #endif
+}
 
-inline static int
-ctx_is_opaque_color (CtxRasterizer *rasterizer)
+void ctx_client_unlock (CtxClient *client)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  if (gstate->global_alpha_u8 != 255)
-    return 0;
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-  {
-    uint8_t ga[2];
-    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
-    return ga[1] == 255;
-  }
-  return 0;
+#if CTX_THREADS
+    mtx_unlock (&client->mtx);
+#endif
 }
 
-static void
-ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
+#if 0
+void ctx_client_handle_event (Ctx *ctx, CtxEvent *ctx_event, const char *event)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 2;
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  if (!active)
+    return;
+  if (active->internal)
+    return;
+  VT *vt = active->vt;
+  CtxClient *client = vt_get_client (vt);
+
+  ctx_client_lock (client);
+
+  if (!strcmp (event, "F11"))
+  {
+#ifndef NO_SDL
+    if (ctx_renderer_is_sdl (ctx))
     {
-      rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
-      rasterizer->fragment = NULL;
-      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;
-      rasterizer->color[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rasterizer->color);
-      rasterizer->color[1] = rasterizer->color[3];
+      ctx_sdl_set_fullscreen (ctx, !ctx_sdl_get_fullscreen (ctx));
     }
-  else
+#endif
+  }
+  else if (!strcmp (event, "shift-return"))
   {
-    rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer);
-    rasterizer->comp_op  = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
+    vt_feed_keystring (vt, ctx_event, "return");
   }
-
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_GRAYA8_clear_normal;
-  else
-    switch (gstate->blend_mode)
+  else if (!strcmp (event, "shift-control-v") )
     {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+      char *text = ctx_get_clipboard (ctx);
+      if (text)
         {
-          rasterizer->comp_op = ctx_GRAYA8_copy_normal;
+          if (vt)
+            vt_paste (vt, text);
+          free (text);
         }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
-        else
-        switch (gstate->source_fill.type)
+    }
+  else if (!strcmp (event, "shift-control-c") && vt)
+    {
+      char *text = vt_get_selection (vt);
+      if (text)
         {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              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_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_opaque_color);
-              else
-                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_color);
-              rasterizer->fragment = NULL;
-            }
-            else
-            {
-              rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color_normal);
-              rasterizer->fragment = NULL;
-            }
-            break;
-          default:
-            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic_normal);
-            break;
+          ctx_set_clipboard (ctx, text);
+          free (text);
         }
-        break;
-      default:
-        switch (gstate->source_fill.type)
+    }
+  else if (!strcmp (event, "shift-control-t") ||
+           ((ctx_renderer_is_fb (ctx) || ctx_renderer_is_term (ctx))
+           &&   !strcmp (event, "control-t") ))
+  {
+    //XXX add_tab (vt_find_shell_command(), 1);
+  }
+  else if (!strcmp (event, "shift-control-n") )
+    {
+      pid_t pid;
+      if ( (pid=fork() ) ==0)
         {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
-            rasterizer->fragment = NULL;
-            break;
-          default:
-            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
-            break;
+          unsetenv ("CTX_VERSION");
+         // execlp (execute_self, execute_self, NULL);
+          exit (0);
         }
-        break;
     }
+
+#if 0
+  else if (!strcmp (event, "alt-1"))   switch_to_tab(0);
+  else if (!strcmp (event, "alt-2"))   switch_to_tab(1);
+  else if (!strcmp (event, "alt-3"))   switch_to_tab(2);
+  else if (!strcmp (event, "alt-4"))   switch_to_tab(3);
+  else if (!strcmp (event, "alt-5"))   switch_to_tab(4);
+  else if (!strcmp (event, "alt-6"))   switch_to_tab(5);
+  else if (!strcmp (event, "alt-7"))   switch_to_tab(6);
+  else if (!strcmp (event, "alt-8"))   switch_to_tab(7);
+  else if (!strcmp (event, "alt-9"))   switch_to_tab(8);
+  else if (!strcmp (event, "alt-0"))   switch_to_tab(9);
 #endif
+  else if (!strcmp (event, "shift-control-q") )
+    {
+      ctx_quit (ctx);
+    }
+  else if (!strcmp (event, "shift-control-w") )
+    {
+      active->do_quit = 1;
+    }
+  else if (!strcmp (event, "shift-control-s") )
+    {
+      if (vt)
+      {
+        char *sel = vt_get_selection (vt);
+        if (sel)
+        {
+          vt_feed_keystring (vt, ctx_event, sel);
+          free (sel);
+        }
+      }
+    }
+  else
+    {
+      if (vt)
+        vt_feed_keystring (vt, ctx_event, event);
+    }
+  ctx_client_unlock (client);
 }
 #endif
 
-#endif
-#if CTX_ENABLE_RGB332
-
-inline static void
-ctx_332_unpack (uint8_t pixel,
-                uint8_t *red,
-                uint8_t *green,
-                uint8_t *blue)
+static int ctx_clients_dirty_count (void)
 {
-  *blue   = (pixel & 3) <<6;
-  *green = ( (pixel >> 2) & 7) <<5;
-  *red   = ( (pixel >> 5) & 7) <<5;
-  if (*blue > 223)  { *blue  = 255; }
-  if (*green > 223) { *green = 255; }
-  if (*red > 223)   { *red   = 255; }
+  int changes = 0;
+  for (CtxList *l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    if ((client->drawn_rev != vt_rev (client->vt) ) ||
+        vt_has_blink (client->vt))
+      changes++;
+  }
+  return changes;
 }
 
-static inline uint8_t
-ctx_332_pack (uint8_t red,
-              uint8_t green,
-              uint8_t blue)
+static void ctx_client_titlebar_drag_maximized (CtxEvent *event, void *data, void *data2)
 {
-  uint8_t c  = (red >> 5) << 5;
-  c |= (green >> 5) << 2;
-  c |= (blue >> 6);
-  return c;
-}
+  CtxClient *client = data;
 
-static inline void
-ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  active = active_tab = client;
+  if (event->type == CTX_DRAG_RELEASE)
+  {
+    static int prev_drag_end_time = 0;
+    if (event->time - prev_drag_end_time < 500)
     {
-      ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]);
-#if CTX_RGB332_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
-      else
-#endif
-        { rgba[3] = 255; }
-      pixel+=1;
-      rgba +=4;
+      //client_shade_toggle (client->id);
+      ctx_client_unmaximize (client->id);
+      ctx_client_raise_top (client->id);
+      active_tab = NULL;
     }
+    prev_drag_end_time = event->time;
+  }
+  ctx_set_dirty (event->ctx, 1);
+  vt_rev_inc (client->vt);
+  event->stop_propagate = 1;
 }
 
-static inline void
-ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+float ctx_client_min_y_pos (Ctx *ctx)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-#if CTX_RGB332_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_332_pack (255, 0, 255); }
-      else
-#endif
-        { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); }
-      pixel+=1;
-      rgba +=4;
-    }
+  return _ctx_font_size * 2; // a titlebar and a panel
 }
 
-#endif
-#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED
+float ctx_client_max_y_pos (Ctx *ctx)
+{
+  return ctx_height (ctx);
+}
 
-static inline void
-ctx_565_unpack (uint16_t pixel,
-                uint8_t *red,
-                uint8_t *green,
-                uint8_t *blue,
-                int      byteswap)
+void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client,
+                               float x, float y, float width, float titlebar_height)
 {
-  uint16_t byteswapped;
-  if (byteswap)
-    { byteswapped = (pixel>>8) | (pixel<<8); }
+#if 0
+  ctx_move_to (ctx, x, y + height * 0.8);
+  if (client == active)
+    ctx_rgba (ctx, 1, 1,0.4, 1.0);
   else
-    { byteswapped  = pixel; }
-  *blue   = (byteswapped & 31) <<3;
-  *green = ( (byteswapped>>5) & 63) <<2;
-  *red   = ( (byteswapped>>11) & 31) <<3;
-  if (*blue > 248) { *blue = 255; }
-  if (*green > 248) { *green = 255; }
-  if (*red > 248) { *red = 255; }
+    ctx_rgba (ctx, 1, 1,1, 0.8);
+  ctx_text (ctx, client->title);
+#else
+  ctx_rectangle (ctx, x, y - titlebar_height,
+                 width, titlebar_height);
+  if (client == active)
+     itk_style_color (ctx, "titlebar-focused-bg");
+  else
+     itk_style_color (ctx, "titlebar-bg");
+
+  if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y == titlebar_height)
+  {
+    ctx_listen (ctx, CTX_DRAG, ctx_client_titlebar_drag_maximized, client, NULL);
+    ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_ALL);
+  }
+  else
+  {
+    ctx_listen (ctx, CTX_DRAG, ctx_client_titlebar_drag, client, NULL);
+    ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_ALL);
+  }
+  ctx_fill (ctx);
+  //ctx_font_size (ctx, itk->font_size);//titlebar_height);// * 0.85);
+
+  if (client == active &&
+      (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y != titlebar_height))
+#if 1
+  ctx_rectangle (ctx, x + width - titlebar_height,
+                  y - titlebar_height, titlebar_height,
+                  titlebar_height);
+#endif
+  ctx_rgb (ctx, 1, 0,0);
+  ctx_listen (ctx, CTX_PRESS, ctx_client_close, client, NULL);
+  ctx_listen_set_cursor (ctx, CTX_CURSOR_ARROW);
+  //ctx_fill (ctx);
+  ctx_begin_path (ctx);
+  ctx_move_to (ctx, x + width - titlebar_height * 0.8, y - titlebar_height * 0.22);
+  if (client == active)
+    itk_style_color (ctx, "titlebar-focused-close");
+  else
+    itk_style_color (ctx, "titlebar-close");
+  ctx_text (ctx, "X");
+
+  ctx_move_to (ctx, x +  width/2, y - titlebar_height * 0.22);
+  if (client == active)
+    itk_style_color (ctx, "titlebar-focused-fg");
+  else
+    itk_style_color (ctx, "titlebar-fg");
+
+  ctx_save (ctx);
+  ctx_text_align (ctx, CTX_TEXT_ALIGN_CENTER);
+  if (client->title)
+    ctx_text (ctx, client->title);
+  else
+    ctx_text (ctx, "untitled");
+  ctx_restore (ctx);
+#endif
 }
 
-static inline uint16_t
-ctx_565_pack (uint8_t red,
-              uint8_t green,
-              uint8_t blue,
-              int     byteswap)
+#if 0
+static void key_down (CtxEvent *event, void *data1, void *data2)
 {
-  uint32_t c = (red >> 3) << 11;
-  c |= (green >> 2) << 5;
-  c |= blue >> 3;
-  if (byteswap)
-    { return (c>>8) | (c<<8); } /* swap bytes */
-  return c;
+  fprintf (stderr, "down %i %s\n", event->unicode, event->string);
 }
-
-static inline uint16_t
-ctx_888_to_565 (uint32_t in, int byteswap)
+static void key_up (CtxEvent *event, void *data1, void *data2)
 {
-  uint8_t *rgb=(uint8_t*)(&in);
-  return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap);
+  fprintf (stderr, "up %i %s\n", event->unicode, event->string);
 }
-
-static inline uint32_t
-ctx_565_to_888 (uint16_t in, int byteswap)
+static void key_press (CtxEvent *event, void *data1, void *data2)
 {
-  uint32_t ret = 0;
-  uint8_t *rgba=(uint8_t*)&ret;
-  ctx_565_unpack (in,
-                  &rgba[0],
-                  &rgba[1],
-                  &rgba[2],
-                  byteswap);
-  return ret;
+  fprintf (stderr, "press %i %s\n", event->unicode, event->string);
 }
-
 #endif
-#if CTX_ENABLE_RGB565
 
-
-static inline void
-ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+int ctx_clients_draw (Ctx *ctx)
 {
-  const uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
+  _ctx_font_size = ctx_get_font_size (ctx);
+  float titlebar_height = _ctx_font_size;
+  int n_clients         = ctx_list_length (clients);
+
+  if (active && flag_is_set(active->flags, ITK_CLIENT_MAXIMIZED) && n_clients == 1)
+  {
+    ctx_client_draw (ctx, active, 0, 0);
+    return 0;
+  }
+
+  for (CtxList *l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED))
     {
-      ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 0);
-#if CTX_RGB565_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
+      if (client == active_tab)
+      {
+        ctx_client_draw (ctx, client, 0, titlebar_height);
+      }
       else
-#endif
-        { rgba[3] = 255; }
-      pixel+=1;
-      rgba +=4;
+      {
+        ctx_client_use_images (ctx, client);
+      }
     }
-}
+  }
 
-static inline void
-ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
+  {
+  for (CtxList *l = clients; l; l = l->next)
+  {
+    CtxClient *client = l->data;
+    VT *vt = client->vt;
+    if (vt && !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED))
     {
-#if CTX_RGB565_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_565_pack (255, 0, 255, 0); }
+      if (flag_is_set(client->flags, ITK_CLIENT_SHADED))
+      {
+        ctx_client_use_images (ctx, client);
+      }
       else
-#endif
-        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); }
-      pixel+=1;
-      rgba +=4;
+      {
+        ctx_client_draw (ctx, client, client->x, client->y);
+      }
+
+      // resize regions
+      if (client == active &&
+         !flag_is_set(client->flags, ITK_CLIENT_SHADED) &&
+         !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) &&
+         flag_is_set(client->flags, ITK_CLIENT_UI_RESIZABLE))
+      {
+        itk_style_color (ctx, "titlebar-focused-bg");
+
+        ctx_rectangle (ctx,
+                       client->x,
+                       client->y - titlebar_height * 2,
+                       client->width, titlebar_height);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_n, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_N);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x,
+                       client->y + client->height - titlebar_height,
+                       client->width, titlebar_height * 2);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_s, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_S);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x + client->width,
+                       client->y - titlebar_height,
+                       titlebar_height, client->height + titlebar_height);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_e, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_E);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x - titlebar_height,
+                       client->y - titlebar_height,
+                       titlebar_height, client->height + titlebar_height);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_w, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_W);
+        ctx_begin_path (ctx); 
+
+        ctx_rectangle (ctx,
+                       client->x + client->width - titlebar_height,
+                       client->y - titlebar_height * 2,
+                       titlebar_height * 2, titlebar_height * 2);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_ne, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_NE);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x - titlebar_height,
+                       client->y - titlebar_height * 2,
+                       titlebar_height * 2, titlebar_height * 2);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_nw, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_NW);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x - titlebar_height,
+                       client->y + client->height - titlebar_height,
+                       titlebar_height * 2, titlebar_height * 2);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_sw, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_SW);
+        ctx_begin_path (ctx);
+
+        ctx_rectangle (ctx,
+                       client->x + client->width - titlebar_height,
+                       client->y + client->height - titlebar_height,
+                       titlebar_height * 2, titlebar_height * 2);
+        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_se, client, NULL);
+        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_SE);
+        ctx_begin_path (ctx);
+
+      }
+
+      if (client->flags & ITK_CLIENT_TITLEBAR)
+        ctx_client_titlebar_draw (ctx, client, client->x, client->y, client->width, titlebar_height);
     }
+  }
+  }
+  return 0;
 }
 
-static void
-ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS)
-{
-  uint8_t pixels[count * 4];
-  ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count);
-}
+extern int _ctx_enable_hash_cache;
 
+void vt_audio_task (VT *vt, int click);
 
-#endif
-#if CTX_ENABLE_RGB565_BYTESWAPPED
+int ctx_input_pending (Ctx *ctx, int timeout);
 
-static inline void
-ctx_RGB565_BS_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+int ctx_clients_need_redraw (Ctx *ctx)
 {
-  const uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-      ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 1);
-#if CTX_RGB565_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
-      else
+  int changes = 0;
+  int follow_mouse = focus_follows_mouse;
+      CtxList *to_remove = NULL;
+  //ensure_layout ();
+
+//  if (print_shape_cache_rate)
+//    fprintf (stderr, "\r%f ", ctx_shape_cache_rate);
+
+   CtxClient *client = find_active (ctx_pointer_x (ctx),
+                                    ctx_pointer_y (ctx));
+
+   if (follow_mouse || ctx_pointer_is_down (ctx, 0) ||
+       ctx_pointer_is_down (ctx, 1) || (active==NULL))
+   {
+        if (client)
+        {
+          if (active != client)
+          {
+            active = client;
+            if (follow_mouse == 0 ||
+                (ctx_pointer_is_down (ctx, 0) ||
+                 ctx_pointer_is_down (ctx, 1)))
+            {
+              //if (client != clients->data)
+       #if 1
+              if ((client->flags & ITK_CLIENT_MAXIMIZED)==0)
+              {
+                ctx_list_remove (&clients, client);
+                ctx_list_append (&clients, client);
+              }
 #endif
-        { rgba[3] = 255; }
-      pixel+=1;
-      rgba +=4;
-    }
+            }
+            changes ++;
+          }
+        }
+   }
+
+   for (CtxList *l = clients; l; l = l->next)
+   {
+     CtxClient *client = l->data;
+     if (client->vt)
+       {
+         if (vt_is_done (client->vt))
+           ctx_list_prepend (&to_remove, client);
+       }
+   }
+   for (CtxList *l = to_remove; l; l = l->next)
+   {
+     ctx_client_remove (ctx, l->data);
+     changes++;
+   }
+   while (to_remove)
+   {
+     ctx_list_remove (&to_remove, to_remove->data);
+   }
+
+   changes += ctx_clients_dirty_count ();
+   return changes != 0;
 }
 
-static inline void
-ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+float ctx_avg_bytespeed = 0.0;
+
+static void ctx_client_handle_events_iteration (Ctx *ctx)
 {
-  uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-#if CTX_RGB565_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_565_pack (255, 0, 255, 1); }
+  static int fail_safe = 0;
+  //int n_clients = ctx_list_length (clients);
+      int pending_data = 0;
+      long time_start = ctx_ticks ();
+      int sleep_time = 1000000/ctx_target_fps;
+
+      pending_data = ctx_input_pending (ctx, sleep_time);
+
+      ctx_fetched_bytes = 0;
+      if (pending_data || fail_safe>100)
+      {
+        if (!pending_data)pending_data = 1;
+        /* record amount of time spent - and adjust time of reading for
+         * vts?
+         */
+        long int fractional_sleep = sleep_time / pending_data;
+        for (CtxList *l = clients; l; l = l->next)
+        {
+          CtxClient *client = l->data;
+          ctx_client_lock (client);
+          int found = 0;
+          for (CtxList *l2 = clients; l2; l2 = l2->next)
+            if (l2->data == client) found = 1;
+          if (!found)
+            goto done;
+          
+          ctx_fetched_bytes += vt_poll (client->vt, fractional_sleep);
+          //ctx_fetched_bytes += vt_poll (client->vt, sleep_time); //fractional_sleep);
+          ctx_client_unlock (client);
+        }
+done:
+        fail_safe = 0;
+      }
       else
-#endif
-        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 1); }
-      pixel+=1;
-      rgba +=4;
-    }
-}
+      {
+        fail_safe ++;
+        for (CtxList *l = clients; l; l = l->next)
+        {
+          CtxClient *client = l->data;
+          vt_audio_task (client->vt, 0);
+        }
+      }
 
-static void
-ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS)
-{
-  uint8_t pixels[count * 4];
-  ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count);
-}
+      //int got_events = 0;
 
+      //while (ctx_get_event (ctx)) { }
+#if 0
+      if (changes /*|| pending_data */)
+      {
+        ctx_target_fps *= 1.6;
+        if (ctx_target_fps > 60) ctx_target_fps = 60;
+      }
+      else
+      {
+        ctx_target_fps = ctx_target_fps * 0.95 + 30.0 * 0.05;
 
-#endif
+        // 20fps is the lowest where sun 8bit ulaw 8khz works reliably
+      }
 
-CtxPixelFormatInfo CTX_COMPOSITE_SUFFIX(ctx_pixel_formats)[]=
-{
-#if CTX_ENABLE_RGBA8
-  {
-    CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
-    NULL, NULL, NULL, ctx_setup_RGBA8
-  },
-#endif
-#if CTX_ENABLE_BGRA8
-  {
-    CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_GRAYF
-  {
-    CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
-    NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF,
-  },
-#endif
-#if CTX_ENABLE_GRAYAF
-  {
-    CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
-    NULL, NULL, NULL, ctx_setup_GRAYAF,
-  },
-#endif
-#if CTX_ENABLE_RGBAF
-  {
-    CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF,
-    NULL, NULL, NULL, ctx_setup_RGBAF,
-  },
-#endif
-#if CTX_ENABLE_RGB8
-  {
-    CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_GRAY1
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8,
-    ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8,
-    ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY2
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8,
-    ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8,
-    ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY4
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8,
-    ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8,
-    ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY8
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8,
-    ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAYA8
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8,
-    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8,
+      if (ctx_avg_bytespeed > 1024 * 1024) ctx_target_fps = 10.0;
+
+      if (_ctx_green < 0.4)
+        ctx_target_fps = 120.0;
+      else if (_ctx_green > 0.6)
+        ctx_target_fps = 25.0;
+
+      //ctx_target_fps = 30.0;
 #else
-    CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_RGB332
-  {
-    CTX_FORMAT_RGB332, 3, 8, 4, 10, 12, CTX_FORMAT_RGBA8,
-    ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332,
-    ctx_composite_convert, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_RGB565
-  {
-    CTX_FORMAT_RGB565, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
-    ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565,
-    ctx_composite_RGB565, ctx_setup_RGBA8,
-  },
+      ctx_target_fps = 30.0;
 #endif
-#if CTX_ENABLE_RGB565_BYTESWAPPED
-  {
-    CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
-    ctx_RGB565_BS_to_RGBA8,
-    ctx_RGBA8_to_RGB565_BS,
-    ctx_composite_RGB565_BS, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_CMYKAF
-  {
-    CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, NULL, ctx_setup_CMYKAF,
-  },
-#endif
-#if CTX_ENABLE_CMYKA8
-  {
-    CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKAF,
-  },
-#endif
-#if CTX_ENABLE_CMYK8
-  {
-    CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYKAF,
-  },
+
+      long time_end = ctx_ticks ();
+
+      int timed = (time_end-time_start);
+      float bytespeed = ctx_fetched_bytes / ((timed)/ (1000.0f * 1000.0f));
+
+      ctx_avg_bytespeed = bytespeed * 0.2 + ctx_avg_bytespeed * 0.8;
+#if 0
+      fprintf (stderr, "%.2fmb/s %i/%i  %.2f                    \r", ctx_avg_bytespeed/1024/1024, 
ctx_fetched_bytes, timed, ctx_target_fps);
 #endif
-  {
-    CTX_FORMAT_NONE
-  }
-};
+}
 
 
-void
-CTX_COMPOSITE_SUFFIX(ctx_compositor_setup) (CtxRasterizer *rasterizer)
+static int ctx_clients_handle_events_fun (void *data)
 {
-  if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_TEXTURE)
+  Ctx *ctx = data;
+  while (!ctx->quit)
   {
-    if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed)
-      _ctx_texture_prepare_color_management (rasterizer,
-        rasterizer->state->gstate.source_fill.texture.buffer);
+    int n_clients = ctx_list_length (clients);
+    ctx_client_handle_events_iteration (data);
+    switch (n_clients)
+    {
+      case 0:
+        usleep (1000 * 10);
+        break;
+      case 1:
+        usleep (1); // letting quit work - and also makes framerate for dump
+        break;
+      default:
+        usleep (0); // the switching between clients should be enough
+        break;
+    }
   }
+  return 0;
+}
 
-  if (rasterizer->format->setup)
-  {
-    // this works also when _default is passed
-    rasterizer->format->setup (rasterizer);
-  }
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
-  {
-    case CTX_SOURCE_LINEAR_GRADIENT:
-    case CTX_SOURCE_RADIAL_GRADIENT:
-      ctx_gradient_cache_prime (rasterizer);
-  }
-#endif
+void ctx_clients_handle_events (Ctx *ctx)
+{
+#if CTX_THREADS==0
+    ctx_client_handle_events_iteration (ctx);
+#else
+    static thrd_t tid = 0;
+    if (tid == 0)
+    {
+      thrd_create (&tid, (void*)ctx_clients_handle_events_fun, ctx);
+    }
 #endif
 }
 
-#endif
+#endif /* CTX_VT */
 #endif //  __CTX_H__
diff --git a/operations/common/vector-fill.c b/operations/common/vector-fill.c
index 4523577d4..a067dc98e 100644
--- a/operations/common/vector-fill.c
+++ b/operations/common/vector-fill.c
@@ -319,7 +319,7 @@ gegl_op_class_init (GeglOpClass *klass)
     "name",        "gegl:fill-path",
     "title",       _("Fill Path"),
     "categories",  "render:vector",
-    "reference-hash", "92f09df038c01fed0460305d33f4250b",
+    "reference-hash", "941dcd6c90739ee2d8a19593c07828b1",
     "description", _("Renders a filled region"),
     "reference-composition", composition,
     NULL);
diff --git a/tests/compositions/reference/gegl.png b/tests/compositions/reference/gegl.png
index 366f89ad9..b15b77eba 100644
Binary files a/tests/compositions/reference/gegl.png and b/tests/compositions/reference/gegl.png differ


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