[gegl] ctx: update from upstream



commit 787a971a692eeba2b7c6112a9548bf5b99dbe2c0
Author: Øyvind Kolås <pippin gimp org>
Date:   Thu Dec 16 01:48:29 2021 +0100

    ctx: update from upstream

 gegl/ctx/ctx.h | 4845 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 3145 insertions(+), 1700 deletions(-)
---
diff --git a/gegl/ctx/ctx.h b/gegl/ctx/ctx.h
index 06b3fc314..46da326d2 100644
--- a/gegl/ctx/ctx.h
+++ b/gegl/ctx/ctx.h
@@ -1,4 +1,4 @@
-/* ctx git commit: e119e43 */
+/* ctx git commit: 4f1952c */
 /* 
  * ctx.h is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -49,6 +49,7 @@ extern "C" {
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
+#include <sys/select.h> // XXX if events?
 #endif
 
 typedef struct _Ctx            Ctx;
@@ -374,23 +375,25 @@ int ctx_utf8_strlen (const char *s);
 /* If cairo.h is included before ctx.h add cairo integration code
  */
 #ifdef CAIRO_H
+#ifndef CTX_CAIRO
 #define CTX_CAIRO 1
-#else
-#define CTX_CAIRO 0
+#endif
 #endif
 
+#ifndef CTX_SDL
 #ifdef SDL_h_
 #define CTX_SDL 1
 #else
 #define CTX_SDL 0
 #endif
+#endif
 
 #ifndef CTX_FB
-#if CTX_SDL
-#define CTX_FB 1
-#else
 #define CTX_FB 0
 #endif
+
+#ifndef CTX_KMS
+#define CTX_KMS 0
 #endif
 
 #if CTX_SDL
@@ -406,6 +409,9 @@ int ctx_utf8_strlen (const char *s);
 #endif
 
 #if CTX_CAIRO
+#ifndef CAIRO_H
+typedef struct _cairo_t cairo_t;
+#endif
 
 /* render the deferred commands of a ctx context to a cairo
  * context
@@ -439,7 +445,7 @@ int      ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
 
 typedef enum
 {
-  CTX_FILL_RULE_WINDING,
+  CTX_FILL_RULE_WINDING = 0,
   CTX_FILL_RULE_EVEN_ODD
 } CtxFillRule;
 
@@ -529,8 +535,9 @@ typedef enum
 
 typedef enum
 {
-  CTX_TEXT_ALIGN_START = 0,
-  CTX_TEXT_ALIGN_END,
+  CTX_TEXT_ALIGN_START = 0,  // in mrg these didnt exist
+  CTX_TEXT_ALIGN_END,        // but left/right did
+  CTX_TEXT_ALIGN_JUSTIFY, // not handled in ctx
   CTX_TEXT_ALIGN_CENTER,
   CTX_TEXT_ALIGN_LEFT,
   CTX_TEXT_ALIGN_RIGHT
@@ -665,6 +672,8 @@ void *ctx_get_renderer (Ctx *ctx);
 
 int ctx_renderer_is_sdl (Ctx *ctx);
 int ctx_renderer_is_fb (Ctx *ctx);
+int ctx_renderer_is_kms (Ctx *ctx);
+int ctx_renderer_is_tiled (Ctx *ctx);
 int ctx_renderer_is_ctx (Ctx *ctx);
 int ctx_renderer_is_term (Ctx *ctx);
 
@@ -675,8 +684,8 @@ int ctx_renderer_is_term (Ctx *ctx);
  */
 int ctx_is_dirty (Ctx *ctx);
 void ctx_set_dirty (Ctx *ctx, int dirty);
-float ctx_get_float (Ctx *ctx, uint64_t hash);
-void ctx_set_float (Ctx *ctx, uint64_t hash, float value);
+float ctx_get_float (Ctx *ctx, uint32_t hash);
+void ctx_set_float (Ctx *ctx, uint32_t hash, float value);
 
 unsigned long ctx_ticks (void);
 void ctx_flush (Ctx *ctx);
@@ -1644,11 +1653,10 @@ typedef enum
 
 enum _CtxAntialias
 {
-  CTX_ANTIALIAS_DEFAULT, // fast - suitable for realtime UI
+  CTX_ANTIALIAS_DEFAULT, //
   CTX_ANTIALIAS_NONE, // non-antialiased
   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;
 
@@ -1702,7 +1710,7 @@ typedef struct _CtxParser CtxParser;
   float      cell_height,
   int        cursor_x,
   int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
   int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
   void  *prop_data,
   void (*exit) (void *exit_data),
@@ -1746,6 +1754,11 @@ int
 ctx_get_contents (const char     *path,
                    unsigned char **contents,
                    long           *length);
+int
+ctx_get_contents2 (const char     *path,
+                   unsigned char **contents,
+                   long           *length,
+                   long            max_len);
 
 void ctx_parser_free (CtxParser *parser);
 typedef struct _CtxSHA1 CtxSHA1;
@@ -1777,6 +1790,28 @@ void ctx_matrix_multiply (CtxMatrix       *result,
                           const CtxMatrix *s);
 
 
+/* we already have the start of the file available which disambiguates some
+ * of our important supported formats, give preference to magic, then extension
+ * then text plain vs binary.
+ */
+const char *ctx_guess_media_type (const char *path, const char *content, int len);
+
+/* get media-type, with preference towards using extension of path and
+ * not reading the data at all.
+ */
+const char *ctx_path_get_media_type (const char *path);
+
+typedef enum {
+  CTX_MEDIA_TYPE_NONE=0,
+  CTX_MEDIA_TYPE_TEXT,
+  CTX_MEDIA_TYPE_IMAGE,
+  CTX_MEDIA_TYPE_VIDEO,
+  CTX_MEDIA_TYPE_AUDIO,
+  CTX_MEDIA_TYPE_INODE,
+  CTX_MEDIA_TYPE_APPLICATION,
+} CtxMediaTypeClass;
+
+CtxMediaTypeClass ctx_media_type_class (const char *media_type);
 
 
 float ctx_term_get_cell_width (Ctx *ctx);
@@ -1854,6 +1889,7 @@ struct _CtxString
 
 CtxString   *ctx_string_new_with_size  (const char *initial, int initial_size);
 CtxString   *ctx_string_new            (const char *initial);
+CtxString   *ctx_string_new_printf (const char *format, ...);
 char       *ctx_string_dissolve       (CtxString *string);
 void        ctx_string_free           (CtxString *string, int freealloc);
 const char *ctx_string_get            (CtxString *string);
@@ -1868,6 +1904,7 @@ void        ctx_string_append_string  (CtxString *string, CtxString *string2);
 void        ctx_string_append_unichar (CtxString *string, unsigned int unichar);
 void        ctx_string_append_data    (CtxString *string, const char *data, int len);
 
+void        ctx_string_pre_alloc       (CtxString *string, int size);
 void        ctx_string_append_utf8char (CtxString *string, const char *str);
 void        ctx_string_append_printf  (CtxString *string, const char *format, ...);
 void        ctx_string_replace_utf8   (CtxString *string, int pos, const char *new_glyph);
@@ -4243,7 +4280,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 }
 
 #endif
-
 /* definitions that determine which features are included and their settings,
  * for particular platforms - in particular microcontrollers ctx might need
  * tuning for different quality/performance/resource constraints.
@@ -4743,7 +4779,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_MAX_TEXTURES
-#define CTX_MAX_TEXTURES         16
+#define CTX_MAX_TEXTURES         32
 #endif
 
 #ifndef CTX_HASH_ROWS
@@ -4820,7 +4856,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_TILED
-#if CTX_SDL || CTX_FB
+#if CTX_SDL || CTX_FB || CTX_KMS
 #define CTX_TILED 1
 #else
 #define CTX_TILED 0
@@ -4828,7 +4864,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_THREADS
-#if CTX_FB
+#if CTX_TILED
 #define CTX_THREADS 1
 #else
 #define CTX_THREADS 0
@@ -4836,8 +4872,21 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #if CTX_THREADS
-#include <threads.h>
+#include <pthread.h>
+#define mtx_lock pthread_mutex_lock
+#define mtx_unlock pthread_mutex_unlock
+#define mtx_t pthread_mutex_t
+#define cnd_t pthread_cond_t
+#define mtx_plain NULL
+#define mtx_init pthread_mutex_init
+#define cnd_init(a) pthread_cond_init(a,NULL)
+#define cnd_wait pthread_cond_wait
+#define cnd_broadcast pthread_cond_broadcast
+#define thrd_create(tid, tiled_render_fun, args) pthread_create(tid, NULL, tiled_render_fun, args)
+#define thrd_t pthread_t
 #endif
+
+
  /* Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
  */
 
@@ -4904,12 +4953,15 @@ ctx_fabsf (float x)
 static inline float
 ctx_invsqrtf (float x)
 {
-  void *foo = &x;
+  union
+  {
+    float f;
+    uint32_t i;
+  } u = { x };
   float xhalf = 0.5f * x;
-  int i=* (int *) foo;
-  void *bar = &i;
+  int i=u.i;
   i = 0x5f3759df - (i >> 1);
-  x = * (float *) bar;
+  x = u.f;
   x *= (1.5f - xhalf * x * x);
   x *= (1.5f - xhalf * x * x); //repeating Newton-Raphson step for higher precision
   return x;
@@ -4918,12 +4970,16 @@ ctx_invsqrtf (float x)
 static inline float
 ctx_invsqrtf_fast (float x)
 {
-  void *foo = &x;
+  union
+  {
+    float f;
+    uint32_t i;
+  } u = { x };
+
 //float xhalf = 0.5f * x;
-  int i=* (int *) foo;
-  void *bar = &i;
+  int i=u.i;
   i = 0x5f3759df - (i >> 1);
-  x = * (float *) bar;
+  x = u.f;
 //x *= (1.5f - xhalf * x * x);
   return x;
 }
@@ -5090,8 +5146,8 @@ static inline float _ctx_parse_float (const char *str, char **endptr)
   return strtod (str, endptr); /* XXX: , vs . problem in some locales */
 }
 
-const char *ctx_get_string (Ctx *ctx, uint64_t hash);
-void ctx_set_string (Ctx *ctx, uint64_t hash, const char *value);
+const char *ctx_get_string (Ctx *ctx, uint32_t hash);
+void ctx_set_string (Ctx *ctx, uint32_t hash, const char *value);
 typedef struct _CtxColor CtxColor;
 
 void
@@ -5109,8 +5165,8 @@ CtxState *ctx_get_state (Ctx *ctx);
 void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out);
 void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a);
 void ctx_color_free (CtxColor *color);
-void ctx_set_color (Ctx *ctx, uint64_t hash, CtxColor *color);
-int  ctx_get_color (Ctx *ctx, uint64_t hash, CtxColor *color);
+void ctx_set_color (Ctx *ctx, uint32_t hash, CtxColor *color);
+int  ctx_get_color (Ctx *ctx, uint32_t hash, CtxColor *color);
 int  ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string);
 
 int ctx_color_is_transparent (CtxColor *color);
@@ -5119,7 +5175,7 @@ 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);
-int ctx_is_set_now (Ctx *ctx, uint64_t hash);
+int ctx_is_set_now (Ctx *ctx, uint32_t hash);
 void ctx_set_size (Ctx *ctx, int width, int height);
 
 static inline float ctx_matrix_get_scale (CtxMatrix *matrix)
@@ -5175,253 +5231,255 @@ int ctx_pixel_format_ebpp (CtxPixelFormat format);
 
 #define TOKENHASH(a)    ((uint64_t)a)
 
-#define CTX_strokeSource TOKENHASH(3061861651908008)
-#define CTX_add_stop   TOKENHASH(1274978316678)
-#define CTX_addStop    TOKENHASH(40799943078278)
-#define CTX_alphabetic         TOKENHASH(2629359926678406)
-#define CTX_arc        TOKENHASH(11526)
-#define CTX_arc_to     TOKENHASH(1187065094)
-#define CTX_arcTo      TOKENHASH(38558051590)
-#define CTX_begin_path         TOKENHASH(3004110681622984)
-#define CTX_beginPath  TOKENHASH(8437143659599196)
-#define CTX_bevel      TOKENHASH(29868488)
-#define CTX_bottom     TOKENHASH(1043772488)
-#define CTX_cap        TOKENHASH(37066)
-#define CTX_center     TOKENHASH(1358332362)
-#define CTX_clear      TOKENHASH(42154890)
-#define CTX_color      TOKENHASH(43086922)
-#define CTX_copy       TOKENHASH(1807434)
-#define CTX_clip       TOKENHASH(1203082)
-#define CTX_close_path         TOKENHASH(3004110663420810)
-#define CTX_closePath  TOKENHASH(8437144279135038)
-#define CTX_cmyka      TOKENHASH(7199690)
-#define CTX_cmyk       TOKENHASH(908234)
-#define CTX_cmykaS     TOKENHASH(36313095114)
-#define CTX_cmykS      TOKENHASH(1135467466)
-#define CTX_color      TOKENHASH(43086922)
-#define CTX_blending   TOKENHASH(653586873224)
-#define CTX_blend      TOKENHASH(13646728)
-#define CTX_blending_mode      TOKENHASH(8147360531130856)
-#define CTX_blendingMode       TOKENHASH(7483585768187540)
-#define CTX_blend_mode         TOKENHASH(2758775686577032)
-#define CTX_blendMode  TOKENHASH(7773213171090182)
-#define CTX_composite  TOKENHASH(16930059746378)
-#define CTX_compositing_mode   TOKENHASH(2417816728103524)
-#define CTX_compositingMode    TOKENHASH(2807194446992106)
-#define CTX_curve_to   TOKENHASH(1215559149002)
-#define CTX_curveTo    TOKENHASH(39483449320906)
-#define CTX_darken     TOKENHASH(1089315020)
-#define CTX_defineGlyph        TOKENHASH(2497926167421194)
-#define CTX_defineTexture      TOKENHASH(2623744577477404)
-#define CTX_kerningPair        TOKENHASH(6964644556489058)
-#define CTX_destinationIn      TOKENHASH(8153299143600102)
-#define CTX_destination_in     TOKENHASH(3824201982576824)
-#define CTX_destinationAtop    TOKENHASH(8185118415574560)
-#define CTX_destination_atop   TOKENHASH(7742210324901698)
-#define CTX_destinationOver    TOKENHASH(3261713333438500)
-#define CTX_destination_over   TOKENHASH(7742210324728474)
-#define CTX_destinationOut     TOKENHASH(7742210322269456)
-#define CTX_destination_out    TOKENHASH(8153299143489102)
-#define CTX_difference         TOKENHASH(2756492040618700)
-#define CTX_done       TOKENHASH(492620)
-#define CTX_drgba      TOKENHASH(6573324)
-#define CTX_drgb       TOKENHASH(281868)
-#define CTX_drgbaS     TOKENHASH(36312468748)
-#define CTX_drgbS      TOKENHASH(1134841100)
-#define CTX_end        TOKENHASH(13326)
-#define CTX_endfun     TOKENHASH(1122513934)
-#define CTX_end_group  TOKENHASH(41200834917390)
-#define CTX_endGroup   TOKENHASH(3570227948106766)
-#define CTX_even_odd   TOKENHASH(426345774606)
-#define CTX_evenOdd    TOKENHASH(13671748091406)
-#define CTX_exit       TOKENHASH(1465998)
-#define CTX_fill       TOKENHASH(946896)
-#define CTX_fill_rule  TOKENHASH(16405972808400)
-#define CTX_fillRule   TOKENHASH(2776813389378256)
-#define CTX_flush      TOKENHASH(22395792)
-#define CTX_font       TOKENHASH(1475664)
-#define CTX_font_size  TOKENHASH(17342343316560)
-#define CTX_setFontSize        TOKENHASH(8657699789799734)
-#define CTX_fontSize   TOKENHASH(2806775148872784)
-#define CTX_function   TOKENHASH(1136803546576)
-#define CTX_getkey     TOKENHASH(1827516882)
-#define CTX_global_alpha       TOKENHASH(6945103263242432)
-#define CTX_globalAlpha        TOKENHASH(2684560928159160)
-#define CTX_glyph      TOKENHASH(22207378)
-#define CTX_gradient_add_stop  TOKENHASH(7829524561074416)
-#define CTX_gradientAddStop    TOKENHASH(8126442749593072)
-#define CTX_graya      TOKENHASH(8068370)
-#define CTX_gray       TOKENHASH(1776914)
-#define CTX_grayaS     TOKENHASH(36313963794)
-#define CTX_grayS      TOKENHASH(1136336146)
-#define CTX_hanging    TOKENHASH(20424786132)
-#define CTX_height     TOKENHASH(1497979348)
-#define CTX_hor_line_to        TOKENHASH(8345271542735158)
-#define CTX_horLineTo  TOKENHASH(3629696407754856)
-#define CTX_hue        TOKENHASH(15828)
-#define CTX_identity   TOKENHASH(1903455910294)
-#define CTX_ideographic        TOKENHASH(4370819675496700)
-#define CTX_imageSmoothing     TOKENHASH(4268778175825416)
-#define CTX_join       TOKENHASH(1072216)
-#define CTX_laba       TOKENHASH(205020)
-#define CTX_lab        TOKENHASH(8412)
-#define CTX_lcha       TOKENHASH(217436)
-#define CTX_lch        TOKENHASH(20828)
-#define CTX_labaS      TOKENHASH(1134764252)
-#define CTX_labS       TOKENHASH(35463388)
-#define CTX_lchaS      TOKENHASH(1134776668)
-#define CTX_lchS       TOKENHASH(35475804)
-#define CTX_left       TOKENHASH(1458652)
-#define CTX_lighter    TOKENHASH(43466246876)
-#define CTX_lighten    TOKENHASH(34876312284)
-#define CTX_linear_gradient    TOKENHASH(7801595375834212)
-#define CTX_linearGradient     TOKENHASH(4439260636789186)
-#define CTX_line_cap   TOKENHASH(1243731165916)
-#define CTX_lineCap    TOKENHASH(3436510399409980)
-#define CTX_setLineCap         TOKENHASH(7897176123029482)
-#define CTX_line_height        TOKENHASH(3300223516389168)
-#define CTX_line_join  TOKENHASH(35977601450716)
-#define CTX_lineJoin   TOKENHASH(3403122163024604)
-#define CTX_setLineJoin        TOKENHASH(2768281536656332)
-#define CTX_line_spacing       TOKENHASH(2519451230887150)
-#define CTX_line_to    TOKENHASH(37986206428)
-#define CTX_lineTo     TOKENHASH(1233857774300)
-#define CTX_lineDash   TOKENHASH(3001937455186652)
-#define CTX_lineDashOffset     TOKENHASH(3704120356324362)
-#define CTX_line_width         TOKENHASH(3004303386575580)
-#define CTX_lineWidth  TOKENHASH(8241159254028040)
-#define CTX_setLineWidth       TOKENHASH(8037913618228476)
-#define CTX_view_box   TOKENHASH(1823485803248)
-#define CTX_viewBox    TOKENHASH(3915860941641152)
-#define CTX_middle     TOKENHASH(499528414)
-#define CTX_miter      TOKENHASH(42447582)
-#define CTX_miter_limit        TOKENHASH(4281255327472850)
-#define CTX_miterLimit         TOKENHASH(7937453649653124)
-#define CTX_move_to    TOKENHASH(37986223198)
-#define CTX_moveTo     TOKENHASH(1233857791070)
-#define CTX_multiply   TOKENHASH(1886723143134)
-#define CTX_new_page   TOKENHASH(500602882528)
-#define CTX_newPage    TOKENHASH(16020123011552)
-#define CTX_new_path   TOKENHASH(734678600160)
-#define CTX_newPath    TOKENHASH(23510545975776)
-#define CTX_new_state  TOKENHASH(16912954280416)
-#define CTX_none       TOKENHASH(492640)
-#define CTX_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)
-#define CTX_radial_gradient    TOKENHASH(8267515704460560)
-#define CTX_radialGradient     TOKENHASH(4399889250822134)
-#define CTX_rectangle  TOKENHASH(16375644301800)
-#define CTX_rect       TOKENHASH(1452520)
-#define CTX_rel_arc_to         TOKENHASH(3496527781786088)
-#define CTX_relArcTo   TOKENHASH(3209152175601038)
-#define CTX_rel_curve_to       TOKENHASH(4439651822639910)
-#define CTX_relCurveTo         TOKENHASH(7294415873689320)
-#define CTX_rel_hor_line_to    TOKENHASH(7051067105640810)
-#define CTX_relHorLineTo       TOKENHASH(8737419863647946)
-#define CTX_relVerLineTo       TOKENHASH(8737441317512906)
-#define CTX_rel_line_to        TOKENHASH(8345271542378314)
-#define CTX_relLineTo  TOKENHASH(3629696197927444)
-#define CTX_rel_move_to        TOKENHASH(8344984486309706)
-#define CTX_relMoveTo  TOKENHASH(3571677202293268)
-#define CTX_rel_quad_to        TOKENHASH(8343627754794826)
-#define CTX_relQuadTo  TOKENHASH(7894357900599828)
-#define CTX_rel_smoothq_to     TOKENHASH(7340038162167138)
-#define CTX_relSmoothqTo       TOKENHASH(3188040406230844)
-#define CTX_rel_smooth_to      TOKENHASH(8144941131301668)
-#define CTX_relSmoothTo        TOKENHASH(8947422784198618)
-#define CTX_rel_ver_line_to    TOKENHASH(8148126344839530)
-#define CTX_restore    TOKENHASH(16411699688)
-#define CTX_reset      TOKENHASH(46639592)
-#define CTX_rgba       TOKENHASH(205416)
-#define CTX_rgb        TOKENHASH(8808)
-#define CTX_rgbaS      TOKENHASH(1134764648)
-#define CTX_rgbS       TOKENHASH(35463784)
-#define CTX_right      TOKENHASH(46811880)
-#define CTX_rotate     TOKENHASH(516142184)
-#define CTX_round      TOKENHASH(13679720)
-#define CTX_round_rectangle    TOKENHASH(4332080966833870)
-#define CTX_roundRectangle     TOKENHASH(8317255488676642)
-#define CTX_save       TOKENHASH(508138)
-#define CTX_scale      TOKENHASH(15604074)
-#define CTX_screen     TOKENHASH(1088921962)
-#define CTX_setkey     TOKENHASH(1827516906)
-#define CTX_shadowBlur         TOKENHASH(2924056626980284)
-#define CTX_shadowColor        TOKENHASH(3509599043947446)
-#define CTX_shadowOffsetX      TOKENHASH(8499312693589794)
-#define CTX_shadowOffsetY      TOKENHASH(8499312693589796)
-#define CTX_smooth_quad_to     TOKENHASH(6832232668547050)
-#define CTX_smoothQuadTo       TOKENHASH(8278352345012646)
-#define CTX_smooth_to  TOKENHASH(38898089692138)
-#define CTX_smoothTo   TOKENHASH(3515270388878314)
-#define CTX_sourceIn   TOKENHASH(3444145493687402)
-#define CTX_source_in  TOKENHASH(35942915423338)
-#define CTX_sourceAtop         TOKENHASH(2920281959978332)
-#define CTX_source_atop        TOKENHASH(3007410591464110)
-#define CTX_sourceOut  TOKENHASH(7371294932695718)
-#define CTX_source_out         TOKENHASH(3851660580666474)
-#define CTX_sourceOver         TOKENHASH(7584784067385004)
-#define CTX_sourceTransform TOKENHASH(7515321363744130)
-#define CTX_source_over        TOKENHASH(8690648756484770)
-#define CTX_square     TOKENHASH(511950058)
-#define CTX_start      TOKENHASH(47455658)
-#define CTX_start_move         TOKENHASH(2798358138985898)
-#define CTX_start_group        TOKENHASH(7836274863228782)
-#define CTX_startGroup         TOKENHASH(3812645199786240)
-#define CTX_stroke     TOKENHASH(498181546)
-#define CTX_text_align         TOKENHASH(3398277113762284)
-#define CTX_textAlign  TOKENHASH(3063795447820748)
-#define CTX_texture    TOKENHASH(16424292844)
-#define CTX_text_baseline      TOKENHASH(2589194334827348)
-#define CTX_text_baseline      TOKENHASH(2589194334827348)
-#define CTX_textBaseline       TOKENHASH(8381669925369340)
-#define CTX_fillRect   TOKENHASH(3811453831115472)
-#define CTX_text       TOKENHASH(1495532)
-#define CTX_text_direction     TOKENHASH(3614589880641524)
-#define CTX_textDirection      TOKENHASH(6790122975782654)
-#define CTX_text_indent        TOKENHASH(3633795456290560)
-#define CTX_text_stroke        TOKENHASH(8259523149811490)
-#define CTX_strokeText         TOKENHASH(8131451867629426)
-#define CTX_strokeRect         TOKENHASH(8165399289138988)
-#define CTX_top        TOKENHASH(37996)
-#define CTX_transform  TOKENHASH(34396827557164)
-#define CTX_translate  TOKENHASH(16912418348332)
-#define CTX_verLineTo  TOKENHASH(3629696413166220)
-#define CTX_ver_line_to        TOKENHASH(8345271542726354)
-#define CTX_width      TOKENHASH(22426354)
-#define CTX_winding    TOKENHASH(20424590066)
-#define CTX_x  TOKENHASH(52)
-#define CTX_xor        TOKENHASH(42100)
-#define CTX_y  TOKENHASH(54)
-#define CTX_colorSpace         TOKENHASH(3674150843793134)
-#define CTX_userRGB    TOKENHASH(59177128181102)
-#define CTX_userCMYK   TOKENHASH(3354734206905240)
-#define CTX_deviceRGB  TOKENHASH(7818727413767480)
-#define CTX_deviceCMYK         TOKENHASH(8943291245184210)
-#define CTX_silver     TOKENHASH(1358459626)
-#define CTX_fuchsia    TOKENHASH(7225355728)
-#define CTX_gray       TOKENHASH(1776914)
-#define CTX_yellow     TOKENHASH(1714319862)
-#define CTX_white      TOKENHASH(16145074)
-#define CTX_maroon     TOKENHASH(1110548702)
-#define CTX_magenta    TOKENHASH(7952877790)
-#define CTX_blue       TOKENHASH(506760)
-#define CTX_green      TOKENHASH(34028818)
-#define CTX_red        TOKENHASH(12776)
-#define CTX_purple     TOKENHASH(500344292)
-#define CTX_olive      TOKENHASH(16276386)
-#define CTX_teal       TOKENHASH(924140)
-#define CTX_black      TOKENHASH(27597704)
-#define CTX_cyan       TOKENHASH(1056458)
-#define CTX_navy       TOKENHASH(1818848)
-#define CTX_lime       TOKENHASH(490204)
-#define CTX_aqua       TOKENHASH(244934)
-#define CTX_transparent        TOKENHASH(3654078210101184)
-#define CTX_currentColor       TOKENHASH(7501877057638746)
+#define CTX_strokeSource TOKENHASH(3387288669)
+#define CTX_add_stop TOKENHASH(3572486242)
+#define CTX_addStop TOKENHASH(3805374936)
+#define CTX_alphabetic TOKENHASH(2558771929)
+#define CTX_arc TOKENHASH(7298)
+#define CTX_arc_to TOKENHASH(4010563993)
+#define CTX_arcTo TOKENHASH(4138935887)
+#define CTX_begin_path TOKENHASH(3275811535)
+#define CTX_beginPath TOKENHASH(2384638508)
+#define CTX_bevel TOKENHASH(25538884)
+#define CTX_bottom TOKENHASH(905225156)
+#define CTX_cap TOKENHASH(32838)
+#define CTX_center TOKENHASH(1219785030)
+#define CTX_clear TOKENHASH(37825286)
+#define CTX_color TOKENHASH(38757318)
+#define CTX_copy TOKENHASH(1672134)
+#define CTX_clip TOKENHASH(1067782)
+#define CTX_close_path TOKENHASH(3215881683)
+#define CTX_closePath TOKENHASH(3625577848)
+#define CTX_cmyka TOKENHASH(2870086)
+#define CTX_cmyk TOKENHASH(772934)
+#define CTX_cmykaS TOKENHASH(3116500921)
+#define CTX_cmykS TOKENHASH(934005574)
+#define CTX_color TOKENHASH(38757318)
+#define CTX_blending TOKENHASH(3402343403)
+#define CTX_blend TOKENHASH(9317124)
+#define CTX_blending_mode TOKENHASH(4000829592)
+#define CTX_blendingMode TOKENHASH(2577020122)
+#define CTX_blend_mode TOKENHASH(2229422236)
+#define CTX_blendMode TOKENHASH(3450578624)
+#define CTX_composite TOKENHASH(2191186513)
+#define CTX_compositing_mode TOKENHASH(3415700633)
+#define CTX_compositingMode TOKENHASH(3625102151)
+#define CTX_curve_to TOKENHASH(3569729066)
+#define CTX_curveTo TOKENHASH(3536162037)
+#define CTX_darken TOKENHASH(950767688)
+#define CTX_defineGlyph TOKENHASH(3698027829)
+#define CTX_defineTexture TOKENHASH(4201008335)
+#define CTX_kerningPair TOKENHASH(3655936472)
+#define CTX_destinationIn TOKENHASH(2718725020)
+#define CTX_destination_in TOKENHASH(3351938654)
+#define CTX_destinationAtop TOKENHASH(3609906960)
+#define CTX_destination_atop TOKENHASH(2783515582)
+#define CTX_destinationOver TOKENHASH(2378926016)
+#define CTX_destination_over TOKENHASH(2856771196)
+#define CTX_destinationOut TOKENHASH(3944490553)
+#define CTX_destination_out TOKENHASH(3021444620)
+#define CTX_difference TOKENHASH(2530251746)
+#define CTX_done TOKENHASH(357320)
+#define CTX_drgba TOKENHASH(2243720)
+#define CTX_drgb TOKENHASH(146568)
+#define CTX_drgbaS TOKENHASH(2541895879)
+#define CTX_drgbS TOKENHASH(933379208)
+#define CTX_end TOKENHASH(9098)
+#define CTX_endfun TOKENHASH(983966602)
+#define CTX_end_group TOKENHASH(2564160724)
+#define CTX_endGroup TOKENHASH(3639210663)
+#define CTX_even_odd TOKENHASH(2587574889)
+#define CTX_evenOdd TOKENHASH(4065502508)
+#define CTX_exit TOKENHASH(1330698)
+#define CTX_fill TOKENHASH(811596)
+#define CTX_fill_rule TOKENHASH(3026141741)
+#define CTX_fillRule TOKENHASH(2727819936)
+#define CTX_flush TOKENHASH(18066188)
+#define CTX_font TOKENHASH(1340364)
+#define CTX_font_size TOKENHASH(3138232552)
+#define CTX_setFontSize TOKENHASH(2794810212)
+#define CTX_fontSize TOKENHASH(2516141542)
+#define CTX_function TOKENHASH(2157387644)
+#define CTX_getkey TOKENHASH(1688969550)
+#define CTX_global_alpha TOKENHASH(4195339170)
+#define CTX_globalAlpha TOKENHASH(3503999095)
+#define CTX_glyph TOKENHASH(17877774)
+#define CTX_gradient_add_stop TOKENHASH(2527862800)
+#define CTX_gradientAddStop TOKENHASH(2707733066)
+#define CTX_graya TOKENHASH(3738766)
+#define CTX_gray TOKENHASH(1641614)
+#define CTX_grayaS TOKENHASH(3152913809)
+#define CTX_grayS TOKENHASH(934874254)
+#define CTX_hanging TOKENHASH(3379012612)
+#define CTX_height TOKENHASH(1359432016)
+#define CTX_hor_line_to TOKENHASH(3576305368)
+#define CTX_horLineTo TOKENHASH(2768557894)
+#define CTX_hue TOKENHASH(11600)
+#define CTX_identity TOKENHASH(4244560551)
+#define CTX_ideographic TOKENHASH(4062138887)
+#define CTX_imageSmoothing TOKENHASH(3391439578)
+#define CTX_join TOKENHASH(936916)
+#define CTX_laba TOKENHASH(69720)
+#define CTX_lab TOKENHASH(4184)
+#define CTX_lcha TOKENHASH(82136)
+#define CTX_lch TOKENHASH(16600)
+#define CTX_labaS TOKENHASH(933302360)
+#define CTX_labS TOKENHASH(29167704)
+#define CTX_lchaS TOKENHASH(933314776)
+#define CTX_lchS TOKENHASH(29180120)
+#define CTX_left TOKENHASH(1323352)
+#define CTX_lighter TOKENHASH(3085731552)
+#define CTX_lighten TOKENHASH(2243427702)
+#define CTX_linear_gradient TOKENHASH(2750495200)
+#define CTX_linearGradient TOKENHASH(2530643087)
+#define CTX_line_cap TOKENHASH(3442398380)
+#define CTX_lineCap TOKENHASH(4099906770)
+#define CTX_setLineCap TOKENHASH(3062640202)
+#define CTX_line_height TOKENHASH(2825006065)
+#define CTX_line_join TOKENHASH(2796226529)
+#define CTX_lineJoin TOKENHASH(3149521206)
+#define CTX_setLineJoin TOKENHASH(3876390174)
+#define CTX_line_spacing TOKENHASH(3474024390)
+#define CTX_line_to TOKENHASH(2950597468)
+#define CTX_lineTo TOKENHASH(3995194545)
+#define CTX_lineDash TOKENHASH(2275747153)
+#define CTX_lineDashOffset TOKENHASH(2164798257)
+#define CTX_line_width TOKENHASH(2644675969)
+#define CTX_lineWidth TOKENHASH(4067116285)
+#define CTX_setLineWidth TOKENHASH(3835759450)
+#define CTX_view_box TOKENHASH(3076034236)
+#define CTX_viewBox TOKENHASH(3661895848)
+#define CTX_middle TOKENHASH(360981082)
+#define CTX_miter TOKENHASH(38117978)
+#define CTX_miter_limit TOKENHASH(2692682139)
+#define CTX_miterLimit TOKENHASH(3784823268)
+#define CTX_move_to TOKENHASH(3482077014)
+#define CTX_moveTo TOKENHASH(3135948887)
+#define CTX_multiply TOKENHASH(2379318058)
+#define CTX_new_page TOKENHASH(3781461413)
+#define CTX_newPage TOKENHASH(3875814849)
+#define CTX_new_path TOKENHASH(4253517559)
+#define CTX_newPath TOKENHASH(2442450175)
+#define CTX_new_state TOKENHASH(3282144098)
+#define CTX_none TOKENHASH(357340)
+#define CTX_nonzero TOKENHASH(2230085415)
+#define CTX_non_zero TOKENHASH(3127422280)
+#define CTX_normal TOKENHASH(808293340)
+#define CTX_quad_to TOKENHASH(3896875982)
+#define CTX_quadTo TOKENHASH(3916306495)
+#define CTX_radial_gradient TOKENHASH(4226017763)
+#define CTX_radialGradient TOKENHASH(3218566169)
+#define CTX_rectangle TOKENHASH(4111149391)
+#define CTX_rect TOKENHASH(1317220)
+#define CTX_rel_arc_to TOKENHASH(2653353243)
+#define CTX_relArcTo TOKENHASH(2940381656)
+#define CTX_rel_curve_to TOKENHASH(2413603721)
+#define CTX_relCurveTo TOKENHASH(3745640049)
+#define CTX_rel_hor_line_to TOKENHASH(3292310681)
+#define CTX_relHorLineTo TOKENHASH(2661057467)
+#define CTX_relVerLineTo TOKENHASH(3868849192)
+#define CTX_rel_line_to TOKENHASH(2865414393)
+#define CTX_relLineTo TOKENHASH(2437091951)
+#define CTX_rel_move_to TOKENHASH(4169997481)
+#define CTX_relMoveTo TOKENHASH(2527491593)
+#define CTX_rel_quad_to TOKENHASH(4209276505)
+#define CTX_relQuadTo TOKENHASH(3961311908)
+#define CTX_rel_smoothq_to TOKENHASH(3923163705)
+#define CTX_relSmoothqTo TOKENHASH(2913202089)
+#define CTX_rel_smooth_to TOKENHASH(4229528839)
+#define CTX_relSmoothTo TOKENHASH(3458671695)
+#define CTX_rel_ver_line_to TOKENHASH(2484242991)
+#define CTX_restore TOKENHASH(2936409475)
+#define CTX_reset TOKENHASH(42309988)
+#define CTX_rgba TOKENHASH(70116)
+#define CTX_rgb TOKENHASH(4580)
+#define CTX_rgbaS TOKENHASH(933302756)
+#define CTX_rgbS TOKENHASH(29168100)
+#define CTX_right TOKENHASH(42482276)
+#define CTX_rotate TOKENHASH(377594852)
+#define CTX_round TOKENHASH(9350116)
+#define CTX_round_rectangle TOKENHASH(2766896494)
+#define CTX_roundRectangle TOKENHASH(3688082153)
+#define CTX_save TOKENHASH(372838)
+#define CTX_scale TOKENHASH(11274470)
+#define CTX_screen TOKENHASH(950374630)
+#define CTX_setkey TOKENHASH(1688969574)
+#define CTX_shadowBlur TOKENHASH(3119062524)
+#define CTX_shadowColor TOKENHASH(3795289804)
+#define CTX_shadowOffsetX TOKENHASH(4134163333)
+#define CTX_shadowOffsetY TOKENHASH(3519010566)
+#define CTX_smooth_quad_to TOKENHASH(3789701842)
+#define CTX_smoothQuadTo TOKENHASH(4024936051)
+#define CTX_smooth_to TOKENHASH(2307159288)
+#define CTX_smoothTo TOKENHASH(3997790061)
+#define CTX_sourceIn TOKENHASH(3513756343)
+#define CTX_source_in TOKENHASH(3936775584)
+#define CTX_sourceAtop TOKENHASH(3201391080)
+#define CTX_source_atop TOKENHASH(3568635572)
+#define CTX_sourceOut TOKENHASH(4217691207)
+#define CTX_source_out TOKENHASH(2998974401)
+#define CTX_sourceOver TOKENHASH(4071274055)
+#define CTX_sourceTransform TOKENHASH(3608891648)
+#define CTX_source_over TOKENHASH(2221728393)
+#define CTX_square TOKENHASH(373402726)
+#define CTX_start TOKENHASH(43126054)
+#define CTX_start_move TOKENHASH(2528525896)
+#define CTX_start_group TOKENHASH(2643259216)
+#define CTX_startGroup TOKENHASH(4199711715)
+#define CTX_stroke TOKENHASH(359634214)
+#define CTX_text_align TOKENHASH(2641259250)
+#define CTX_textAlign TOKENHASH(4087119491)
+#define CTX_texture TOKENHASH(2603404275)
+#define CTX_text_baseline TOKENHASH(2666328946)
+#define CTX_text_baseline TOKENHASH(2666328946)
+#define CTX_textBaseline TOKENHASH(3671121506)
+#define CTX_fillRect TOKENHASH(2617922007)
+#define CTX_text TOKENHASH(1360232)
+#define CTX_text_direction TOKENHASH(2683352974)
+#define CTX_textDirection TOKENHASH(2303324726)
+#define CTX_text_stroke TOKENHASH(2394879415)
+#define CTX_strokeText TOKENHASH(4077103477)
+#define CTX_strokeRect TOKENHASH(3918462693)
+#define CTX_top TOKENHASH(33768)
+#define CTX_transform TOKENHASH(3717307466)
+#define CTX_translate TOKENHASH(2746303805)
+#define CTX_verLineTo TOKENHASH(2881865279)
+#define CTX_ver_line_to TOKENHASH(3445689061)
+#define CTX_width TOKENHASH(18096750)
+#define CTX_winding TOKENHASH(3743938776)
+#define CTX_x TOKENHASH(48)
+#define CTX_xor TOKENHASH(37872)
+#define CTX_y TOKENHASH(50)
+#define CTX_colorSpace TOKENHASH(2624117287)
+#define CTX_userRGB TOKENHASH(2839509677)
+#define CTX_userCMYK TOKENHASH(4240023559)
+#define CTX_deviceRGB TOKENHASH(3975717407)
+#define CTX_deviceCMYK TOKENHASH(4096729420)
+#define CTX_silver TOKENHASH(1219912294)
+#define CTX_fuchsia TOKENHASH(3356500405)
+#define CTX_gray TOKENHASH(1641614)
+#define CTX_yellow TOKENHASH(1575772530)
+#define CTX_white TOKENHASH(11815470)
+#define CTX_maroon TOKENHASH(972001370)
+#define CTX_magenta TOKENHASH(2383173845)
+#define CTX_blue TOKENHASH(371460)
+#define CTX_green TOKENHASH(29699214)
+#define CTX_red TOKENHASH(8548)
+#define CTX_purple TOKENHASH(361796960)
+#define CTX_olive TOKENHASH(11946782)
+#define CTX_teal TOKENHASH(788840)
+#define CTX_black TOKENHASH(23268100)
+#define CTX_cyan TOKENHASH(921158)
+#define CTX_navy TOKENHASH(1683548)
+#define CTX_lime TOKENHASH(354904)
+#define CTX_aqua TOKENHASH(109634)
+#define CTX_transparent TOKENHASH(3143361910)
+#define CTX_currentColor TOKENHASH(2944012414)
 
 #endif
+
+
+
 #ifndef __CTX_LIBC_H
 #define __CTX_LIBC_H
 
@@ -5795,7 +5853,7 @@ struct _CtxDrawlist
 typedef struct _CtxKeyDbEntry CtxKeyDbEntry;
 struct _CtxKeyDbEntry
 {
-  uint64_t key;
+  uint32_t key;
   float value;
   //union { float f[1]; uint8_t u8[4]; }value;
 };
@@ -5963,6 +6021,9 @@ struct _CtxEvents
   unsigned char    pointer_down[CTX_MAX_DEVICES];
   CtxEvent         drag_event[CTX_MAX_DEVICES];
   CtxList         *idles;
+  CtxList         *idles_to_remove;
+  CtxList         *idles_to_add;
+  int              in_idle_dispatch;
   CtxList         *events; // for ctx_get_event
   int              ctx_get_event_enabled;
   int              idle_id;
@@ -6299,13 +6360,14 @@ struct _CtxCtx
 extern int _ctx_max_threads;
 extern int _ctx_enable_hash_cache;
 void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
+ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len);
 const char *
 ctx_get (Ctx *ctx, const char *key);
 
 int ctx_renderer_is_term (Ctx *ctx);
 Ctx *ctx_new_ctx (int width, int height);
-Ctx *ctx_new_fb (int width, int height, int drm);
+Ctx *ctx_new_fb (int width, int height);
+Ctx *ctx_new_kms (int width, int height);
 Ctx *ctx_new_sdl (int width, int height);
 Ctx *ctx_new_term (int width, int height);
 Ctx *ctx_new_termimg (int width, int height);
@@ -6363,11 +6425,11 @@ static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, f
 
 int ctx_color_model_get_components (CtxColorModel model);
 
-static void ctx_state_set (CtxState *state, uint64_t key, float value);
+static void ctx_state_set (CtxState *state, uint32_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 ();
-static float ctx_state_get (CtxState *state, uint64_t hash);
+static float ctx_state_get (CtxState *state, uint32_t hash);
 
 #if CTX_RASTERIZER
 
@@ -6500,10 +6562,10 @@ CTX_INLINE static void ctx_lerp_RGBA8_split (const uint32_t v0, const uint32_t v
                                              uint32_t *dest_ga, uint32_t *dest_rb)
 {
   const uint32_t cov = dx;
-  const uint32_t si_ga = (v1 & 0xff00ff00);
+  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 di_rb = v0 & 0x00ff00ff;
   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));
@@ -6514,7 +6576,7 @@ CTX_INLINE static uint32_t ctx_lerp_RGBA8_merge (uint32_t di_ga, uint32_t di_rb,
 {
   const uint32_t cov = dx;
   const uint32_t d_rb = si_rb - di_rb;
-  const uint32_t d_ga = (si_ga - di_ga) >> 8;
+  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)));
@@ -6581,11 +6643,38 @@ const char *ctx_texture_init (
 
 #if CTX_TILED
 #if !__COSMOPOLITAN__
-#include <threads.h>
+//#include <threads.h>
 #endif
 #endif
 typedef struct _CtxTiled CtxTiled;
 
+
+typedef struct _EvSource EvSource;
+struct _EvSource
+{
+  void   *priv; /* private storage  */
+
+  /* returns non 0 if there is events waiting */
+  int   (*has_event) (EvSource *ev_source);
+
+  /* get an event, the returned event should be freed by the caller  */
+  char *(*get_event) (EvSource *ev_source);
+
+  /* destroy/unref this instance */
+  void  (*destroy)   (EvSource *ev_source);
+
+  /* get the underlying fd, useful for using select on  */
+  int   (*get_fd)    (EvSource *ev_source);
+
+
+  void  (*set_coord) (EvSource *ev_source, double x, double y);
+  /* set_coord is needed to warp relative cursors into normalized range,
+   * like normal mice/trackpads/nipples - to obey edges and more.
+   */
+
+  /* if this returns non-0 select can be used for non-blocking.. */
+};
+
 struct _CtxTiled
 {
    void (*render)    (void *term, CtxCommand *command);
@@ -6624,6 +6713,10 @@ struct _CtxTiled
    int           pointer_down[3];
 
    CtxCursor     shown_cursor;
+   int          vt_active;
+   EvSource    *evsource[4];
+   int          evsource_count;
+   uint8_t      *fb;
 #if CTX_TILED
    cnd_t  cond;
    mtx_t  mtx;
@@ -6634,15 +6727,15 @@ static void
 _ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
                                       CtxBuffer     *buffer);
 
+int ctx_is_set (Ctx *ctx, uint32_t hash);
+
 #endif
 
 
-uint64_t    thash              (const char *utf8);
-const char *thash_decode       (uint64_t hash);
-uint64_t    ctx_strhash        (const char *str);
+uint32_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);
+int         ctx_get_int        (Ctx *ctx, uint32_t hash);
+int         ctx_get_is_set     (Ctx *ctx, uint32_t hash);
 Ctx        *ctx_new_for_buffer (CtxBuffer *buffer);
 
 #if CTX_IMPLEMENTATION
@@ -6976,7 +7069,10 @@ int    ctx_pcm_queue             (Ctx *ctx, const int8_t *data, int frames);
 #if CTX_ALSA_AUDIO
 #include <alsa/asoundlib.h>
 #endif
-#include <alloca.h>
+
+
+
+//#include <alloca.h>
 
 #endif
 
@@ -7634,70 +7730,91 @@ int ctx_a85len (const char *src, int count)
 #endif
 
 #endif
-#ifndef THASH_H
-#define THASH_H
+#ifndef SQUOZE_H
+#define SQUOZE_H
 
-#if !__COSMOPOLITAN__
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
-#endif
 
-#define THASH_NO_INTERNING   // ctx doesn't make use of thash_decode
+uint32_t squoze6 (const char *utf8);
+uint64_t squoze10 (const char *utf8);
+uint64_t squoze12 (const char *utf8);
+const char *squoze6_decode (uint32_t hash);
+const char *squoze10_decode (uint64_t hash);
+const char *squoze12_decode (uint64_t hash);
 
-#define THASH_ENTER_DIRECT     16
+//#define SQUOZE_NO_INTERNING  // this disables the interning - providing only a hash (and decode for 
non-overflowed hashes)
 
-#define THASH_SPACE            0
-#define THASH_DEC_OFFSET       29
-#define THASH_INC_OFFSET       30
-#define THASH_ENTER_UTF5       31
-#define THASH_START_OFFSET     'l'
-#define THASH_JUMP_OFFSET      27
-#define THASH_MAXLEN           10
+#define SQUOZE_ENTER_SQUEEZE    16
 
-// todo: better whitespace handling for double version
+#define SQUOZE_SPACE            0
+#define SQUOZE_DEC_OFFSET_A     27
+#define SQUOZE_INC_OFFSET_A     28
+#define SQUOZE_DEC_OFFSET_B     29
+#define SQUOZE_INC_OFFSET_B     30
+#define SQUOZE_ENTER_UTF5       31
 
+#define SQUOZE_JUMP_STRIDE      26
+#define SQUOZE_JUMP_OFFSET      19
 
-static inline int thash_new_offset (uint32_t unichar)
-{
-   int offset = unichar % 32;
-   return unichar - offset + 14; // this gives ~85% compression on test corpus
-   return unichar; // this gives 88% compression on test corpus
-}
+static inline uint32_t squoze_utf8_to_unichar (const char *input);
+static inline int      squoze_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
+static inline int      squoze_utf8_len        (const unsigned char first_byte);
 
-static int thash_is_in_range (uint32_t offset, uint32_t unichar)
-{
-  if (unichar == 32)
-    return 1;
-  if (offset - unichar <= 13 ||
-      unichar - offset <= 14)
-      return 1;
-  return 0;
-}
 
-static int thash_is_in_jump_range_dec (uint32_t offset, uint32_t unichar)
+/* returns the base-offset of the segment this unichar belongs to,
+ *
+ * segments are 26 items long and are offset so that the 'a'-'z' is
+ * one segment.
+ */
+static inline int squoze_new_offset (uint32_t unichar)
 {
-  return thash_is_in_range (offset - THASH_JUMP_OFFSET, unichar);
+  uint32_t ret = unichar - (unichar % SQUOZE_JUMP_STRIDE) + SQUOZE_JUMP_OFFSET;
+  if (ret > unichar) ret -= SQUOZE_JUMP_STRIDE;
+  return ret;
 }
 
-static int thash_is_in_jump_range_inc (uint32_t offset, uint32_t unichar)
+static int squoze_needed_jump (uint32_t off, uint32_t unicha)
 {
-  return thash_is_in_range (offset + THASH_JUMP_OFFSET, unichar);
-}
+  int count = 0;
+  int unichar = unicha;
+  int offset = off;
+
+  if (unichar == 32) // space is always in range
+    return 0;
 
-//uint32_t ctx_utf8_to_unichar (const char *input);
-//int ctx_unichar_to_utf8      (uint32_t ch, uint8_t  *dest);
-//int ctx_utf8_len             (const unsigned char first_byte);
+  /* TODO: replace this with direct computation of values instead of loops */
+
+  while (unichar < offset)
+  {
+    offset -= SQUOZE_JUMP_STRIDE;
+    count ++;
+  }
+  if (count)
+  {
+    return -count;
+  }
+  while (unichar - offset >= SQUOZE_JUMP_STRIDE)
+  {
+    offset += SQUOZE_JUMP_STRIDE;
+    count ++;
+  }
+  return count;
+}
 
-static int thash_utf5_length (uint32_t unichar)
+static inline int
+squoze_utf5_length (uint32_t unichar)
 {
   int octets = 0;
-  if (unichar == 0) return 1;
+  if (unichar == 0)
+    return 1;
   while (unichar)
-  {  octets ++;
-     unichar /= 16;
+  {
+    octets ++;
+    unichar /= 16;
   }
   return octets;
 }
@@ -7710,61 +7827,189 @@ typedef struct EncodeUtf5 {
   uint32_t current;
 } EncodeUtf5;
 
-static void thash_encode_utf5 (const char *input, int inlen,
-                               char *output, int *r_outlen)
+static inline uint64_t
+squoze_overflow_mask_for_dim (int squoze_dim)
 {
-  uint32_t offset = THASH_START_OFFSET;
+  return ((uint64_t)1<<(squoze_dim * 5 + 1));
+}
 
-  int is_utf5 = 1;
-  int len = 0;
+static int squoze_compute_cost_utf5 (int offset, int val, int next_val)
+{
+  int cost = 0; 
+  cost += squoze_utf5_length (val);
+  if (next_val)
+  {
+    int no_change_cost = squoze_utf5_length (next_val);
+#if 0 // not hit in test-corpus, it is easier to specify and
+      // port the hash consistently without it
+    offset = squoze_new_offset (val);
+    int change_cost = 1;
+    int needed_jump = squoze_needed_jump (offset, next_val);
 
-  for (int i = 0; i < inlen; i+= ctx_utf8_len (input[i]))
+    if (needed_jump == 0)
+    {
+      change_cost += 1;
+    } else if (needed_jump >= -2 && needed_jump <= 2)
+    {
+      change_cost += 2;
+    }
+    else if (needed_jump >= -10 && needed_jump <= -10)
+    {
+      change_cost += 3;
+    }
+    else
+    {
+      change_cost += 100;
+    }
+
+    if (change_cost < no_change_cost)
+    {
+      cost += change_cost;
+    }
+    else
+#endif
+    {
+      cost += no_change_cost;
+    }
+
+  }
+
+
+
+  return cost;
+}
+
+static int squoze_compute_cost_squeezed (int offset, int val, int next_val)
+{
+  int needed_jump = squoze_needed_jump (offset, val);
+  int cost = 0;
+  if (needed_jump == 0)
   {
-    int val = ctx_utf8_to_unichar(&input[i]);
-    int next_val = ' '; // always in range
-    int next_next_val = ' ';
-    int first_len = ctx_utf8_len (input[i]);
-    if (i + first_len < inlen)
+    cost += 1;
+  }
+  else if (needed_jump >= -2 && needed_jump <= 2)
+  {
+    cost += 2;
+    offset += SQUOZE_JUMP_STRIDE * needed_jump;
+  }
+  else if (needed_jump >= -10 && needed_jump <= 10)
+  {
+    cost += 3;
+    offset += SQUOZE_JUMP_STRIDE * needed_jump;
+  }
+  else
+  {
+    cost += 100; // very expensive, makes the other choice win
+  }
+
+  if (next_val)
+  {
+    int change_cost = 1 + squoze_utf5_length (next_val);
+    int no_change_cost = 0;
+    needed_jump = squoze_needed_jump (offset, next_val);
+
+    if (needed_jump == 0)
     {
-        int next_len = ctx_utf8_to_unichar (&input[i + first_len]);
-        if (i + first_len + next_len < inlen)
-        {
-          next_next_val = ctx_utf8_to_unichar (&input[i + first_len + next_len]);
-        }
+      no_change_cost += 1;
+    }
+    else if (needed_jump >= -2 && needed_jump <= 2)
+    {
+      no_change_cost += 2;
+    }
+    else if (needed_jump >= -10 && needed_jump <= 10)
+    {
+      no_change_cost += 3;
+      offset += SQUOZE_JUMP_STRIDE * needed_jump;
     }
+    else
+    {
+      no_change_cost = change_cost;
+    }
+    if (change_cost < no_change_cost)
+      cost += change_cost;
+    else
+      cost += no_change_cost;
+  }
+
+  return cost;
+}
+
+
+static void squoze5_encode (const char *input, int inlen,
+                            char *output, int *r_outlen,
+                            int permit_squeezed,
+                            int escape_endzero)
+{
+  int offset  = squoze_new_offset('a');
+  int is_utf5 = 1;
+  int len     = 0;
+
+  for (int i = 0; i < inlen; i+= squoze_utf8_len (input[i]))
+  {
+    int val = squoze_utf8_to_unichar (&input[i]);
+    int next_val = 0;
+    int first_len = squoze_utf8_len (input[i]);
+    if (i + first_len < inlen)
+      next_val = squoze_utf8_to_unichar (&input[i+first_len]);
 
     if (is_utf5)
     {
-      int in_range = 
-          thash_is_in_range (offset, val) +
-          thash_is_in_range (offset, next_val) +
-          thash_is_in_range (offset, next_next_val);
-      int change_cost = 4;
-      int no_change_cost = thash_utf5_length (val) + thash_utf5_length (next_val)
-                                                   + thash_utf5_length (next_next_val);
+      int change_cost    = squoze_compute_cost_squeezed (offset, val, next_val);
+      int no_change_cost = squoze_compute_cost_utf5 (offset, val, next_val);
+  
+      if (i != 0)          /* ignore cost of initial 'G' */
+        change_cost += 1;
 
-      if (in_range > 2 && change_cost < no_change_cost)
+      if (permit_squeezed && change_cost <= no_change_cost)
       {
-        output[len++] = THASH_ENTER_DIRECT;
+        output[len++] = SQUOZE_ENTER_SQUEEZE;
         is_utf5 = 0;
       }
-    } else
+    }
+    else
+    {
+      int change_cost    = 1 + squoze_compute_cost_utf5 (offset, val, next_val);
+      int no_change_cost = squoze_compute_cost_squeezed (offset, val, next_val);
+
+      if (change_cost < no_change_cost)
+      {
+        output[len++] = SQUOZE_ENTER_UTF5;
+        is_utf5 = 1;
+      }
+    }
+
+    if (!is_utf5)
     {
-      if (!thash_is_in_range(offset, val))
+      int needed_jump = squoze_needed_jump (offset, val);
+      if (needed_jump)
       {
-        if (thash_is_in_jump_range_dec (offset, val))
+        if (needed_jump >= -2 && needed_jump <= 2)
         {
-            output[len++] = THASH_DEC_OFFSET;
-            offset -= THASH_JUMP_OFFSET;
+          switch (needed_jump)
+          {
+            case -1: output[len++] = SQUOZE_DEC_OFFSET_B; break;
+            case  1: output[len++] = SQUOZE_INC_OFFSET_B; break;
+            case -2: output[len++] = SQUOZE_DEC_OFFSET_A; break;
+            case  2: output[len++] = SQUOZE_INC_OFFSET_A; break;
+          }
+          offset += SQUOZE_JUMP_STRIDE * needed_jump;
         }
-        else if (thash_is_in_jump_range_inc (offset, val))
-        {
-          output[len++] = THASH_INC_OFFSET;
-          offset += THASH_JUMP_OFFSET;
+        else if (needed_jump >= -10 && needed_jump <= 10) {
+              int encoded_val;
+              if (needed_jump < -2)
+                encoded_val = 5 - needed_jump;
+              else
+                encoded_val = needed_jump - 3;
+
+              output[len++] = (encoded_val / 4) + SQUOZE_DEC_OFFSET_A;
+              output[len++] = (encoded_val % 4) + SQUOZE_DEC_OFFSET_A;
+
+              offset += SQUOZE_JUMP_STRIDE * needed_jump;
         }
         else
         {
-          output[len++] = THASH_ENTER_UTF5;
+          assert(0); // should not be reached
+          output[len++] = SQUOZE_ENTER_UTF5;
           is_utf5 = 1;
         }
       }
@@ -7772,244 +8017,356 @@ static void thash_encode_utf5 (const char *input, int inlen,
 
     if (is_utf5)
     {
-          int octets = 0;
-          offset = thash_new_offset (val);
-          while (val)
-          {
-            int oval = val % 16;
-            int last = 0;
-            if (val / 32 == 0) last = 16;
-            output[len+ (octets++)] = oval + last;
-            val /= 16;
-          }
-          for (int j = 0; j < octets/2; j++) // mirror in-place
-          {
-            int tmp = output[len+j];
-            output[len+j] = output[len+octets-1-j];
-            output[len+octets-1-j] = tmp;
-          }
-          len += octets;
-        }
-        else 
+      int octets = 0;
+      offset = squoze_new_offset (val);
+      while (val)
       {
-        if (val == 32)
-        {
-          output[len++] = THASH_SPACE;
-        }
-        else
-        {
-          output[len++]= val-offset+14;
-        }
+        int oval = val % 16;
+        int hi = 16;
+        if (val / 16) hi = 0;
+        output[len+ (octets++)] = oval + hi;
+        val /= 16;
       }
+      for (int j = 0; j < octets/2; j++) // mirror in-place
+      {                                  // TODO refactor to be single pass
+        int tmp = output[len+j];
+        output[len+j] = output[len+octets-1-j];
+        output[len+octets-1-j] = tmp;
+      }
+      len += octets;
+    }
+    else 
+    {
+       if (val == ' ')
+       {
+         output[len++] = SQUOZE_SPACE;
+       }
+       else
+       {
+         output[len++] = val-offset+1;
+       }
+    }
+  }
+
+  if (escape_endzero && len && output[len-1]==0)
+  {
+    if (is_utf5)
+      output[len++] = 16;
+    else
+      output[len++] = SQUOZE_ENTER_UTF5;
   }
-  if (len && output[len-1]==0)
-    output[len++] = 16;
   output[len]=0;
-  *r_outlen = len;
+  if (r_outlen)
+    *r_outlen = len;
 }
 
-static inline uint64_t _thash (const char *utf8)
+static inline uint64_t _squoze (int squoze_dim, const char *utf8)
 {
   char encoded[4096]="";
   int  encoded_len=0;
-  int  wordlen = 0;
-  thash_encode_utf5 (utf8, strlen (utf8), encoded, &encoded_len);
-#if 0
-  Word word = {0};
-  word.utf5 = (encoded[0] != THASH_ENTER_DIRECT);
-  for (int i = !word.utf5; i < encoded_len; i++)
-    word_set_val (&word, wordlen++, encoded[i]);
-  return word.hash;
-#else
+  squoze5_encode (utf8, strlen (utf8), encoded, &encoded_len, 1, 1);
   uint64_t hash = 0;
-  int  utf5 = (encoded[0] != THASH_ENTER_DIRECT);
-  for (int i = !utf5; i < encoded_len; i++)
-  {
-    uint64_t val = encoded[i];
+  int  utf5 = (encoded[0] != SQUOZE_ENTER_SQUEEZE);
+  uint64_t multiplier = ((squoze_dim == 6) ? 0x25bd1e975
+                                           : 0x98173415bd1e975);
+
+  uint64_t overflowed_mask = squoze_overflow_mask_for_dim (squoze_dim);
+  uint64_t all_bits        = overflowed_mask - 1;
 
-    if (wordlen < THASH_MAXLEN)
+  int rshift = (squoze_dim == 6) ? 8 : 16;
+
+
+  if (encoded_len - (!utf5) <= squoze_dim)
+  {
+    for (int i = !utf5; i < encoded_len; i++)
     {
-      hash = hash | (val << (5*wordlen));
-      hash &= (((uint64_t)1<<52)-1);
+      uint64_t val = encoded[i];
+      hash = hash | (val << (5*(i-(!utf5))));
     }
-    else
+    hash <<= 1; // make room for the bit that encodes utf5 or squeeze
+  }
+  else
+  {
+    for (int i = 0; i < encoded_len; i++)
     {
-      hash = hash ^ ((hash << 4) + val);
-      hash &= (((uint64_t)1<<52)-1);
+      uint64_t val = encoded[i];
+      hash = hash ^ val;
+      hash = hash * multiplier;
+      hash = hash & all_bits;
+      hash = hash ^ ((hash >> rshift));
     }
-    wordlen++;
+    hash |= overflowed_mask;
   }
-  hash <<= 1;
-  if (wordlen >= THASH_MAXLEN) 
-    hash |= ((uint64_t)1<<51); // overflowed
-  return hash |  utf5;
-#endif
+  return hash | utf5;
 }
 
-typedef struct _Interned Interned;
+typedef struct _CashInterned CashInterned;
 
-struct _Interned {
+struct _CashInterned {
     uint64_t   hash;
     char      *string;
 };
 
-static Interned *interned = NULL;
+static CashInterned *interned = NULL;
 static int n_interned = 0;
 static int s_interned = 0;
-static int interned_sorted = 1;
 
-static int interned_compare (const void *a, const void *b)
+static int squoze_interned_find (uint64_t hash)
 {
-  const Interned *ia = (Interned*)a;
-  const Interned *ib = (Interned*)b;
-  if (ia->hash < ib->hash ) return -1;
-  else if (ia->hash > ib->hash ) return 1;
+#if 1
+  int min = 0;
+  int max = n_interned - 1;
+  if (max <= 0)
+    return 0;
+  do
+  {
+     int pos = (min + max)/2;
+     if (interned[pos].hash == hash)
+       return pos;
+     else if (min == max - 1)
+       return max;
+     else if (interned[pos].hash < hash)
+       min = pos;
+     else
+       max = pos;
+  } while (min != max);
+  return max;
+#else
+  for (int i = 0; i < n_interned; i++)
+    if (interned[i].hash > hash)
+      return i;
   return 0;
+#endif
 }
 
-uint64_t thash (const char *utf8)
+static inline uint64_t squoze (int squoze_dim, const char *utf8)
 {
-  uint64_t hash = _thash (utf8);
-#ifdef THASH_NO_INTERNING
+  uint64_t hash = _squoze (squoze_dim, utf8);
+#ifdef SQUOZE_NO_INTERNING
   return hash;
 #endif
-  if (hash & ((uint64_t)1<<51)) /* overflowed */
+  uint64_t overflowed_mask = squoze_overflow_mask_for_dim (squoze_dim);
+  if (hash & overflowed_mask)
   {
-    int i;
-    for (i = 0; i < n_interned; i++)
-    {
-      Interned *entry = &interned[i];
-      if (entry->hash == hash)
-        return hash;
-    }
+    int pos = squoze_interned_find (hash);
+    if (interned && interned[pos].hash == hash)
+      return hash;
 
     if (n_interned + 1 >= s_interned)
     {
-       s_interned = (s_interned + 128)*1.5;
-       //fprintf (stderr, "\r%p %i ", interned, s_interned);
-       interned = (Interned*)realloc (interned, s_interned * sizeof (Interned));
+       s_interned = (s_interned + 128)*2;
+       interned = (CashInterned*)realloc (interned, s_interned * sizeof (CashInterned));
     }
 
+    n_interned++;
+#if 1
+    if (n_interned-pos)
+      memmove (&interned[pos+1], &interned[pos], (n_interned-pos) * sizeof (CashInterned));
+    // the memmove is the expensive part of testing for collisions
+    // insertions should be cheaper! at least looking up strings
+    // is cheap
+#else
+    pos = n_interned-1;
+#endif
     {
-      Interned *entry = &interned[n_interned];
+      CashInterned *entry = &interned[pos];
       entry->hash = hash;
       entry->string = strdup (utf8);
     }
-    n_interned++;
-    interned_sorted = 0;
+
   }
   return hash;
 }
-uint64_t ctx_strhash(const char *str) {
-  return thash (str);
+
+uint32_t squoze6 (const char *utf8)
+{
+  return squoze (6, utf8);
 }
 
-typedef struct ThashUtf5Dec {
-  int      is_utf5;
-  int      offset;
-  void    *write_data;
-  uint32_t current;
-  void   (*append_unichar) (uint32_t unichar, void *write_data);
-} ThashUtf5Dec;
+uint64_t squoze10 (const char *utf8)
+{
+  return squoze (10, utf8);
+}
+
+uint64_t squoze12 (const char *utf8)
+{
+  return squoze (12, utf8);
+}
+
+uint32_t ctx_strhash(const char *str) {
+  return squoze (6, str);
+}
+
+typedef struct CashUtf5Dec {
+  int       is_utf5;
+  int       offset;
+  void     *write_data;
+  uint32_t  current;
+  void    (*append_unichar) (uint32_t unichar, void *write_data);
+  int       jumped_amount;
+  int       jump_mode;
+} CashUtf5Dec;
 
-typedef struct ThashUtf5DecDefaultData {
-   uint8_t *buf;
-   int     length;
-} ThashUtf5DecDefaultData;
+typedef struct CashUtf5DecDefaultData {
+  uint8_t *buf;
+  int      length;
+} CashUtf5DecDefaultData;
 
-static void thash_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *write_data)
+static void squoze_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *write_data)
 {
-  ThashUtf5DecDefaultData *data = (ThashUtf5DecDefaultData*)write_data;
-  unsigned char utf8[8]="";
-  utf8[ctx_unichar_to_utf8 (unichar, utf8)]=0;
-  for (int j = 0; utf8[j]; j++)
-    data->buf[data->length++]=utf8[j];
-  data->buf[data->length]=0;
+  CashUtf5DecDefaultData *data = (CashUtf5DecDefaultData*)write_data;
+  int length = squoze_unichar_to_utf8 (unichar, &data->buf[data->length]);
+  data->buf[data->length += length] = 0;
+}
+
+static void squoze_decode_jump (CashUtf5Dec *dec, uint8_t in)
+{
+  dec->offset -= SQUOZE_JUMP_STRIDE * dec->jumped_amount;
+  int jump_len = (dec->jump_mode - SQUOZE_DEC_OFFSET_A) * 4 +
+                 (in - SQUOZE_DEC_OFFSET_A);
+  if (jump_len > 7)
+    jump_len = 5 - jump_len;
+  else
+    jump_len += 3;
+  dec->offset += jump_len * SQUOZE_JUMP_STRIDE;
+  dec->jumped_amount = 0;
 }
 
-static void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
+static void squoze_decode_utf5 (CashUtf5Dec *dec, uint8_t in)
 {
   if (dec->is_utf5)
   {
-      if (in > 16)
+    if (in >= 16)
+    {
+      if (dec->current)
       {
-        if (dec->current)
-        {
-          dec->offset = thash_new_offset (dec->current);
-          dec->append_unichar (dec->current, dec->write_data);
-          dec->current = 0;
-        }
+        dec->offset = squoze_new_offset (dec->current);
+        dec->append_unichar (dec->current, dec->write_data);
+        dec->current = 0;
       }
-      if (in == THASH_ENTER_DIRECT)
-      {
-        if (dec->current)
-        {
-          dec->offset = thash_new_offset (dec->current);
-          dec->append_unichar (dec->current, dec->write_data);
-          dec->current = 0;
-        }
-        dec->is_utf5 = 0;
-      }
-      else
+    }
+    if (in == SQUOZE_ENTER_SQUEEZE)
+    {
+      if (dec->current)
       {
-        dec->current = dec->current * 16 + (in % 16);
+        dec->offset = squoze_new_offset (dec->current);
+        dec->append_unichar (dec->current, dec->write_data);
+        dec->current = 0;
       }
+      dec->is_utf5 = 0;
+    }
+    else
+    {
+      dec->current = dec->current * 16 + (in % 16);
+    }
   }
   else
   {
+    if (dec->jumped_amount)
+    {
       switch (in)
       {
-        case THASH_ENTER_UTF5: dec->is_utf5 = 1;  break;
-        case THASH_SPACE:      dec->append_unichar (' ', dec->write_data); break;
-        case THASH_DEC_OFFSET: dec->offset -= THASH_JUMP_OFFSET; break;
-        case THASH_INC_OFFSET: dec->offset += THASH_JUMP_OFFSET; break;
+        case SQUOZE_DEC_OFFSET_A:
+        case SQUOZE_DEC_OFFSET_B:
+        case SQUOZE_INC_OFFSET_A:
+        case SQUOZE_INC_OFFSET_B:
+          squoze_decode_jump (dec, in);
+          break;
         default:
-          dec->append_unichar (dec->offset + in - 14, dec->write_data);
+          dec->append_unichar (dec->offset + (in - 1), dec->write_data);
+          dec->jumped_amount = 0;
+          dec->jump_mode = 0;
+          break;
       }
+    }
+    else
+    {
+      switch (in)
+      {
+        case SQUOZE_ENTER_UTF5:
+          dec->is_utf5 = 1;
+          dec->jumped_amount = 0;
+          dec->jump_mode = 0;
+          break;
+        case SQUOZE_SPACE: 
+          dec->append_unichar (' ', dec->write_data);
+          dec->jumped_amount = 0;
+          dec->jump_mode = 0;
+          break;
+        case SQUOZE_DEC_OFFSET_A:
+          dec->jumped_amount = -2;
+          dec->jump_mode = in;
+          dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE;
+          break;
+        case SQUOZE_INC_OFFSET_A:
+          dec->jumped_amount = 2;
+          dec->jump_mode = in;
+          dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE;
+          break;
+        case SQUOZE_DEC_OFFSET_B:
+          dec->jumped_amount = -1;
+          dec->jump_mode = in;
+          dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE;
+          break;
+        case SQUOZE_INC_OFFSET_B:
+          dec->jumped_amount = 1;
+          dec->jump_mode = in;
+          dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE;
+          break;
+        default:
+          dec->append_unichar (dec->offset + (in - 1), dec->write_data);
+          dec->jumped_amount = 0;
+          dec->jump_mode = 0;
+      }
+    }
   }
 }
 
-static void thash_decode_utf5_bytes (int is_utf5, 
+static void squoze_decode_utf5_bytes (int is_utf5, 
                         const unsigned char *input, int inlen,
                         char *output, int *r_outlen)
 {
-  ThashUtf5DecDefaultData append_data= {(unsigned char*)output, };
-  ThashUtf5Dec dec = {is_utf5,
-                    THASH_START_OFFSET,
-                    &append_data,
-  0, thash_decode_utf5_append_unichar_as_utf8
-  };
+  CashUtf5DecDefaultData append_data = {(unsigned char*)output, };
+  CashUtf5Dec dec = {is_utf5,
+                     squoze_new_offset('a'),
+                     &append_data,
+                     0,
+                     squoze_decode_utf5_append_unichar_as_utf8,
+                     0
+                    };
   for (int i = 0; i < inlen; i++)
-  {
-    thash_decode_utf5 (&dec, input[i]);
-  }
+    squoze_decode_utf5 (&dec, input[i]);
   if (dec.current)
     dec.append_unichar (dec.current, dec.write_data);
-  if (r_outlen)*r_outlen = append_data.length;
+  if (r_outlen)
+    *r_outlen = append_data.length;
 }
 
-const char *thash_decode (uint64_t hash)
+static const char *squoze_decode_r (int squoze_dim, uint64_t hash, char *ret, int retlen)
 {
-  if (!interned_sorted && interned)
-  {
-    qsort (interned, n_interned, sizeof (Interned), interned_compare);
-    interned_sorted = 1;
-  }
-  if (hash &  ((uint64_t)1<<51))
-  {
+  uint64_t overflowed_mask = ((uint64_t)1<<(squoze_dim * 5 + 1));
 
+  if (hash & overflowed_mask)
+  {
+#if 0
     for (int i = 0; i < n_interned; i++)
     {
-      Interned *entry = &interned[i];
+      CashInterned *entry = &interned[i];
       if (entry->hash == hash)
         return entry->string;
     }
-    return "[missing string]";
+#else
+    int pos = squoze_interned_find (hash);
+    if (!interned || (interned[pos].hash!=hash))
+      return NULL;
+    return interned[pos].string;
+#endif
+    return NULL;
   }
 
-  static char ret[4096]="";
-  uint8_t utf5[40]="";
-  uint64_t tmp = hash & (((uint64_t)1<<51)-1);
+  uint8_t utf5[140]=""; // we newer go really high since there isnt room
+                        // in the integers
+  uint64_t tmp = hash & (overflowed_mask-1);
   int len = 0;
   int is_utf5 = tmp & 1;
   tmp /= 2;
@@ -8019,117 +8376,132 @@ const char *thash_decode (uint64_t hash)
     uint64_t remnant = tmp % 32;
     uint64_t val = remnant;
 
-    if      ( in_utf5 && val == THASH_ENTER_DIRECT) in_utf5 = 0;
-    else if (!in_utf5 && val == THASH_ENTER_UTF5) in_utf5 = 1;
+    if      ( in_utf5 && val == SQUOZE_ENTER_SQUEEZE) in_utf5 = 0;
+    else if (!in_utf5 && val == SQUOZE_ENTER_UTF5) in_utf5 = 1;
 
     utf5[len++] = val;
     tmp -= remnant;
     tmp /= 32;
   }
-  if (in_utf5 && len && utf5[len-1] > 'G')
-  {
-    utf5[len++] = 0;//utf5_alphabet[0]; 
-  }
   utf5[len]=0;
-  int retlen = sizeof (ret);
-  thash_decode_utf5_bytes (is_utf5, utf5, len, ret, &retlen);
-  ret[len]=0;
+  squoze_decode_utf5_bytes (is_utf5, utf5, len, ret, &retlen);
+  //ret[retlen]=0;
   return ret;
 }
 
-#if 0
-
-#include <assert.h>
-#pragma pack(push,1)
-typedef union Word
-{
-  uint64_t hash;
-  struct {
-    unsigned int utf5:1;
-    unsigned int c0:5;
-    unsigned int c1:5;
-    unsigned int c2:5;
-    unsigned int c3:5;
-    unsigned int c4:5;
-    unsigned int c5:5;
-    unsigned int c6:5;
-    unsigned int c7:5;
-    unsigned int c8:5;
-    unsigned int c9:5;
-    unsigned int overflowed:1;
-  };
-} Word;
+/* copy the value as soon as possible, some mitigation is in place
+ * for more than one value in use and cross-thread interactions.
+ */
+static const char *squoze_decode (int squoze_dim, uint64_t hash)
+{
+#define THREAD __thread  // use thread local storage
+  static THREAD int no = 0;
+  static THREAD char ret[8][256];
+  no ++;
+  if (no > 7) no = 0;
+  return squoze_decode_r (squoze_dim, hash, ret[no], 256);
+#undef THREAD
+}
 
-static inline void word_set_val (Word *word, int no, int val)
+const char *squoze6_decode (uint32_t hash)
 {
-#if 0
-  switch(no)
-  {
-     case 0: word->c0 = val; break;
-     case 1: word->c1 = val; break;
-     case 2: word->c2 = val; break;
-     case 3: word->c3 = val; break;
-     case 4: word->c4 = val; break;
-     case 5: word->c5 = val; break;
-     case 6: word->c6 = val; break;
-     case 7: word->c7 = val; break;
-     case 8: word->c8 = val; break;
-     case 9: word->c9 = val; break;
-     default:
-       // for overflow only works when setting all in sequence
-       word->hash = word->hash + ((uint64_t)(val) << (5*no+1));
-       word->overflowed = 1;
-       break;
-  }
-#else
-  word->hash = word->hash | ((uint64_t)(val) << (5*no+1));
-  if (no >= 9) 
-    word->hash |= ((uint64_t)1<<51);
-  word->hash &= (((uint64_t)1<<52)-1);
-#endif
+  return squoze_decode (6, hash);
 }
 
-static inline int word_get_val (Word *word, int no)
+const char *squoze10_decode (uint64_t hash)
 {
-  switch(no)
-  {
-     case 0: return word->c0;break;
-     case 1: return word->c1;break;
-     case 2: return word->c2;break;
-     case 3: return word->c3;break;
-     case 4: return word->c4;break;
-     case 5: return word->c5;break;
-     case 6: return word->c6;break;
-     case 7: return word->c7;break;
-     case 8: return word->c8;break;
-     case 9: return word->c9;break;
-  }
+  return squoze_decode (10, hash);
 }
 
-static inline int word_get_length (Word *word)
+const char *squoze12_decode (uint64_t hash)
 {
-   int len = 0;
-   if (word->c0) len ++; else return len;
-   if (word->c1) len ++; else return len;
-   if (word->c2) len ++; else return len;
-   if (word->c3) len ++; else return len;
-   if (word->c4) len ++; else return len;
-   if (word->c5) len ++; else return len;
-   if (word->c6) len ++; else return len;
-   if (word->c7) len ++; else return len;
-   if (word->c8) len ++; else return len;
-   if (word->c9) len ++; else return len;
-   return len;
+  return squoze_decode (12, hash);
 }
 
+static inline uint32_t
+squoze_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;
+}
+static inline int
+squoze_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;
+}
 
-static Word *word_append_unichar (Word *word, uint32_t unichar)
+static inline int
+squoze_utf8_len (const unsigned char first_byte)
 {
-  //word_set_char (word, word_get_length (word), unichar);
-  // append unichar - possibly advancing.
-  return word;
+  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;
 }
-#endif
 
 #endif
 /* atty - audio interface and driver for terminals
@@ -8407,24 +8779,24 @@ ctx_u8 (CtxCode code,
         uint8_t e, uint8_t f, uint8_t g, uint8_t h);
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = {cmd};\
-  ctx_process (ctx, &command);}while(0) \
+  CtxEntry commands[4] = {{cmd}};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
-#define CTX_PROCESS_F(cmd, x, y) do {\
-  CtxEntry command = ctx_f(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
+#define CTX_PROCESS_F(cmd,x,y) do {\
+  CtxEntry commands[4] = {ctx_f(cmd,x,y),};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
-#define CTX_PROCESS_F1(cmd, x) do {\
-  CtxEntry command = ctx_f(cmd, x, 0);\
-  ctx_process (ctx, &command);}while(0)
+#define CTX_PROCESS_F1(cmd,x) do {\
+  CtxEntry commands[4] = {ctx_f(cmd,x,0),};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
 #define CTX_PROCESS_U32(cmd, x, y) do {\
-  CtxEntry command = ctx_u32(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
+  CtxEntry commands[4] = {ctx_u32(cmd, x, y)};\
+  ctx_process (ctx, &commands[0]);}while(0)
 
 #define CTX_PROCESS_U8(cmd, x) do {\
-  CtxEntry command = ctx_u8(cmd, x,0,0,0,0,0,0,0);\
-  ctx_process (ctx, &command);}while(0)
+  CtxEntry commands[4] = {ctx_u8(cmd, x,0,0,0,0,0,0,0)};\
+  ctx_process (ctx, &commands[0]);}while(0)
 
 
 #if CTX_BITPACK_PACKER
@@ -8553,9 +8925,10 @@ _ctx_file_set_contents (const char     *path,
 }
 
 static int
-__ctx_file_get_contents (const char     *path,
-                        unsigned char **contents,
-                        long           *length)
+___ctx_file_get_contents (const char     *path,
+                          unsigned char **contents,
+                          long           *length,
+                          long            max_len)
 {
   FILE *file;
   long  size;
@@ -8566,6 +8939,12 @@ __ctx_file_get_contents (const char     *path,
     { return -1; }
   fseek (file, 0, SEEK_END);
   size = remaining = ftell (file);
+
+  if (size > max_len)
+  {
+     size = remaining = max_len;
+  }
+
   if (length)
     { *length =size; }
   rewind (file);
@@ -8588,6 +8967,14 @@ __ctx_file_get_contents (const char     *path,
   return 0;
 }
 
+static int
+__ctx_file_get_contents (const char     *path,
+                        unsigned char **contents,
+                        long           *length)
+{
+  return ___ctx_file_get_contents (path, contents, length, 1024*1024*1024);
+}
+
 #if !__COSMOPOLITAN__
 #include <limits.h>
 #endif
@@ -8948,7 +9335,7 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
       static CtxEntry sbuf[CTX_MAX_JOURNAL_SIZE];
       drawlist->entries = &sbuf[0];
       drawlist->size = CTX_MAX_JOURNAL_SIZE;
-      ctx_drawlist_compact (drawlist);
+      if(0)ctx_drawlist_compact (drawlist);
     }
 #else
   int new_size = desired_size;
@@ -9176,11 +9563,13 @@ int ctx_append_drawlist (Ctx *ctx, void *data, int length)
 int ctx_set_drawlist (Ctx *ctx, void *data, int length)
 {
   CtxDrawlist *drawlist = &ctx->drawlist;
-  ctx->drawlist.count = 0;
   if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
     {
       return -1;
     }
+  ctx->drawlist.count = 0;
+  if (!data || length == 0)
+    return 0;
   if (CTX_UNLIKELY(length % 9)) return -1;
   ctx_drawlist_resize (drawlist, length/9);
   memcpy (drawlist->entries, data, length);
@@ -9337,24 +9726,24 @@ ctx_u8 (CtxCode code,
 }
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = {cmd};\
-  ctx_process (ctx, &command);}while(0) \
+  CtxEntry commands[4] = {{cmd}};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
-#define CTX_PROCESS_F(cmd, x, y) do {\
-  CtxEntry command = ctx_f(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
+#define CTX_PROCESS_F(cmd,x,y) do {\
+  CtxEntry commands[4] = {ctx_f(cmd,x,y),};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
-#define CTX_PROCESS_F1(cmd, x) do {\
-  CtxEntry command = ctx_f(cmd, x, 0);\
-  ctx_process (ctx, &command);}while(0)
+#define CTX_PROCESS_F1(cmd,x) do {\
+  CtxEntry commands[4] = {ctx_f(cmd,x,0),};\
+  ctx_process (ctx, &commands[0]);}while(0) \
 
 #define CTX_PROCESS_U32(cmd, x, y) do {\
-  CtxEntry command = ctx_u32(cmd, x, y);\
-  ctx_process (ctx, &command);}while(0)
+  CtxEntry commands[4] = {ctx_u32(cmd, x, y)};\
+  ctx_process (ctx, &commands[0]);}while(0)
 
 #define CTX_PROCESS_U8(cmd, x) do {\
-  CtxEntry command = ctx_u8(cmd, x,0,0,0,0,0,0,0);\
-  ctx_process (ctx, &command);}while(0)
+  CtxEntry commands[4] = {ctx_u8(cmd, x,0,0,0,0,0,0,0)};\
+  ctx_process (ctx, &commands[0]);}while(0)
 
 
 static void
@@ -10863,7 +11252,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);
+  uint32_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]);
@@ -11080,7 +11469,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 
 
-static float ctx_state_get (CtxState *state, uint64_t hash)
+static float ctx_state_get (CtxState *state, uint32_t hash)
 {
   for (int i = state->gstate.keydb_pos-1; i>=0; i--)
     {
@@ -11090,14 +11479,14 @@ static float ctx_state_get (CtxState *state, uint64_t hash)
   return -0.0;
 }
 
-static void ctx_state_set (CtxState *state, uint64_t key, float value)
+static void ctx_state_set (CtxState *state, uint32_t key, float value)
 {
   if (key != CTX_new_state)
     {
       if (ctx_state_get (state, key) == value)
         { return; }
       for (int i = state->gstate.keydb_pos-1;
-           state->keydb[i].key != CTX_new_state && i >=0;
+           i >= 0 && state->keydb[i].key != CTX_new_state;
            i--)
         {
           if (state->keydb[i].key == key)
@@ -11138,7 +11527,7 @@ static float ctx_string_index_to_float (int index)
   return CTX_KEYDB_STRING_START + index;
 }
 
-static void *ctx_state_get_blob (CtxState *state, uint64_t key)
+static void *ctx_state_get_blob (CtxState *state, uint32_t key)
 {
   float stored = ctx_state_get (state, key);
   int idx = ctx_float_to_string_index (stored);
@@ -11152,7 +11541,7 @@ static void *ctx_state_get_blob (CtxState *state, uint64_t key)
   return NULL;
 }
 
-static const char *ctx_state_get_string (CtxState *state, uint64_t key)
+static const char *ctx_state_get_string (CtxState *state, uint32_t key)
 {
   const char *ret = (char*)ctx_state_get_blob (state, key);
   if (ret && ret[0] == 127)
@@ -11161,7 +11550,7 @@ static const char *ctx_state_get_string (CtxState *state, uint64_t key)
 }
 
 
-static void ctx_state_set_blob (CtxState *state, uint64_t key, uint8_t *data, int len)
+static void ctx_state_set_blob (CtxState *state, uint32_t key, uint8_t *data, int len)
 {
   int idx = state->gstate.stringpool_pos;
 
@@ -11185,7 +11574,7 @@ static void ctx_state_set_blob (CtxState *state, uint64_t key, uint8_t *data, in
   ctx_state_set (state, key, ctx_string_index_to_float (idx));
 }
 
-static void ctx_state_set_string (CtxState *state, uint64_t key, const char *string)
+static void ctx_state_set_string (CtxState *state, uint32_t key, const char *string)
 {
   float old_val = ctx_state_get (state, key);
   int   old_idx = ctx_float_to_string_index (old_val);
@@ -11212,7 +11601,7 @@ static void ctx_state_set_string (CtxState *state, uint64_t key, const char *str
   ctx_state_set_blob (state, key, (uint8_t*)string, strlen(string));
 }
 
-static int ctx_state_get_color (CtxState *state, uint64_t key, CtxColor *color)
+static int ctx_state_get_color (CtxState *state, uint32_t key, CtxColor *color)
 {
   CtxColor *stored = (CtxColor*)ctx_state_get_blob (state, key);
   if (stored)
@@ -11226,7 +11615,7 @@ static int ctx_state_get_color (CtxState *state, uint64_t key, CtxColor *color)
   return -1;
 }
 
-static void ctx_state_set_color (CtxState *state, uint64_t key, CtxColor *color)
+static void ctx_state_set_color (CtxState *state, uint32_t key, CtxColor *color)
 {
   CtxColor mod_color;
   CtxColor old_color;
@@ -11240,39 +11629,39 @@ static void ctx_state_set_color (CtxState *state, uint64_t key, CtxColor *color)
   ctx_state_set_blob (state, key, (uint8_t*)&mod_color, sizeof (CtxColor));
 }
 
-const char *ctx_get_string (Ctx *ctx, uint64_t hash)
+const char *ctx_get_string (Ctx *ctx, uint32_t hash)
 {
   return ctx_state_get_string (&ctx->state, hash);
 }
-float ctx_get_float (Ctx *ctx, uint64_t hash)
+float ctx_get_float (Ctx *ctx, uint32_t hash)
 {
   return ctx_state_get (&ctx->state, hash);
 }
-int ctx_get_int (Ctx *ctx, uint64_t hash)
+int ctx_get_int (Ctx *ctx, uint32_t hash)
 {
   return ctx_state_get (&ctx->state, hash);
 }
-void ctx_set_float (Ctx *ctx, uint64_t hash, float value)
+void ctx_set_float (Ctx *ctx, uint32_t hash, float value)
 {
   ctx_state_set (&ctx->state, hash, value);
 }
-void ctx_set_string (Ctx *ctx, uint64_t hash, const char *value)
+void ctx_set_string (Ctx *ctx, uint32_t hash, const char *value)
 {
   ctx_state_set_string (&ctx->state, hash, value);
 }
-void ctx_set_color (Ctx *ctx, uint64_t hash, CtxColor *color)
+void ctx_set_color (Ctx *ctx, uint32_t hash, CtxColor *color)
 {
   ctx_state_set_color (&ctx->state, hash, color);
 }
-int  ctx_get_color (Ctx *ctx, uint64_t hash, CtxColor *color)
+int  ctx_get_color (Ctx *ctx, uint32_t hash, CtxColor *color)
 {
   return ctx_state_get_color (&ctx->state, hash, color);
 }
-int ctx_is_set (Ctx *ctx, uint64_t hash)
+int ctx_is_set (Ctx *ctx, uint32_t hash)
 {
   return ctx_get_float (ctx, hash) != -0.0f;
 }
-int ctx_is_set_now (Ctx *ctx, uint64_t hash)
+int ctx_is_set_now (Ctx *ctx, uint32_t hash)
 {
   return ctx_is_set (ctx, hash);
 }
@@ -11301,7 +11690,7 @@ ctx_RGBA8_associate_alpha (uint8_t *u8)
 {
 #if 1
   uint32_t val = *((uint32_t*)(u8));
-  uint8_t a = u8[3];
+  uint32_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);
@@ -12317,8 +12706,6 @@ ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
   }
 }
 
-
-
 static void
 ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
                                    float x,
@@ -12337,8 +12724,16 @@ ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
 
   if (dy == 0.0f && dx > 0.0f)
   {
+    if (!(y >= 0 && y < bheight))
+    {
+      uint32_t *dst = (uint32_t*)rgba;
+      for (i = 0 ; i < count; i++)
+        *dst++ = 0;
+      return;
+    }
+
     if ((dx > 0.99f && dx < 1.01f && 
-         ox < 0.01 && oy < 0.01) || y < 0 || y > bheight-1)
+         ox < 0.01 && oy < 0.01))
     {
       /* TODO: this could have been rigged up in composite_setup */
       ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer,
@@ -12382,7 +12777,7 @@ ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
 
   if (xi_delta == 65536 && u < bwidth -1)
   {
-    int du = (xi >> 8);
+    int du = (xi >> 8) & 0xff;
 
     src0 = data + u;
     src1 = ndata + u;
@@ -12430,7 +12825,7 @@ ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
     }
     loaded = u;
     ((uint32_t*)(&rgba[0]))[0] = 
-      ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8));
+      ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, ((xi>>8)&0xff));
     xi += xi_delta;
     rgba += 4;
     u = xi >> 16;
@@ -12438,7 +12833,7 @@ ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
   }
 
   }
-  else
+  else // //
   {
     uint32_t *data = ((uint32_t*)buffer->data);
     for (i= 0; i < count; i ++)
@@ -12590,13 +12985,15 @@ ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer,
 
     uint32_t u_offset = bheight * bwidth;
     uint32_t v_offset = u_offset + bheight_div_2 * bwidth_div_2;
+
     if (rasterizer->swap_red_green)
     {
       v_offset = bheight * bwidth;
       u_offset = v_offset + bheight_div_2 * bwidth_div_2;
     }
 
-    int ix = x * 65536;
+    // XXX this is incorrect- but fixes some bug!
+    int ix = 65536;//x * 65536;
     int iy = y * 65536;
 
     int ideltax = dx * 65536;
@@ -13244,8 +13641,8 @@ ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
   {
      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 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));
@@ -16467,7 +16864,7 @@ static inline void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, f
   float ty = y;
   //float ox = rasterizer->x;
   //float oy = rasterizer->y;
-  if ((rasterizer->uses_transforms))
+  if (rasterizer->uses_transforms)
     {
       _ctx_user_to_device (rasterizer->state, &tx, &ty);
     }
@@ -16483,7 +16880,7 @@ static inline void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, f
   if (CTX_UNLIKELY(rasterizer->has_prev<=0))
     {
 #if 0
-      if ((rasterizer->uses_transforms))
+      if (rasterizer->uses_transforms)
       {
         // storing transformed would save some processing for a tiny
         // amount of runtime RAM XXX
@@ -16804,7 +17201,7 @@ static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
     {
       CtxSegment *segment = segments + edges[i];
       int edge_end = segment->data.s16[3]-1;
-      if ((edge_end < scanline))
+      if (edge_end < scanline)
         {
 
           int dx_dy = abs(segment->delta);
@@ -16926,11 +17323,11 @@ inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int app
          (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))
+      if (rasterizer->active_edges < CTX_MAX_EDGES-2 &&
+          maxy >= scanline)
         {
           int dy = (entries[edge_pos].data.s16[3] - 1 - miny);
-          if ((dy))
+          if (dy)
             {
               int yd = scanline - miny;
               int no = rasterizer->active_edges;
@@ -16950,13 +17347,13 @@ inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int app
                 rasterizer->needs_aa15 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15);
               }
 
-              if ((miny > scanline) )
+              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))
+                  if (rasterizer->pending_edges < CTX_MAX_PENDING-1)
                   {
                     edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
                     rasterizer->edges[no];
@@ -17072,12 +17469,12 @@ ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
           int first     = graystart >> 8;
           int last      = grayend   >> 8;
 
-          if ((first < minx))
+          if (first < minx)
           { 
             first = minx;
             graystart=0;
           }
-          if ((last > maxx))
+          if (last > maxx)
           {
             last = maxx;
             grayend=255;
@@ -17127,12 +17524,12 @@ ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer,
           int first     = graystart >> 8;
           int last      = grayend   >> 8;
 
-          if ((first < minx))
+          if (first < minx)
           { 
             first = minx;
             graystart=0;
           }
-          if ((last > maxx))
+          if (last > maxx)
           {
             last = maxx;
             grayend=255;
@@ -17256,12 +17653,12 @@ ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer,
           int first     = graystart >> 8;
           int last      = grayend   >> 8;
 
-          if ((first < minx))
+          if (first < minx)
           { 
             first = minx;
             graystart=0;
           }
-          if ((last > maxx))
+          if (last > maxx)
           {
             last = maxx;
             grayend=255;
@@ -17986,7 +18383,7 @@ ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, const int fill_rule
           | (rasterizer->active_edges + rasterizer->pending_edges == rasterizer->ending_edges)
           );
 
-    if ((needs_full_aa))
+    if (needs_full_aa)
     {
         int increment = CTX_FULL_AA/real_aa;
         memset (coverage, 0, coverage_size);
@@ -18209,7 +18606,7 @@ ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
     if (comp_op == ctx_RGBA8_source_copy_normal_color)
     {
       uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
+      if (width == 1)
       {
         for (int y = y0; y < y1; y++)
         {
@@ -18235,7 +18632,7 @@ ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
       uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
       uint32_t si_a  = rasterizer->color[3];
 
-      if ((width == 1))
+      if (width == 1)
       {
         for (int y = y0; y < y1; y++)
         {
@@ -18289,7 +18686,7 @@ ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
   if (comp_op == ctx_RGBA8_source_copy_normal_color)
     {
       uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
+      if (width == 1)
       {
         for (int y = y0; y < y1; y++)
         {
@@ -18316,7 +18713,7 @@ ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
     else if (comp_op == ctx_RGBA8_source_over_normal_color)
     {
       uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
+      if (width == 1)
       {
         for (int y = y0; y < y1; y++)
         {
@@ -18364,14 +18761,14 @@ static inline float ctx_fmod1f (float val)
 static void
 ctx_rasterizer_fill (CtxRasterizer *rasterizer)
 {
-  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
+  int preserved_count = rasterizer->preserve?rasterizer->edge_list.count:1;
   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;
 
-  CtxSegment temp[count]; /* copy of already built up path's poly line
+  CtxSegment temp[preserved_count]; /* copy of already built up path's poly line
                           XXX - by building a large enough path
                           the stack can be smashed!
                          */
@@ -18431,7 +18828,7 @@ ctx_rasterizer_fill (CtxRasterizer *rasterizer)
       CtxSegment *entry2 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[2];
       CtxSegment *entry3 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[3];
 
-      if ((!rasterizer->state->gstate.clipped != 0) &
+      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]) &
@@ -18698,7 +19095,7 @@ done:
   if (CTX_UNLIKELY(rasterizer->preserve))
     {
       memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
+      rasterizer->edge_list.count = preserved_count;
     }
 #if CTX_ENABLE_SHADOW_BLUR
   if (CTX_UNLIKELY(rasterizer->in_shadow))
@@ -18764,16 +19161,12 @@ ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
   if (rasterizer->term_glyphs)
   {
     float tx = 0;
-    float ty = rasterizer->state->gstate.font_size;
-    float txb = 0;
-    float tyb = 0;
+    font_size = rasterizer->state->gstate.font_size;
 
     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;
+    _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size);
   }
   if (rasterizer->term_glyphs && !stroke &&
       fabs (font_size - ch) < 0.5)
@@ -18796,7 +19189,6 @@ ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
   _ctx_glyph (rasterizer->ctx, unichar, stroke);
 }
 
-
 static void
 _ctx_text (Ctx        *ctx,
            const char *string,
@@ -18809,10 +19201,9 @@ ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke)
   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;
+    float tx = 0;
+    font_size = rasterizer->state->gstate.font_size;
+    _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size);
   }
   int   ch = ctx_term_get_cell_height (rasterizer->ctx);
   int   cw = ctx_term_get_cell_width (rasterizer->ctx);
@@ -18896,7 +19287,6 @@ ctx_rasterizer_arc (CtxRasterizer *rasterizer,
        ( (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
@@ -19001,6 +19391,7 @@ ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
   rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
 }
 
+#if 0
 static void
 ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
 {
@@ -19067,18 +19458,21 @@ foo:
     }
   ctx_rasterizer_reset (rasterizer);
 }
+#endif
 
 static void
 ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
 {
   CtxGState *gstate = &rasterizer->state->gstate;
   CtxSource source_backup;
+  int count = rasterizer->edge_list.count;
+  if (count <= 0)
+    return;
   if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL)
   {
     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;
@@ -20508,7 +20902,7 @@ ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
 CtxAntialias ctx_get_antialias (Ctx *ctx)
 {
 #if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  if (ctx_renderer_is_tiled (ctx))
   {
      CtxTiled *fb = (CtxTiled*)(ctx->renderer);
      return fb->antialias;
@@ -20523,7 +20917,6 @@ CtxAntialias ctx_get_antialias (Ctx *ctx)
     //case 5: return CTX_ANTIALIAS_GOOD;
     default:
     case 15: return CTX_ANTIALIAS_DEFAULT;
-    case 17: return CTX_ANTIALIAS_BEST;
   }
 }
 
@@ -20536,7 +20929,6 @@ static int _ctx_antialias_to_aa (CtxAntialias antialias)
     case CTX_ANTIALIAS_GOOD: return 5;
     default:
     case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA;
-    case CTX_ANTIALIAS_BEST: return 17;
   }
 }
 
@@ -20544,7 +20936,7 @@ void
 ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
 {
 #if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
+  if (ctx_renderer_is_tiled (ctx))
   {
      CtxTiled *fb = (CtxTiled*)(ctx->renderer);
      fb->antialias = antialias;
@@ -21138,7 +21530,6 @@ ctx_utf8_to_unichar (const char *input)
 #if CTX_RASTERIZER
 
 
-
 static int
 ctx_rect_intersect (const CtxIntRectangle *a, const CtxIntRectangle *b)
 {
@@ -21174,7 +21565,8 @@ _ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
     }
 }
 
-static int ctx_str_count_lines (const char *str)
+static int
+ctx_str_count_lines (const char *str)
 {
   int count = 0;
   for (const char *p = str; *p; p++)
@@ -21232,9 +21624,9 @@ ctx_hasher_process (void *user_data, CtxCommand *command)
           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);
+#if 1
+        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);
@@ -21262,9 +21654,9 @@ ctx_hasher_process (void *user_data, CtxCommand *command)
           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
+#if 1
           ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+    //    ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
 #endif
           ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
           ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
@@ -21300,8 +21692,8 @@ ctx_hasher_process (void *user_data, CtxCommand *command)
           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));
+#if 0
           ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
 #endif
           ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
@@ -21577,6 +21969,7 @@ Ctx *ctx_hasher_new (int width, int height, int cols, int 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;
@@ -21598,6 +21991,7 @@ uint8_t *ctx_hasher_get_hash (Ctx *ctx, int col, int row)
 #include <sys/ioctl.h>
 #endif
 
+#if 0
 int ctx_terminal_width (void)
 {
   char buf[1024];
@@ -21694,6 +22088,26 @@ int ctx_terminal_height (void)
   }
   return 0;
 }
+#else
+
+
+int ctx_terminal_width (void)
+{
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 640;
+  return ws.ws_xpixel;
+} 
+
+int ctx_terminal_height (void)
+{
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 450;
+  return ws.ws_ypixel;
+}
+
+#endif
 
 int ctx_terminal_cols (void)
 {
@@ -21739,7 +22153,7 @@ int ctx_terminal_rows (void)
 #endif
 
 static int  size_changed = 0;       /* XXX: global state */
-static int  signal_installed = 0;   /* XXX: global state */
+static int  ctx_term_signal_installed = 0;   /* XXX: global state */
 
 static const char *mouse_modes[]=
 {TERMINAL_MOUSE_OFF,
@@ -21815,10 +22229,10 @@ static const NcKeyCode keycodes[]={
   {"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",  "\033[10~"},
+  {"F2",        "F2",  "\033[11~"},
+  {"F3",        "F3",  "\033[12~"},
+  {"F4",        "F4",  "\033[13~"},
   {"F1",        "F1",  "\033OP"},
   {"F2",        "F2",  "\033OQ"},
   {"F3",        "F3",  "\033OR"},
@@ -22146,10 +22560,10 @@ const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
   if (x) *x = -1;
   if (y) *y = -1;
 
-  if (!signal_installed)
+  if (!ctx_term_signal_installed)
     {
       _nc_raw ();
-      signal_installed = 1;
+      ctx_term_signal_installed = 1;
       signal (SIGWINCH, nc_resize_term);
     }
   if (mouse_mode) // XXX too often to do it all the time!
@@ -22360,6 +22774,14 @@ int ctx_nct_consume_events (Ctx *ctx)
       mrg_set_size (mrg, width, height);
       mrg_queue_draw (mrg, NULL);
 #endif
+      //if (ctx_renderer_is_ctx (ctx))
+#if 0
+      {
+        int width = ctx_terminal_width ();
+        int height = ctx_terminal_height ();
+        ctx_set_size (ctx, width, height);
+      }
+#endif
 
     }
     else
@@ -22388,10 +22810,10 @@ const char *ctx_native_get_event (Ctx *n, int timeoutms)
   static unsigned char buf[256];
   int length;
 
-  if (!signal_installed)
+  if (!ctx_term_signal_installed)
     {
       _nc_raw ();
-      signal_installed = 1;
+      ctx_term_signal_installed = 1;
       signal (SIGWINCH, nc_resize_term);
     }
 //if (mouse_mode) // XXX too often to do it all the time!
@@ -22579,9 +23001,11 @@ void ctx_list_backends(void)
 #if CTX_SDL
     fprintf (stderr, " SDL");
 #endif
+#if CTX_KMS
+    fprintf (stderr, " kms");
+#endif
 #if CTX_FB
     fprintf (stderr, " fb");
-    fprintf (stderr, " drm");
 #endif
     fprintf (stderr, " term");
     fprintf (stderr, " termimg");
@@ -22684,18 +23108,20 @@ Ctx *ctx_new_ui (int width, int height)
   }
 #endif
 
-#if CTX_FB
+#if CTX_KMS
   if (!ret && !getenv ("DISPLAY"))
   {
-    if ((backend==NULL) || (!strcmp (backend, "drm")))
-    ret = ctx_new_fb (width, height, 1);
+    if ((backend==NULL) || (!strcmp (backend, "kms")))
+      ret = ctx_new_kms (width, height);
+  }
+#endif
 
-    if (!ret)
+#if CTX_FB
+  if (!ret && !getenv ("DISPLAY"))
     {
       if ((backend==NULL) || (!strcmp (backend, "fb")))
-        ret = ctx_new_fb (width, height, 0);
+        ret = ctx_new_fb (width, height);
     }
-  }
 #endif
 
 #if CTX_RASTERIZER
@@ -22740,6 +23166,14 @@ void ctx_set_size (Ctx *ctx, int width, int height)
     ctx->events.width = width;
     ctx->events.height = height;
     _ctx_resized (ctx, width, height, 0);
+#if 1
+    if (ctx_renderer_is_ctx (ctx))
+    {
+      CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+      ctxctx->width = width;
+      ctxctx->height= height;
+    }
+#endif
   }
 #endif
 }
@@ -22824,37 +23258,88 @@ 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;
+  unsigned long ticks = ctx_ticks ();
+  long tick_delta = (prev_ticks == 0) ? 0 : ticks - prev_ticks;
   prev_ticks = ticks;
 
+
   if (!ctx->events.idles)
   {
     return;
   }
+
+  ctx->events.in_idle_dispatch=1;
+
   for (l = ctx->events.idles; l; l = l->next)
   {
     CtxIdleCb *item = l->data;
 
+    long rem = item->ticks_remaining;
     if (item->ticks_remaining >= 0)
-      item->ticks_remaining -= tick_delta;
+    {
+      rem -= tick_delta;
+
+      item->ticks_remaining -= tick_delta / 100;
 
-    if (item->ticks_remaining < 0)
+    if (rem < 0)
     {
+      int to_be_removed = 0;
+      for (CtxList *l2 = ctx->events.idles_to_remove; l2; l2=l2->next)
+      {
+        CtxIdleCb *item2 = l2->data;
+        if (item2 == item) to_be_removed = 1;
+      }
+      
+      if (!to_be_removed)
+      {
       if (item->cb (ctx, item->idle_data) == 0)
-        ctx_list_prepend (&to_remove, item);
+      {
+        ctx_list_prepend (&ctx->events.idles_to_remove, item);
+      }
       else
         item->ticks_remaining = item->ticks_full;
+      }
+    }
+    else
+        item->ticks_remaining = rem;
+    }
+    else
+    {
+      int to_be_removed = 0;
+      for (CtxList *l2 = ctx->events.idles_to_remove; l2; l2=l2->next)
+      {
+        CtxIdleCb *item2 = l2->data;
+        if (item2 == item) to_be_removed = 1;
+      }
+      
+      if (!to_be_removed)
+      {
+        if (item->cb (ctx, item->idle_data) == 0)
+        {
+          ctx_list_prepend (&ctx->events.idles_to_remove, item);
+        }
+        else
+          item->ticks_remaining = item->ticks_full;
+      }
     }
   }
-  for (l = to_remove; l; l = l->next)
+
+  while (ctx->events.idles_to_add)
   {
-    CtxIdleCb *item = l->data;
+    CtxIdleCb *item = ctx->events.idles_to_add->data;
+    ctx_list_prepend (&ctx->events.idles, item);
+    ctx_list_remove (&ctx->events.idles_to_add, item);
+  }
+
+  while (ctx->events.idles_to_remove)
+  {
+    CtxIdleCb *item = ctx->events.idles_to_remove->data;
     if (item->destroy_notify)
       item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
+    ctx_list_remove (&ctx->events.idles, item);
+    ctx_list_remove (&ctx->events.idles_to_remove, item);
   }
+  ctx->events.in_idle_dispatch=0;
 }
 
 
@@ -22937,7 +23422,7 @@ static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2)
     }
   if (!handled)
   for (i = events->n_bindings-1; i>=0; i--)
-    if (!strcmp (events->bindings[i].nick, "unhandled"))
+    if (!strcmp (events->bindings[i].nick, "any"))
     {
       if (events->bindings[i].cb)
       {
@@ -22957,24 +23442,32 @@ CtxBinding *ctx_get_bindings (Ctx *ctx)
 void ctx_remove_idle (Ctx *ctx, int handle)
 {
   CtxList *l;
-  CtxList *to_remove = NULL;
+  //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);
+    {
+      ctx_list_prepend (&ctx->events.idles_to_remove, item);
+    }
   }
-  for (l = to_remove; l; l = l->next)
+
+  if (ctx->events.in_idle_dispatch)
+    return;
+
+  while (ctx->events.idles_to_remove)
   {
-    CtxIdleCb *item = l->data;
+    CtxIdleCb *item = ctx->events.idles_to_remove->data;
     if (item->destroy_notify)
       item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
+    ctx_list_remove (&ctx->events.idles, item);
+    ctx_list_remove (&ctx->events.idles_to_remove, item);
   }
 }
 
@@ -22989,6 +23482,9 @@ int ctx_add_timeout_full (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_
   item->ticks_remaining = ms * 1000;
   item->destroy_notify  = destroy_notify;
   item->destroy_data    = destroy_data;
+  if (ctx->events.in_idle_dispatch)
+  ctx_list_append (&ctx->events.idles_to_add, item);
+  else
   ctx_list_append (&ctx->events.idles, item);
   return item->id;
 }
@@ -23528,6 +24024,11 @@ int ctx_fb_events = 0;
 int ctx_fb_consume_events (Ctx *ctx);
 #endif
 
+#if CTX_KMS
+int ctx_kms_events = 0;
+int ctx_kms_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);
@@ -23545,6 +24046,11 @@ void ctx_consume_events (Ctx *ctx)
   if (ctx_fb_events)
     ctx_fb_consume_events (ctx);
   else
+#endif
+#if CTX_KMS
+  if (ctx_kms_events)
+    ctx_kms_consume_events (ctx);
+  else
 #endif
   if (ctx_native_events)
     ctx_ctx_consume_events (ctx);
@@ -23628,6 +24134,13 @@ void ctx_get_event_fds (Ctx *ctx, int *fd, int *count)
 CtxEvent *ctx_get_event (Ctx *ctx)
 {
   static CtxEvent event_copy;
+  if (ctx->events.events)
+    {
+      event_copy = *((CtxEvent*)(ctx->events.events->data));
+      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
+      return &event_copy;
+    }
+
   _ctx_idle_iteration (ctx);
   if (!ctx->events.ctx_get_event_enabled)
     ctx->events.ctx_get_event_enabled = 1;
@@ -24533,7 +25046,7 @@ struct
   int        n_args;
   int        texture_done;
   uint8_t    texture_id[CTX_ID_MAXLEN]; // used in defineTexture only
-  uint64_t   set_key_hash;
+  uint32_t   set_key_hash;
   float      pcx;
   float      pcy;
   int        color_components;
@@ -24553,7 +25066,7 @@ struct
 
   void (*exit) (void *exit_data);
   void *exit_data;
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len);
+  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len);
   int   (*get_prop)(void *prop_data, const char *key, char **data, int *len);
   void *prop_data;
   int   prev_byte;
@@ -24585,7 +25098,7 @@ ctx_parser_init (CtxParser *parser,
                  float      cell_height,
                  int        cursor_x,
                  int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
   int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
                  void  *prop_data,
                  void (*exit) (void *exit_data),
@@ -24623,7 +25136,7 @@ CtxParser *ctx_parser_new (
   float      cell_height,
   int        cursor_x,
   int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*set_prop)(void *prop_data, uint32_t key, const char *data,  int len),
   int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
   void  *prop_data,
   void (*exit) (void *exit_data),
@@ -24788,7 +25301,7 @@ static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_m
 
 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 */
+  uint32_t ret = str[0]; /* if it is single char it already is the CtxCode */
 
   /* this is handled outside the hashing to make it possible to be case insensitive
    * with the rest.
@@ -24820,7 +25333,7 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str)
 
   if (str[0] && str[1])
     {
-      uint64_t str_hash;
+      uint32_t str_hash;
       /* trim ctx_ and CTX_ prefix */
       if ( (str[0] == 'c' && str[1] == 't' && str[2] == 'x' && str[3] == '_') ||
            (str[0] == 'C' && str[1] == 'T' && str[2] == 'X' && str[3] == '_') )
@@ -26437,6 +26950,12 @@ ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar)
   float scale              = stbtt_ScaleForPixelHeight (ttf_info, font_size);
   int advance, lsb;
   int glyph = ctx_glyph_stb_find (font, unichar);
+
+#if CTX_EVENTS
+  if (ctx_renderer_is_term (ctx))
+    return 2;
+#endif
+
   if (glyph==0)
     { return 0.0f; }
   stbtt_GetGlyphHMetrics (ttf_info, glyph, &advance, &lsb);
@@ -26508,16 +27027,41 @@ ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
 
 #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)
 {
+#if 1
+  int min       = 0;
+  int max       = font->ctx.glyphs-1;
+  uint32_t found;
+
+  do {
+    int pos = (min + max)/2;
+    found = font->ctx.index[pos*2];
+    if (found == glyph)
+    {
+      return font->ctx.index[pos*2+1];
+    } else if (min == max)
+      return -1;
+    else if (min == max-1)
+      return -1;
+    else if (found < glyph)
+    {
+      min = pos;
+    } else {
+      max = pos;
+    }
+
+  } while (min != max);
+
+  return -1;
+#else
   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;
+#endif
 }
 
 static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
@@ -26547,6 +27091,12 @@ ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unichar
   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;
+
+#if CTX_EVENTS
+  if (ctx_renderer_is_term (ctx) && (3.02 - font_size) < 0.03)
+    return 0.0f;
+#endif
+
   for (int i = first_kern + 1; i < font->ctx.length; i++)
     {
       CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
@@ -26582,6 +27132,12 @@ ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
   int   start     = ctx_glyph_find_ctx (font, ctx, unichar);
   if (start < 0)
     { return 0.0; }  // XXX : fallback
+
+#if CTX_EVENTS
+  if (ctx_renderer_is_term (ctx) && (3.02 - font_size) < 0.03)
+    return 2.0f;
+#endif
+
   for (int i = start; i < font->ctx.length; i++)
     {
       CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
@@ -27248,6 +27804,15 @@ void ctx_string_clear (CtxString *string)
   string->str[string->length]=0;
 }
 
+
+void ctx_string_pre_alloc (CtxString *string, int size)
+{
+  char *old = string->str;
+  string->allocated_length = CTX_MAX (size + 2, string->length + 2);
+  string->str = (char*)realloc (old, string->allocated_length);
+}
+
+
 static inline void _ctx_string_append_byte (CtxString *string, char  val)
 {
   if (CTX_LIKELY((val & 0xC0) != 0x80))
@@ -27474,7 +28039,6 @@ uint32_t ctx_string_get_unichar (CtxString *string, int pos)
   return ctx_utf8_to_unichar (p);
 }
 
-
 void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
 {
   int new_len = ctx_utf8_len (*new_glyph);
@@ -27598,6 +28162,24 @@ void ctx_string_append_printf (CtxString *string, const char *format, ...)
   free (buffer);
 }
 
+CtxString *ctx_string_new_printf (const char *format, ...)
+{
+  CtxString *string = ctx_string_new ("");
+  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);
+  return string;
+}
+
 #if CTX_CAIRO
 
 typedef struct _CtxCairo CtxCairo;
@@ -27825,6 +28407,7 @@ ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
             }
           cairo_set_operator (cr, cairo_val);
         }
+        break;
       case CTX_LINE_JOIN:
         {
           int cairo_val = CAIRO_LINE_JOIN_ROUND;
@@ -27899,7 +28482,7 @@ ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
 #endif
       case CTX_TEXT:
         /* XXX: implement some linebreaking/wrap, positioning
-         *      behavior here
+         *      behavior here?
          */
         cairo_show_text (cr, ctx_arg_string () );
         break;
@@ -28332,6 +28915,7 @@ void ctx_ctx_free (CtxCtx *ctx)
   /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
+
 Ctx *ctx_new_ctx (int width, int height)
 {
   float font_size = 12.0;
@@ -28372,25 +28956,39 @@ void ctx_ctx_pcm (Ctx *ctx);
 
 int ctx_ctx_consume_events (Ctx *ctx)
 {
-  int ix, iy;
+  //int ix, iy;
   CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
   const char *event = NULL;
 #if CTX_AUDIO
   ctx_ctx_pcm (ctx);
 #endif
+  assert (ctx_native_events);
+
+#if 1
+    { /* XXX : this is a work-around for signals not working properly, we are polling the
+         size with an ioctl per consume-events
+         */
+      struct winsize ws;
+      ioctl(0,TIOCGWINSZ,&ws);
+      ctxctx->cols = ws.ws_col;
+      ctxctx->rows = ws.ws_row;
+      ctxctx->width = ws.ws_xpixel;
+      ctxctx->height = ws.ws_ypixel;
+      ctx_set_size (ctx, ctxctx->width, ctxctx->height);
+    }
+#endif
+    //char *cmd = ctx_strdup_printf ("touch /tmp/ctx-%ix%i", ctxctx->width, ctxctx->height);
+    //system (cmd);
+    //free (cmd);
+
   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);
@@ -28420,6 +29018,9 @@ int ctx_ctx_consume_events (Ctx *ctx)
         ctxctx->rows = ctx_terminal_rows ();
         ctxctx->width  = ctx_terminal_width ();
         ctxctx->height = ctx_terminal_height ();
+
+        //system ("touch /tmp/ctx-abc");
+
         ctx_set_size (ctx, ctxctx->width, ctxctx->height);
 
         if (prev_frame_contents)
@@ -28427,7 +29028,8 @@ int ctx_ctx_consume_events (Ctx *ctx)
         prev_frame_contents = NULL;
         prev_frame_len = 0;
         ctx_set_dirty (ctx, 1);
-        //ctx_key_press (ctx, 0, "size-changed", 0);
+
+      //   ctx_key_press(ctx,0,"size-changed",0);
       }
       else if (!strcmp (event_type, "keyup"))
       {
@@ -28445,63 +29047,6 @@ int ctx_ctx_consume_events (Ctx *ctx)
       }
       }
     }
-  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"))
-      {
-        //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
-      {
-        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);
-      }
-    }
 
   return 1;
 }
@@ -28716,683 +29261,201 @@ void ctx_tiled_render_fun (void **data)
   tiled->thread_quit++; // need atomic?
 }
 
-#endif
-
-
-#if CTX_EVENTS
 
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#endif
+static int       ctx_tiled_cursor_drawn   = 0;
+static int       ctx_tiled_cursor_drawn_x = 0;
+static int       ctx_tiled_cursor_drawn_y = 0;
+static CtxCursor ctx_tiled_cursor_drawn_shape = 0;
 
 
-#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>
+#define CTX_FB_HIDE_CURSOR_FRAMES 200
 
-typedef struct _EvSource EvSource;
- 
+static int ctx_tiled_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
 
-struct _EvSource
+static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
 {
-  void   *priv; /* private storage  */
+  switch (shape)
+  {
+    case CTX_CURSOR_ARROW:
+      if (x > ((size * 4)-y*4)) return 0;
+      if (x < y && x > y / 16)
+        return 1;
+      return 0;
 
-  /* returns non 0 if there is events waiting */
-  int   (*has_event) (EvSource *ev_source);
+    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;
 
-  /* get an event, the returned event should be freed by the caller  */
-  char *(*get_event) (EvSource *ev_source);
+        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;
+}
 
-  /* destroy/unref this instance */
-  void  (*destroy)   (EvSource *ev_source);
+static void ctx_tiled_undraw_cursor (CtxTiled *tiled)
+{
+    int cursor_size = ctx_height (tiled->ctx) / 28;
 
-  /* get the underlying fd, useful for using select on  */
-  int   (*get_fd)    (EvSource *ev_source);
+    if (ctx_tiled_cursor_drawn)
+    {
+      int no = 0;
+      int startx = -cursor_size;
+      int starty = -cursor_size;
+      if (ctx_tiled_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_tiled_cursor_drawn_x < tiled->width && y + ctx_tiled_cursor_drawn_y < tiled->height)
+        {
+          if (ctx_is_in_cursor (x, y, cursor_size, ctx_tiled_cursor_drawn_shape))
+          {
+            int o = ((ctx_tiled_cursor_drawn_y + y) * tiled->width + (ctx_tiled_cursor_drawn_x + x)) * 4;
+            tiled->fb[o+0]^=0x88;
+            tiled->fb[o+1]^=0x88;
+            tiled->fb[o+2]^=0x88;
+          }
+        }
+      }
 
-  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.
-   */
+    ctx_tiled_cursor_drawn = 0;
+    }
+}
 
-  /* if this returns non-0 select can be used for non-blocking.. */
-};
+static void ctx_tiled_draw_cursor (CtxTiled *tiled)
+{
+    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_tiled_cursor_drawn_x &&
+        cursor_y == ctx_tiled_cursor_drawn_y &&
+        cursor_shape == ctx_tiled_cursor_drawn_shape)
+      ctx_tiled_cursor_same_pos ++;
+    else
+      ctx_tiled_cursor_same_pos = 0;
 
-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
-                                                           //
+    if (ctx_tiled_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
+    {
+      if (ctx_tiled_cursor_drawn)
+        ctx_tiled_undraw_cursor (tiled);
+      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_tiled_cursor_same_pos && ctx_tiled_cursor_drawn)
+      return;
 
-   int           pointer_down[3];
-#endif
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
+    ctx_tiled_undraw_cursor (tiled);
 
-   uint8_t      *fb;
+    no = 0;
 
-   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;
-};
+    int startx = -cursor_size;
+    int starty = -cursor_size;
 
-static char *ctx_fb_clipboard = NULL;
-static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
-{
-  if (ctx_fb_clipboard)
-    free (ctx_fb_clipboard);
-  ctx_fb_clipboard = NULL;
-  if (text)
-  {
-    ctx_fb_clipboard = strdup (text);
-  }
-}
+    if (cursor_shape == CTX_CURSOR_ARROW)
+      startx = starty = 0;
 
-static char *ctx_fb_get_clipboard (CtxFb *sdl)
-{
-  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
-  return strdup ("");
+    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;
+            tiled->fb[o+0]^=0x88;
+            tiled->fb[o+1]^=0x88;
+            tiled->fb[o+2]^=0x88;
+          }
+        }
+      }
+    ctx_tiled_cursor_drawn = 1;
+    ctx_tiled_cursor_drawn_x = cursor_x;
+    ctx_tiled_cursor_drawn_y = cursor_y;
+    ctx_tiled_cursor_drawn_shape = cursor_shape;
 }
 
-#if UINTPTR_MAX == 0xffFFffFF
-  #define fbdrmuint_t uint32_t
-#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
-  #define fbdrmuint_t uint64_t
 #endif
+#if CTX_EVENTS
 
-void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
-{
-   int got_master = 0;
-   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
-   if (!fb->fb_fd)
-     return NULL;
-   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
-                                         // are used by the flip callback
-   fbdrmuint_t res_fb_buf[20]={0};
-   fbdrmuint_t res_crtc_buf[20]={0};
-   fbdrmuint_t res_enc_buf[20]={0};
-   struct   drm_mode_card_res res={0};
-
-   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
-     goto cleanup;
-   got_master = 1;
 
-   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;
-
-
-   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;
-
-     map_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
-       goto cleanup;
-
-     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
-                       fb->fb_fd, map_dumb.offset);
-     if (!base)
-     {
-       goto cleanup;
-     }
-     *width  = create_dumb.width;
-     *height = create_dumb.height;
-
-     struct drm_mode_get_encoder enc={0};
-     enc.encoder_id=conn.encoder_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
-        goto cleanup;
-
-     fb->crtc.crtc_id=enc.crtc_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
-        goto cleanup;
-
-     fb->crtc.fb_id=cmd_dumb.fb_id;
-     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
-     fb->crtc.count_connectors=1;
-     fb->crtc.mode=conn_mode_buf[0];
-     fb->crtc.mode_valid=1;
-     return base;
-   }
-cleanup:
-   if (got_master)
-     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-   fb->fb_fd = 0;
-   return NULL;
-}
-
-void ctx_fbdrm_flip (CtxFb *fb)
-{
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
-}
-
-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;
-}
-
-static void ctx_fb_flip (CtxFb *fb)
-{
-  if (fb->is_drm)
-    ctx_fbdrm_flip (fb);
-  else
-    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
-}
-
-inline static uint32_t
-ctx_swap_red_green2 (uint32_t orig)
-{
-  uint32_t  green_alpha = (orig & 0xff00ff00);
-  uint32_t  red_blue    = (orig & 0x00ff00ff);
-  uint32_t  red         = red_blue << 16;
-  uint32_t  blue        = red_blue >> 16;
-  return green_alpha | red | blue;
-}
-
-static int       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)
-{
-  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 void ctx_fb_undraw_cursor (CtxFb *fb)
-{
-    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;
-    }
-}
-
-static void ctx_fb_draw_cursor (CtxFb *fb)
-{
-    CtxTiled *tiled = (void*)fb;
-    int cursor_x    = ctx_pointer_x (tiled->ctx);
-    int cursor_y    = ctx_pointer_y (tiled->ctx);
-    int cursor_size = ctx_height (tiled->ctx) / 28;
-    CtxCursor cursor_shape = tiled->ctx->cursor;
-    int no = 0;
-
-    if (cursor_x == 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;
-
-    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;
-}
-
-static void ctx_fb_show_frame (CtxFb *fb, int block)
-{
-  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;
-  }
-
-  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;
-  }
-
-    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;
-  }
-}
-
-
-#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)
+#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)
 
 
 
@@ -29487,8 +29550,7 @@ static char *mice_get_event ()
   double relx, rely;
   signed char buf[3];
   int n_read = 0;
-  CtxFb *fb = ctx_ev_src_mice.priv;
-  CtxTiled *tiled = (void*)fb;
+  CtxTiled *tiled = (void*)ctx_ev_src_mice.priv;
   n_read = read (mrg_mice_this->fd, buf, 3);
   if (n_read == 0)
      return strdup ("");
@@ -29941,127 +30003,1147 @@ static char *evsource_kb_get_event (void)
   int length;
 
 
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const MmmKeyCode *match = NULL;
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const MmmKeyCode *match = NULL;
+
+        //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)
+          {
+            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");
+          }
+
+        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
+          {
+            case 1: /* unique match */
+              if (!match)
+                return NULL;
+              return strdup (match->nick);
+              break;
+            case 0: /* no matches, bail*/
+             {
+                static char ret[256]="";
+                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
+                                                             * single unicode
+                                                             * utf8 character
+                                                             */
+                  {
+                    int bytes = read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
+                    if (bytes)
+                    {
+                      buf[ctx_utf8_len(buf[0])]=0;
+                      strcpy (ret, (void*)buf);
+                    }
+                    return strdup(ret); //XXX: simplify
+                  }
+                if (length == 0) /* ascii */
+                  {
+                    buf[1]=0;
+                    strcpy (ret, (void*)buf);
+                    return strdup(ret);
+                  }
+                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
+                    length >=0 ? buf[0] : 0,
+                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
+                    length >=1 ? buf[1] : 0,
+                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
+                    length >=2 ? buf[2] : 0,
+                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
+                    length >=3 ? buf[3] : 0,
+                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
+                    length >=4 ? buf[4] : 0,
+                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
+                    length >=5 ? buf[5] : 0,
+                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
+                    length >=6 ? buf[6] : 0,
+                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
+                    );
+                return strdup(ret);
+            }
+              return NULL;
+            default: /* continue */
+              break;
+          }
+      }
+    else
+      return strdup("key read eek");
+  return strdup("fail");
+}
+
+static int evsource_kb_get_fd (void)
+{
+  return STDIN_FILENO;
+}
+
+
+static EvSource *evsource_kb_new (void)
+{
+  if (evsource_kb_init() == 0)
+  {
+    return &ctx_ev_src_kb;
+  }
+  return NULL;
+}
+
+static int event_check_pending (CtxTiled *tiled)
+{
+  int events = 0;
+  for (int i = 0; i < tiled->evsource_count; i++)
+  {
+    while (evsource_has_event (tiled->evsource[i]))
+    {
+      char *event = evsource_get_event (tiled->evsource[i]);
+      if (event)
+      {
+        if (tiled->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);
+      }
+    }
+  }
+  return events;
+}
+
+int ctx_renderer_is_tiled (Ctx *ctx)
+{
+  return ctx_renderer_is_fb (ctx)
+          || ctx_renderer_is_sdl (ctx)
+       || ctx_renderer_is_kms (ctx)
+     ;
+}
+
+#endif
+
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#endif
+
+#if CTX_KMS || CTX_FB
+static char *ctx_fb_clipboard = NULL;
+static void ctx_fb_set_clipboard (void *fb, const char *text)
+{
+  if (ctx_fb_clipboard)
+    free (ctx_fb_clipboard);
+  ctx_fb_clipboard = NULL;
+  if (text)
+  {
+    ctx_fb_clipboard = strdup (text);
+  }
+}
+
+static char *ctx_fb_get_clipboard (void *sdl)
+{
+  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
+  return strdup ("");
+}
+#endif
+
+
+#if CTX_KMS
+#ifdef __linux__
+  #include <linux/kd.h>
+#endif
+  //#include <linux/fb.h>
+  //#include <linux/vt.h>
+  #include <sys/mman.h>
+  //#include <threads.h>
+  #include <libdrm/drm.h>
+  #include <libdrm/drm_mode.h>
+
+
+typedef struct _CtxKMS CtxKMS;
+struct _CtxKMS
+{
+   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;
+
+   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          is_kms;
+   cnd_t        cond;
+   mtx_t        mtx;
+   struct drm_mode_crtc crtc;
+};
+
+
+#if UINTPTR_MAX == 0xffFFffFF
+  #define fbdrmuint_t uint32_t
+#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
+  #define fbdrmuint_t uint64_t
+#endif
+
+void *ctx_fbkms_new (CtxKMS *fb, int *width, int *height)
+{
+   int got_master = 0;
+   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
+   if (!fb->fb_fd)
+     return NULL;
+   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
+                                         // are used by the flip callback
+   fbdrmuint_t res_fb_buf[20]={0};
+   fbdrmuint_t res_crtc_buf[20]={0};
+   fbdrmuint_t res_enc_buf[20]={0};
+   struct   drm_mode_card_res res={0};
+
+   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
+     goto cleanup;
+   got_master = 1;
+
+   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;
+
+
+   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;
+
+     map_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
+       goto cleanup;
+
+     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                       fb->fb_fd, map_dumb.offset);
+     if (!base)
+     {
+       goto cleanup;
+     }
+     *width  = create_dumb.width;
+     *height = create_dumb.height;
+
+     struct drm_mode_get_encoder enc={0};
+     enc.encoder_id=conn.encoder_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
+        goto cleanup;
+
+     fb->crtc.crtc_id=enc.crtc_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
+        goto cleanup;
+
+     fb->crtc.fb_id=cmd_dumb.fb_id;
+     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
+     fb->crtc.count_connectors=1;
+     fb->crtc.mode=conn_mode_buf[0];
+     fb->crtc.mode_valid=1;
+     return base;
+   }
+cleanup:
+   if (got_master)
+     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+   fb->fb_fd = 0;
+   return NULL;
+}
+
+void ctx_fbkms_flip (CtxKMS *fb)
+{
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
+}
+
+void ctx_fbkms_close (CtxKMS *fb)
+{
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+  close (fb->fb_fd);
+  fb->fb_fd = 0;
+}
+
+static void ctx_kms_flip (CtxKMS *fb)
+{
+  if (fb->is_kms)
+    ctx_fbkms_flip (fb);
+#if 0
+  else
+    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+#endif
+}
+
+inline static uint32_t
+ctx_swap_red_green2 (uint32_t orig)
+{
+  uint32_t  green_alpha = (orig & 0xff00ff00);
+  uint32_t  red_blue    = (orig & 0x00ff00ff);
+  uint32_t  red         = red_blue << 16;
+  uint32_t  blue        = red_blue >> 16;
+  return green_alpha | red | blue;
+}
+
+static void ctx_kms_show_frame (CtxKMS *fb, int block)
+{
+  CtxTiled *tiled = (void*)fb;
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    if (block == 0) // consume event call
+    {
+      ctx_tiled_draw_cursor ((CtxTiled*)fb);
+      ctx_kms_flip (fb);
+    }
+    return;
+  }
+
+  if (block)
+  {
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+    {
+      usleep (500);
+      count ++;
+      if (count > 500)
+      {
+        tiled->shown_frame = tiled->render_frame;
+        return;
+      }
+    }
+  }
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
+
+    if (tiled->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;
+
+
+       if (tiled->min_row == 100){
+          pre_skip = 0;
+          post_skip = 0;
+          // not when kms ?
+#if 0
+     __u32 dummy = 0;
+          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+#endif
+          ctx_tiled_undraw_cursor ((CtxTiled*)fb);
+       }
+       else
+       {
+
+      tiled->min_row = 100;
+      tiled->max_row = 0;
+      tiled->min_col = 100;
+      tiled->max_col = 0;
+
+     // not when kms ?
+ #if 0
+     __u32 dummy = 0;
+     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+#endif
+     ctx_tiled_undraw_cursor ((CtxTiled*)fb);
+     switch (fb->fb_bits)
+     {
+       case 32:
+#if 1
+         {
+           uint8_t *dst = tiled->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*)tiled->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 = tiled->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 = tiled->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 = tiled->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 = tiled->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_tiled_cursor_drawn = 0;
+    ctx_tiled_draw_cursor (&fb->tiled);
+    ctx_kms_flip (fb);
+    tiled->shown_frame = tiled->render_frame;
+  }
+}
+
+int ctx_kms_consume_events (Ctx *ctx)
+{
+  CtxKMS *fb = (void*)ctx->renderer;
+  ctx_kms_show_frame (fb, 0);
+  event_check_pending (&fb->tiled);
+  return 0;
+}
+
+inline static void ctx_kms_reset (CtxKMS *fb)
+{
+  ctx_kms_show_frame (fb, 1);
+}
+
+inline static void ctx_kms_flush (CtxKMS *fb)
+{
+  ctx_tiled_flush ((CtxTiled*)fb);
+}
+
+void ctx_kms_free (CtxKMS *fb)
+{
+  if (fb->is_kms)
+  {
+    ctx_fbkms_close (fb);
+  }
+#ifdef __linux__
+  ioctl (0, KDSETMODE, KD_TEXT);
+#endif
+  if (system("stty sane")){};
+  ctx_tiled_free ((CtxTiled*)fb);
+  //free (fb);
+#if CTX_BABL
+  babl_exit ();
+#endif
+}
+
+//static unsigned char *fb_icc = NULL;
+//static long fb_icc_length = 0;
+
+int ctx_renderer_is_kms (Ctx *ctx)
+{
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_kms_free)
+          return 1;
+  return 0;
+}
+
+#if 0
+static CtxKMS *ctx_fb = NULL;
+static void vt_switch_cb (int sig)
+{
+  CtxTiled *tiled = (void*)ctx_fb;
+  if (sig == SIGUSR1)
+  {
+    if (ctx_fb->is_kms)
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+    ioctl (0, VT_RELDISP, 1);
+    ctx_fb->vt_active = 0;
+#if 0
+    ioctl (0, KDSETMODE, KD_TEXT);
+#endif
+  }
+  else
+  {
+    ioctl (0, VT_RELDISP, VT_ACKACQ);
+    ctx_fb->vt_active = 1;
+    // queue draw
+    tiled->render_frame = ++tiled->frame;
+#if 0
+    ioctl (0, KDSETMODE, KD_GRAPHICS);
+#endif
+    if (ctx_fb->is_kms)
+    {
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_SET_MASTER, 0);
+      ctx_kms_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;
+      }
+    }
+  }
+}
+#endif
+
+static int ctx_kms_get_mice_fd (Ctx *ctx)
+{
+  //CtxKMS *fb = (void*)ctx->renderer;
+  return _ctx_mice_fd;
+}
+
+Ctx *ctx_new_kms (int width, int height)
+{
+#if CTX_RASTERIZER
+  CtxKMS *fb = calloc (sizeof (CtxKMS), 1);
+
+  CtxTiled *tiled = (void*)fb;
+  tiled->fb = ctx_fbkms_new (fb, &tiled->width, &tiled->height);
+  if (tiled->fb)
+  {
+    fb->is_kms         = 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;
+  }
+  if (!tiled->fb)
+    return NULL;
+  tiled->pixels = calloc (fb->fb_mapped_size, 1);
+  ctx_kms_events = 1;
+
+#if CTX_BABL
+  babl_init ();
+#endif
+
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
+
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
+
+  ctx_set_renderer (tiled->ctx, fb);
+  ctx_set_renderer (tiled->ctx_copy, fb);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
+
+  ctx_set_size (tiled->ctx, width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
+
+  tiled->flush = (void*)ctx_kms_flush;
+  tiled->reset = (void*)ctx_kms_reset;
+  tiled->free  = (void*)ctx_kms_free;
+  tiled->set_clipboard = (void*)ctx_fb_set_clipboard;
+  tiled->get_clipboard = (void*)ctx_fb_get_clipboard;
+
+  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);
+
+#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
+
+
+  EvSource *kb = evsource_kb_new ();
+  if (kb)
+  {
+    tiled->evsource[tiled->evsource_count++] = kb;
+    kb->priv = fb;
+  }
+  EvSource *mice  = evsource_mice_new ();
+  if (mice)
+  {
+    tiled->evsource[tiled->evsource_count++] = mice;
+    mice->priv = fb;
+  }
+
+  tiled->vt_active = 1;
+#ifdef __linux__
+  ioctl(0, KDSETMODE, KD_GRAPHICS);
+#endif
+  tiled->shown_frame = tiled->render_frame;
+  //ctx_flush (tiled->ctx);
+#if 0
+  signal (SIGUSR1, vt_switch_cb);
+  signal (SIGUSR2, vt_switch_cb);
+
+  struct vt_stat st;
+  if (ioctl (0, VT_GETSTATE, &st) == -1)
+  {
+    ctx_log ("VT_GET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
+  }
+
+  fb->vt = st.v_active;
+
+  struct vt_mode mode;
+  mode.mode   = VT_PROCESS;
+  mode.relsig = SIGUSR1;
+  mode.acqsig = SIGUSR2;
+  if (ioctl (0, VT_SETMODE, &mode) < 0)
+  {
+    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
+  }
+#endif
+
+  return tiled->ctx;
+#else
+  return NULL;
+#endif
+}
+#else
+
+int ctx_renderer_is_kms (Ctx *ctx)
+{
+  return 0;
+}
+
+#endif
+#endif
+
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#endif
+
+#if CTX_FB
+static int ctx_fb_get_mice_fd (Ctx *ctx)
+{
+  //CtxFb *fb = (void*)ctx->renderer;
+  return _ctx_mice_fd;
+}
+
+#ifdef __linux__
+  #include <linux/fb.h>
+  #include <linux/vt.h>
+  #include <linux/kd.h>
+#endif
+
+#ifdef __NetBSD__
+  typedef uint8_t unchar;
+  typedef uint8_t u_char;
+  typedef uint16_t ushort;
+  typedef uint32_t u_int;
+  typedef uint64_t u_long;
+  #include <sys/param.h>
+  #include <dev/wscons/wsdisplay_usl_io.h>
+  #include <dev/wscons/wsconsio.h>
+  #include <dev/wscons/wsksymdef.h>
+#endif
+
+  #include <sys/mman.h>
+
+typedef struct _CtxFb CtxFb;
+struct _CtxFb
+{
+   CtxTiled tiled;
+#if 0
+   void (*render) (void *fb, CtxCommand *command);
+   void (*reset)  (void *fb);
+   void (*flush)  (void *fb);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard) (void *ctxctx, const char *text);
+   void (*free)   (void *fb);
+   Ctx          *ctx;
+   int           width;
+   int           height;
+   int           cols; // unused
+   int           rows; // unused
+   int           was_down;
+   uint8_t      *pixels;
+   Ctx          *ctx_copy;
+   Ctx          *host[CTX_MAX_THREADS];
+   CtxAntialias  antialias;
+   int           quit;
+   _Atomic int   thread_quit;
+   int           shown_frame;
+   int           render_frame;
+   int           rendered_frame[CTX_MAX_THREADS];
+   int           frame;
+   int           min_col; // hasher cols and rows
+   int           min_row;
+   int           max_col;
+   int           max_row;
+   uint8_t       hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
+   int8_t        tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
+                                                           // responsible for a tile
+                                                           //
+
+
+   int           pointer_down[3];
+#endif
+   int           key_balance;
+   int           key_repeat;
+   int           lctrl;
+   int           lalt;
+   int           rctrl;
+
 
-        //if (!is_active (ctx_ev_src_kb.priv))
-        //  return NULL;
+   int          fb_fd;
+   char        *fb_path;
+   int          fb_bits;
+   int          fb_bpp;
+   int          fb_mapped_size;
+   int          vt;
+   int          tty;
+   cnd_t        cond;
+   mtx_t        mtx;
+#if __linux__
+   struct       fb_var_screeninfo vinfo;
+   struct       fb_fix_screeninfo finfo;
+#endif
+};
 
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
-          {
-            struct timeval tv;
-            fd_set rfds;
-            FD_ZERO (&rfds);
-            FD_SET (STDIN_FILENO, &rfds);
-            tv.tv_sec = 0;
-            tv.tv_usec = 1000 * 120;
-            if (select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 0)
-              return strdup ("escape");
-          }
+#if UINTPTR_MAX == 0xffFFffFF
+  #define fbdrmuint_t uint32_t
+#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
+  #define fbdrmuint_t uint64_t
+#endif
 
-        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
-          {
-            case 1: /* unique match */
-              if (!match)
-                return NULL;
-              return strdup (match->nick);
-              break;
-            case 0: /* no matches, bail*/
-             {
-                static char ret[256]="";
-                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
-                                                             * single unicode
-                                                             * utf8 character
-                                                             */
-                  {
-                    int bytes = read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
-                    if (bytes)
-                    {
-                      buf[ctx_utf8_len(buf[0])]=0;
-                      strcpy (ret, (void*)buf);
-                    }
-                    return strdup(ret); //XXX: simplify
-                  }
-                if (length == 0) /* ascii */
-                  {
-                    buf[1]=0;
-                    strcpy (ret, (void*)buf);
-                    return strdup(ret);
-                  }
-                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
-                    length >=0 ? buf[0] : 0,
-                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
-                    length >=1 ? buf[1] : 0,
-                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
-                    length >=2 ? buf[2] : 0,
-                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
-                    length >=3 ? buf[3] : 0,
-                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
-                    length >=4 ? buf[4] : 0,
-                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
-                    length >=5 ? buf[5] : 0,
-                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
-                    length >=6 ? buf[6] : 0,
-                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
-                    );
-                return strdup(ret);
-            }
-              return NULL;
-            default: /* continue */
-              break;
-          }
-      }
-    else
-      return strdup("key read eek");
-  return strdup("fail");
-}
 
-static int evsource_kb_get_fd (void)
+static void ctx_fb_flip (CtxFb *fb)
 {
-  return STDIN_FILENO;
+#ifdef __linux__
+  ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+#endif
 }
 
-
-static EvSource *evsource_kb_new (void)
+static void ctx_fb_show_frame (CtxFb *fb, int block)
 {
-  if (evsource_kb_init() == 0)
+  CtxTiled *tiled = (void*)fb;
+  if (tiled->shown_frame == tiled->render_frame)
   {
-    return &ctx_ev_src_kb;
+    if (block == 0) // consume event call
+    {
+      ctx_tiled_draw_cursor (tiled);
+      ctx_fb_flip (fb);
+    }
+    return;
   }
-  return NULL;
-}
 
-static int event_check_pending (CtxFb *fb)
-{
-  CtxTiled *tiled = (void*)fb;
-  int events = 0;
-  for (int i = 0; i < fb->evsource_count; i++)
+  if (block)
   {
-    while (evsource_has_event (fb->evsource[i]))
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
     {
-      char *event = evsource_get_event (fb->evsource[i]);
-      if (event)
+      usleep (500);
+      count ++;
+      if (count > 2000)
       {
-        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);
+        tiled->shown_frame = tiled->render_frame;
+        return;
       }
     }
   }
-  return events;
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
+
+    if (tiled->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;
+
+
+       if (tiled->min_row == 100){
+          pre_skip = 0;
+          post_skip = 0;
+#ifdef __linux__
+           __u32 dummy = 0;
+          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+#endif
+          ctx_tiled_undraw_cursor (tiled);
+       }
+       else
+       {
+
+      tiled->min_row = 100;
+      tiled->max_row = 0;
+      tiled->min_col = 100;
+      tiled->max_col = 0;
+#ifdef __linux__
+    {
+     __u32 dummy = 0;
+     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+    }
+#endif
+     ctx_tiled_undraw_cursor (tiled);
+     switch (fb->fb_bits)
+     {
+       case 32:
+#if 1
+         {
+           uint8_t *dst = tiled->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*)tiled->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 = tiled->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 = tiled->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 = tiled->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 = tiled->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_tiled_cursor_drawn = 0;
+    ctx_tiled_draw_cursor (tiled);
+    ctx_fb_flip (fb);
+    tiled->shown_frame = tiled->render_frame;
+  }
 }
 
 int ctx_fb_consume_events (Ctx *ctx)
 {
   CtxFb *fb = (void*)ctx->renderer;
   ctx_fb_show_frame (fb, 0);
-  event_check_pending (fb);
+  event_check_pending (&fb->tiled);
   return 0;
 }
 
@@ -30077,12 +31159,19 @@ inline static void ctx_fb_flush (CtxFb *fb)
 
 void ctx_fb_free (CtxFb *fb)
 {
-  if (fb->is_drm)
-  {
-    ctx_fbdrm_close (fb);
-  }
+  CtxTiled*tiled=(CtxTiled*)fb;
 
+//#ifdef __linux__
   ioctl (0, KDSETMODE, KD_TEXT);
+//#endif
+#ifdef __NetBSD__
+  {
+   int mode = WSDISPLAYIO_MODE_EMUL;
+   ioctl (fb->fb_fd, WSDISPLAYIO_SMODE, &mode);
+  }
+#endif
+  munmap (tiled->fb, fb->fb_mapped_size);
+  close (fb->fb_fd);
   if (system("stty sane")){};
   ctx_tiled_free ((CtxTiled*)fb);
   //free (fb);
@@ -30103,30 +31192,23 @@ int ctx_renderer_is_fb (Ctx *ctx)
 }
 
 static CtxFb *ctx_fb = NULL;
-static void vt_switch_cb (int sig)
+#ifdef __linux__
+static void fb_vt_switch_cb (int sig)
 {
   CtxTiled *tiled = (void*)ctx_fb;
   if (sig == SIGUSR1)
   {
-    if (ctx_fb->is_drm)
-      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
     ioctl (0, VT_RELDISP, 1);
-    ctx_fb->vt_active = 0;
+    tiled->vt_active = 0;
     ioctl (0, KDSETMODE, KD_TEXT);
   }
   else
   {
     ioctl (0, VT_RELDISP, VT_ACKACQ);
-    ctx_fb->vt_active = 1;
+    tiled->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;
 
@@ -30138,55 +31220,46 @@ static void vt_switch_cb (int sig)
     }
   }
 }
+#endif
 
-static int ctx_fb_get_mice_fd (Ctx *ctx)
-{
-  //CtxFb *fb = (void*)ctx->renderer;
-  return _ctx_mice_fd;
-}
 
-Ctx *ctx_new_fb (int width, int height, int drm)
+Ctx *ctx_new_fb (int width, int height)
 {
 #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);
+#ifdef __linux__
+  const char *dev_path = "/dev/fb0";
+#endif
+#ifdef __NetBSD__
+  const char *dev_path = "/dev/ttyE0";
+#endif
+#ifdef __OpenBSD__
+  const char *dev_path = "/dev/ttyC0";
+#endif
+  fb->fb_fd = open (dev_path, O_RDWR);
   if (fb->fb_fd > 0)
-    fb->fb_path = strdup ("/dev/fb0");
+    fb->fb_path = strdup (dev_path);
   else
   {
+#ifdef __linux__
     fb->fb_fd = open ("/dev/graphics/fb0", O_RDWR);
     if (fb->fb_fd > 0)
     {
       fb->fb_path = strdup ("/dev/graphics/fb0");
     }
     else
+#endif
     {
       free (fb);
       return NULL;
     }
   }
 
+#ifdef __linux__
   if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
     {
       fprintf (stderr, "error getting fbinfo\n");
@@ -30204,6 +31277,7 @@ Ctx *ctx_new_fb (int width, int height, int drm)
       free (fb);
       return NULL;
      }
+  ioctl (0, KDSETMODE, KD_GRAPHICS);
 
 //fprintf (stderr, "%s\n", fb->fb_path);
   width = tiled->width = fb->vinfo.xres;
@@ -30217,22 +31291,21 @@ Ctx *ctx_new_fb (int width, int height, int drm)
       fb->vinfo.red.length +
       fb->vinfo.green.length +
       fb->vinfo.blue.length;
-
    else if (fb->fb_bits == 8)
   {
     unsigned short red[256],  green[256],  blue[256];
-    unsigned short original_red[256];
-    unsigned short original_green[256];
-    unsigned short original_blue[256];
+  //  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};
+  //  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__);
-    }
+   // if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
+   // {
+   //   fprintf (stderr, "palette initialization problem %i\n", __LINE__);
+   // }
 
     for (i = 0; i < 256; i++)
     {
@@ -30249,10 +31322,52 @@ Ctx *ctx_new_fb (int width, int height, int drm)
 
   fb->fb_bpp = fb->vinfo.bits_per_pixel / 8;
   fb->fb_mapped_size = fb->finfo.smem_len;
+#endif
+
+#ifdef __NetBSD__
+  struct wsdisplay_fbinfo finfo;
+
+  int mode = WSDISPLAYIO_MODE_DUMBFB;
+  //int mode = WSDISPLAYIO_MODE_MAPPED;
+  if (ioctl (fb->fb_fd, WSDISPLAYIO_SMODE, &mode)) {
+    return NULL;
+  }
+  if (ioctl (fb->fb_fd, WSDISPLAYIO_GINFO, &finfo)) {
+    fprintf (stderr, "ioctl: WSIDSPLAYIO_GINFO failed\n");
+    return NULL;
+  }
+
+  width = tiled->width = finfo.width;
+  height = tiled->height = finfo.height;
+  fb->fb_bits = finfo.depth;
+  fb->fb_bpp = (fb->fb_bits + 1) / 8;
+  fb->fb_mapped_size = width * height * fb->fb_bpp;
+
+
+  if (fb->fb_bits == 8)
+  {
+    uint8_t red[256],  green[256],  blue[256];
+    struct wsdisplay_cmap cmap;
+    cmap.red = red;
+    cmap.green = green;
+    cmap.blue = blue;
+    cmap.count = 256;
+    cmap.index = 0;
+    for (int i = 0; i < 256; i++)
+    {
+      red[i]   = ((( i >> 5) & 0x7) << 5);
+      green[i] = ((( i >> 2) & 0x7) << 5);
+      blue[i]  = ((( i >> 0) & 0x3) << 6);
+    }
+
+    ioctl (fb->fb_fd, WSDISPLAYIO_PUTCMAP, &cmap);
+  }
+#endif
+
                                               
-  fb->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
+  tiled->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
   }
-  if (!fb->fb)
+  if (!tiled->fb)
     return NULL;
   tiled->pixels = calloc (fb->fb_mapped_size, 1);
   ctx_fb_events = 1;
@@ -30319,25 +31434,27 @@ Ctx *ctx_new_fb (int width, int height, int drm)
   start_thread(15);
 #undef start_thread
 
-  ctx_flush (tiled->ctx);
+  //ctx_flush (tiled->ctx);
 
   EvSource *kb = evsource_kb_new ();
   if (kb)
   {
-    fb->evsource[fb->evsource_count++] = kb;
+    tiled->evsource[tiled->evsource_count++] = kb;
     kb->priv = fb;
   }
   EvSource *mice  = evsource_mice_new ();
   if (mice)
   {
-    fb->evsource[fb->evsource_count++] = mice;
+    tiled->evsource[tiled->evsource_count++] = mice;
     mice->priv = fb;
   }
 
-  fb->vt_active = 1;
+  tiled->vt_active = 1;
+#ifdef __linux__
   ioctl(0, KDSETMODE, KD_GRAPHICS);
-  signal (SIGUSR1, vt_switch_cb);
-  signal (SIGUSR2, vt_switch_cb);
+  signal (SIGUSR1, fb_vt_switch_cb);
+  signal (SIGUSR2, fb_vt_switch_cb);
+
   struct vt_stat st;
   if (ioctl (0, VT_GETSTATE, &st) == -1)
   {
@@ -30356,6 +31473,7 @@ Ctx *ctx_new_fb (int width, int height, int drm)
     ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
     return NULL;
   }
+#endif
 
   return tiled->ctx;
 #else
@@ -30392,8 +31510,6 @@ struct _CtxSDL
    SDL_Renderer *renderer;
    SDL_Texture  *texture;
 
-// cnd_t  cond;
-// mtx_t  mtx;
    int           fullscreen;
 };
 
@@ -30409,23 +31525,25 @@ void ctx_screenshot (Ctx *ctx, const char *output_path)
 #if CTX_FB
   if (ctx_renderer_is_fb  (ctx)) valid = 1;
 #endif
+#if CTX_KMS
+  if (ctx_renderer_is_kms (ctx)) valid = 1;
+#endif
 
   if (!valid)
     return;
 
-#if CTX_FB
-  // we rely on the same layout
+  // we rely on the same struxt layout XXX !
   for (int i = 0; i < sdl->width * sdl->height; i++)
   {
     int tmp = sdl->pixels[i*4];
     sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
     sdl->pixels[i*4 + 2] = tmp;
   }
-#endif
 
   stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
 
-#if CTX_FB
+#if 0
+#if CTX_FB || CTX_KMS
   for (int i = 0; i < sdl->width * sdl->height; i++)
   {
     int tmp = sdl->pixels[i*4];
@@ -30434,6 +31552,7 @@ void ctx_screenshot (Ctx *ctx, const char *output_path)
   }
 #endif
 #endif
+#endif
 }
 
 int ctx_show_fps = 1;
@@ -30519,9 +31638,9 @@ static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
     int count = 0;
     while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
     {
-      usleep (50);
+      usleep (500);
       count ++;
-      if (count > 2000)
+      if (count > 100)
       {
         tiled->shown_frame = tiled->render_frame;
         return;
@@ -30765,7 +31884,7 @@ int ctx_sdl_consume_events (Ctx *ctx)
           if (strlen (name)
               &&(event.key.keysym.mod & (KMOD_CTRL) ||
                  event.key.keysym.mod & (KMOD_ALT) ||
-                 strlen (name) >= 2))
+                 ctx_utf8_strlen (name) >= 2))
           {
             if (event.key.keysym.mod & (KMOD_CTRL) )
               {
@@ -31025,7 +32144,7 @@ Ctx *ctx_new_sdl (int width, int height)
   start_thread(15);
 #undef start_thread
 
-  ctx_flush (tiled->ctx);
+  //ctx_flush (tiled->ctx);
   return tiled->ctx;
 #else
   return NULL;
@@ -31204,6 +32323,10 @@ void ctx_term_scanout (CtxTerm *term)
   printf ("\e[H");
 //  printf ("\e[?25l");
   printf ("\e[0m");
+
+  int cur_fg[3]={-1,-1,-1};
+  int cur_bg[3]={-1,-1,-1};
+
   for (CtxList *l = term->lines; l; l = l->next)
   {
     CtxTermLine *line = l->data;
@@ -31215,14 +32338,31 @@ void ctx_term_scanout (CtxTerm *term)
           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]);
+        if (cell->fg[0] != cur_fg[0] ||
+            cell->fg[1] != cur_fg[1] ||
+            cell->fg[2] != cur_fg[2])
+        {
+          ctx_term_set_fg (cell->fg[0], cell->fg[1], cell->fg[2]);
+          cur_fg[0]=cell->fg[0];
+          cur_fg[1]=cell->fg[1];
+          cur_fg[2]=cell->fg[2];
+        }
+        if (cell->bg[0] != cur_bg[0] ||
+            cell->bg[1] != cur_bg[1] ||
+            cell->bg[2] != cur_bg[2])
+        {
+          ctx_term_set_bg (cell->bg[0], cell->bg[1], cell->bg[2]);
+          cur_bg[0]=cell->bg[0];
+          cur_bg[1]=cell->bg[1];
+          cur_bg[2]=cell->bg[2];
+        }
         printf ("%s", cell->utf8);
       }
       else
       {
-        // TODO: accumulate succesive such, and compress them
-        // into one
+        // TODO: accumulate succesive such to be ignored items,
+        // and compress them into one, making us compress largely
+        // reused screens well
         printf ("\e[C");
       }
       strcpy (cell->prev_utf8, cell->utf8);
@@ -32907,6 +34047,8 @@ ctx_render_string (Ctx *ctx, int longform, int *retlen)
 
 
 #endif
+#include <ctype.h>
+#include <sys/stat.h>
 
 #if CTX_EVENTS
 int ctx_width (Ctx *ctx)
@@ -33110,10 +34252,14 @@ ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
      }
    }
 #endif
-#if CTX_FB
    else if (format == CTX_FORMAT_RGBA8 &&
-                   (
-                   ctx_renderer_is_fb (ctx)
+                   ( 1
+#if CTX_FB
+                   || ctx_renderer_is_fb (ctx)
+#endif
+#if CTX_KMS
+                   || ctx_renderer_is_kms (ctx)
+#endif
 #if CTX_SDL
                    || ctx_renderer_is_sdl (ctx)
 #endif
@@ -33136,7 +34282,6 @@ ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
        return;
      }
    }
-#endif
 }
 
 void
@@ -33678,7 +34823,7 @@ void ctx_clip (Ctx *ctx)
 }
 
 void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
+ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len);
 
 void ctx_save (Ctx *ctx)
 {
@@ -33816,7 +34961,7 @@ _ctx_font (Ctx *ctx, const char *name)
 
 #if 0
 void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len)
+ctx_set (Ctx *ctx, uint32_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);
@@ -35018,9 +36163,10 @@ ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *use
 #endif
 
 int
-ctx_get_contents (const char     *uri,
-                  unsigned char **contents,
-                  long           *length)
+ctx_get_contents2 (const char     *uri,
+                   unsigned char **contents,
+                   long           *length,
+                   long            max_len)
 {
   char *temp_uri = NULL; // XXX XXX breaks with data uri's
   int   success  = -1;
@@ -35055,7 +36201,7 @@ ctx_get_contents (const char     *uri,
   }
 
   if (!strncmp (uri, "file://", 7))
-    success = __ctx_file_get_contents (uri + 7, contents, length);
+    success = ___ctx_file_get_contents (uri + 7, contents, length, max_len);
   else
   {
 #ifndef NO_LIBCURL
@@ -35088,12 +36234,243 @@ ctx_get_contents (const char     *uri,
      success = 0;
   }
 #else
-    success = __ctx_file_get_contents (uri, contents, length);
+    success = ___ctx_file_get_contents (uri, contents, length, max_len);
 #endif
   }
   free (temp_uri);
   return success;
 }
+
+
+int
+ctx_get_contents (const char     *uri,
+                  unsigned char **contents,
+                  long           *length)
+{
+  return ctx_get_contents2 (uri, contents, length, 1024*1024*1024);
+}
+
+
+
+typedef struct CtxMagicEntry {
+  const char *mime_type;
+  const char *ext1;
+  int len;
+  uint8_t magic[16];
+} CtxMagicEntry;
+
+static CtxMagicEntry ctx_magics[]={
+  {"image/bmp",  ".bmp", 0, {0}},
+  {"image/png",  ".png", 8, {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}},
+  {"image/jpeg", ".jpg", 8, {0xff, 0xd8, 0xff, 0xdb, 0xff, 0xd8, 0xff, 0xe0}},
+  {"image/jpeg", ".jpeg", 8, {0xff, 0xd8, 0xff, 0xdb, 0xff, 0xd8, 0xff, 0xe0}},
+  {"image/gif",  ".gif", 6, {0x47, 0x49, 0x46, 0x38, 0x37, 0x61}},
+  {"image/gif",  ".gif", 6, {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}},
+  {"image/exr",  ".exr", 4, {0x76, 0x2f, 0x31, 0x01}},
+  {"video/mpeg", ".mpg", 4, {0x00, 0x00, 0x01, 0xba}},
+  {"application/blender", ".blend", 8, {0x42, 0x4c,0x45,0x4e,0x44,0x45,0x52}},
+  {"image/xcf",  ".xcf", 8, {0x67, 0x69,0x6d,0x70,0x20,0x78,0x63,0x66}},
+  {"application/bzip2", ".bz2", 3, {0x42, 0x5a, 0x68}},
+  {"application/gzip", ".gz", 0, {0x0}},
+  {"text/x-csrc", ".c", 0, {0,}},
+  {"text/x-chdr", ".h", 0, {0,}},
+  {"text/css", ".css", 0, {0x0}},
+  {"text/csv", ".csv", 0, {0x0}},
+  {"text/html", ".htm", 0, {0x0}},
+  {"text/html", ".html", 0, {0x0}},
+  {"text/x-makefile", "makefile", 0, {0x0}},
+  {"application/atom+xml", ".atom", 0, {0x0}},
+  {"application/rdf+xml", ".rdf", 0, {0x0}},
+  {"application/javascript", ".js", 0, {0x0}},
+  {"application/json", ".json", 0, {0x0}},
+  {"application/octet-stream", ".bin", 0, {0x0}},
+  {"application/x-object", ".o", 0, {0x0}},
+  {"text/x-sh", ".sh", 0, {0x0}},
+  {"text/x-python", ".py", 0, {0x0}},
+  {"text/x-perl", ".pl", 0, {0x0}},
+  {"text/x-perl", ".pm", 0, {0x0}},
+  {"application/pdf", ".pdf", 0, {0x0}},
+  {"application/ctx", ".ctx", 0, {0x0}},
+  {"text/xml", ".xml",     0, {0x0}},
+  {"video/mp4", ".mp4",    0, {0x0}},
+  {"video/ogg", ".ogv",    0, {0x0}},
+  {"audio/sp-midi", ".mid",  0, {0x0}},
+  {"audio/x-wav", ".wav",  0, {0x0}},
+  {"audio/ogg", ".ogg",    0, {0x0}},
+  {"audio/ogg", ".opus",   0, {0x0}},
+  {"audio/ogg", ".oga",    0, {0x0}},
+  {"audio/mpeg", ".mp1",   0, {0x0}},
+  {"audio/m3u", ".m3u",    0, {0x0}},
+  {"audio/mpeg", ".mp2",   0, {0x0}},
+  {"audio/mpeg", ".mp3",   0, {0x0}},
+  {"audio/mpeg", ".m4a",   0, {0x0}},
+  {"audio/mpeg", ".mpga",  0, {0x0}},
+  {"audio/mpeg", ".mpega", 0, {0x0}},
+  {"font/otf", ".otf", 0,{0x0}},
+  {"font/ttf", ".ttf", 0,{0x0}},
+  // inode-directory
+};
+
+static int ctx_path_is_dir (const char *path)
+{
+  struct stat stat_buf;
+  if (!path || path[0]==0) return 0;
+  lstat (path, &stat_buf);
+  return S_ISDIR (stat_buf.st_mode);
+}
+
+static int ctx_path_is_exec (const char *path)
+{
+  struct stat stat_buf;
+  if (!path || path[0]==0) return 0;
+  lstat (path, &stat_buf);
+  return stat_buf.st_mode & 0x1;
+}
+
+const char *ctx_guess_media_type (const char *path, const char *content, int len)
+{
+  const char *extension_match = NULL;
+  if (path && strrchr (path, '.'))
+  {
+    char *pathdup = strdup (strrchr(path, '.'));
+    for (int i = 0; pathdup[i]; i++) pathdup[i]=tolower(pathdup[i]);
+    for (unsigned int i = 0; i < sizeof (ctx_magics)/sizeof(ctx_magics[0]);i++)
+    {
+      if (ctx_magics[i].ext1 && !strcmp (ctx_magics[i].ext1, pathdup))
+      {
+        extension_match = ctx_magics[i].mime_type;
+      }
+    }
+    free (pathdup);
+  }
+
+  if (len > 16)
+  {
+    for (unsigned int i = 0; i < sizeof (ctx_magics)/sizeof(ctx_magics[0]);i++)
+    {
+       if (ctx_magics[i].len) // skip extension only matches
+       if (!memcmp (content, ctx_magics[i].magic, ctx_magics[i].len))
+       {
+         return ctx_magics[i].mime_type;
+       }
+    }
+  }
+
+  if (extension_match && !strcmp (extension_match, "application/ctx"))
+  {
+          fprintf (stderr, "!!\n");
+    //if (!ctx_path_is_exec (path))
+    //  extension_match = NULL;
+  }
+
+  if (extension_match) return extension_match;
+
+
+  int non_ascii=0;
+  for (int i = 0; i < len; i++)
+  {
+    int p = content[i];
+    if (p > 127) non_ascii = 1;
+    if (p == 0) non_ascii = 1;
+  }
+  if (non_ascii)
+    return "application/octet-stream";
+  return "text/plain";
+}
+
+
+const char *ctx_path_get_media_type (const char *path)
+{
+  char *content = NULL;
+  long length = 0;
+
+  /* XXX : code duplication, factor out in separate fun */
+  if (path && strrchr (path, '.'))
+  {
+    char *pathdup = strdup (strrchr(path, '.'));
+    for (int i = 0; pathdup[i]; i++) pathdup[i]=tolower(pathdup[i]);
+    for (unsigned int i = 0; i < sizeof (ctx_magics)/sizeof(ctx_magics[0]);i++)
+    {
+      if (ctx_magics[i].ext1 && !strcmp (ctx_magics[i].ext1, pathdup))
+      {
+        free (pathdup);
+        return ctx_magics[i].mime_type;
+      }
+    }
+    free (pathdup);
+  }
+  if (ctx_path_is_dir (path))
+    return "inode/directory";
+
+  ctx_get_contents2 (path, (uint8_t**)&content, &length, 32);
+  if (content)
+  {
+  const char *guess = ctx_guess_media_type (path, content, length);
+  free (content);
+  return guess;
+  }
+  return "application/none";
+}
+
+CtxMediaTypeClass ctx_media_type_class (const char *media_type)
+{
+  CtxMediaTypeClass ret = CTX_MEDIA_TYPE_NONE;
+  if (!media_type) return ret;
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_IMAGE;
+    if (media_type[0]!='i')ret = 0;
+    if (media_type[1]!='m')ret = 0;
+    /*
+    if (media_type[2]!='a')ret = 0;
+    if (media_type[3]!='g')ret = 0;
+    if (media_type[4]!='e')ret = 0;*/
+  }
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_VIDEO;
+    if (media_type[0]!='v')ret = 0;
+    if (media_type[1]!='i')ret = 0;
+    /*
+    if (media_type[2]!='d')ret = 0;
+    if (media_type[3]!='e')ret = 0;
+    if (media_type[4]!='o')ret = 0;*/
+  }
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_AUDIO;
+    if (media_type[0]!='a')ret = 0;
+    if (media_type[1]!='u')ret = 0;
+    /*
+    if (media_type[2]!='d')ret = 0;
+    if (media_type[3]!='i')ret = 0;
+    if (media_type[4]!='o')ret = 0;*/
+  }
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_TEXT;
+    if (media_type[0]!='t')ret = 0;
+    if (media_type[1]!='e')ret = 0;
+    /*
+    if (media_type[2]!='x')ret = 0;
+    if (media_type[3]!='t')ret = 0;*/
+  }
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_APPLICATION;
+    if (media_type[0]!='a')ret = 0;
+    if (media_type[1]!='p')ret = 0;
+    /*
+    if (media_type[2]!='p')ret = 0;
+    if (media_type[3]!='l')ret = 0;*/
+  }
+  if (!ret){
+    ret = CTX_MEDIA_TYPE_INODE;
+    if (media_type[0]!='i')ret = 0;
+    if (media_type[1]!='n')ret = 0;
+    /*
+    if (media_type[2]!='o')ret = 0;
+    if (media_type[3]!='d')ret = 0;
+    if (media_type[4]!='e')ret = 0;*/
+  }
+  return ret;
+}
+
 #endif
 
 #endif // CTX_IMPLEMENTATION
@@ -35106,9 +36483,21 @@ typedef enum CtxClientFlags {
   ITK_CLIENT_MAXIMIZED    = 1<<2,
   ITK_CLIENT_ICONIFIED    = 1<<3,
   ITK_CLIENT_SHADED       = 1<<4,
-  ITK_CLIENT_TITLEBAR     = 1<<5
+  ITK_CLIENT_TITLEBAR     = 1<<5,
+  ITK_CLIENT_LAYER2       = 1<<6,  // used for having a second set
+                                   // to draw - useful for splitting
+                                   // scrolled and HUD items
+                                   // with HUD being LAYER2
+                                  
+  ITK_CLIENT_KEEP_ALIVE   = 1<<7,  // do not automatically
+  ITK_CLIENT_FINISHED     = 1<<8,  // do not automatically
+                                   // remove after process quits
+  ITK_CLIENT_PRELOAD      = 1<<9
 } CtxClientFlags;
 
+typedef struct _CtxClient CtxClient;
+typedef void (*CtxClientFinalize)(CtxClient *client, void *user_data);
+
 struct _CtxClient {
   VT    *vt;
   Ctx   *ctx;
@@ -35132,6 +36521,8 @@ struct _CtxClient {
   long   drawn_rev;
   int    id;
   int    internal; // render a settings window rather than a vt
+  void  *user_data;
+  CtxClientFinalize finalize;
 #if CTX_THREADS
   mtx_t  mtx;
 #endif
@@ -35140,7 +36531,6 @@ struct _CtxClient {
 #endif
 };
 
-typedef struct _CtxClient CtxClient;
 
 
 extern CtxList *clients;
@@ -35149,13 +36539,21 @@ extern CtxClient *active_tab;
 
 
 int ctx_client_resize (int id, int width, int height);
+void ctx_client_set_font_size (int id, float font_size);
+float ctx_client_get_font_size (int id);
 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);
+CtxClient *ctx_client_new (Ctx *ctx,
+                           const char *commandline,
+                           int x, int y, int width, int height,
+                           float font_size,
+                           CtxClientFlags flags,
+                           void *user_data,
+                           CtxClientFinalize client_finalize);
+CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int width, int height, float 
font_size, CtxClientFlags flags, void *user_data,
+                CtxClientFinalize client_finalize);
 int ctx_clients_need_redraw (Ctx *ctx);
 
 extern float ctx_shape_cache_rate;
@@ -35169,6 +36567,8 @@ float ctx_client_max_y_pos (Ctx *ctx);
 
 CtxClient *client_by_id (int id);
 
+int ctx_clients_draw (Ctx *ctx, int layer2);
+
 void ctx_client_remove (Ctx *ctx, CtxClient *client);
 
 int ctx_client_height (int id);
@@ -35677,6 +37077,7 @@ struct _VT
 
   CtxList   *saved_lines;
   int       in_alt_screen;
+  int       had_alt_screen;
   int       saved_line_count;
   CtxList   *lines;
   int       line_count;
@@ -35809,6 +37210,7 @@ struct _VT
 
   int popped;
 
+
   /* used to make runs of background on one line be drawn
    * as a single filled rectangle
    */
@@ -35825,6 +37227,7 @@ VT *vt_new (const char *command, int width, int height, float font_size, float l
 
 void vt_open_log (VT *vt, const char *path);
 
+int         ctx_vt_had_alt_screen (VT *vt);
 void        vt_set_px_size        (VT *vt, int width, int height);
 void        vt_set_term_size      (VT *vt, int cols, int rows);
 
@@ -35943,7 +37346,7 @@ static void vt_resize (VT *vt, int cols, int rows, int px_width, int px_height)
  */
 
 
-#ifndef NO_SDL
+#if CTX_SDL
 #include <SDL.h>
 #include <zlib.h>
 
@@ -35977,7 +37380,7 @@ static int ydec (const void *srcp, void *dstp, int count)
   return out_len;
 }
 
-#ifndef NO_SDL
+#if CTX_SDL
 static SDL_AudioDeviceID speaker_device = 0;
 #endif
 
@@ -36177,7 +37580,7 @@ static void sdl_audio_init ()
   static int done = 0;
   if (!done)
   {
-#ifndef NO_SDL
+#if CTX_SDL
   if (SDL_Init(SDL_INIT_AUDIO) < 0)
   {
     fprintf (stderr, "sdl audio init fail\n");
@@ -36191,7 +37594,7 @@ void vt_audio_task (VT *vt, int click)
 {
   if (!vt) return;
   AudioState *audio = &vt->audio;
-#ifndef NO_SDL
+#if CTX_SDL
 
   if (audio->mic)
   {
@@ -37504,15 +38907,6 @@ void vt_audio (VT *vt, const char *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
-
 #if !__COSMOPOLITAN__
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -37782,8 +39176,6 @@ static Image *image_add (int width,
   return image;
 }
 
-
-
 void vtpty_resize (void *data, int cols, int rows, int px_width, int px_height)
 {
   VtPty *vtpty = data;
@@ -37903,7 +39295,7 @@ long vt_rev (VT *vt)
 
 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);
+uint32_t ctx_strhash (const char *utf8);
 
 static void vt_set_title (VT *vt, const char *new_title)
 {
@@ -38314,7 +39706,6 @@ void vt_set_term_size (VT *vt, int icols, int irows)
   if (vt->rows == irows && vt->cols == icols)
     return;
 
-
   if (vt->state == vt_state_ctx)
   {
     // we should queue a pending resize instead,
@@ -38327,7 +39718,7 @@ void vt_set_term_size (VT *vt, int icols, int irows)
 
   while (irows > vt->rows)
     {
-      if (vt->scrollback_count)
+      if (vt->scrollback_count && vt->scrollback)
         {
           vt->scrollback_count--;
           ctx_list_append (&vt->lines, vt->scrollback->data);
@@ -38371,8 +39762,6 @@ void vt_set_px_size (VT *vt, int width, int height)
   vt_set_term_size (vt, cols, rows);
 }
 
-
-
 static void vt_argument_buf_reset (VT *vt, const char *start)
 {
   if (start)
@@ -39688,6 +41077,7 @@ qagain:
                         vt->line_count++;
                       }
                     vt->in_alt_screen = 1;
+                    vt->had_alt_screen = 1;
                     vt_line_feed (vt);
                     _vt_move_to (vt, 1, 1);
                     vt_carriage_return (vt);
@@ -40088,7 +41478,7 @@ static void _vt_rev_htab (VT *vt)
     {
       vt->cursor_x--;
     }
-  while ( ! vt->tabs[ (int) vt->cursor_x-1] && vt->cursor_x > 1);
+  while ( vt->cursor_x > 1 && ! vt->tabs[ (int) vt->cursor_x-1]);
   if (vt->cursor_x < VT_MARGIN_LEFT)
     { vt_carriage_return (vt); }
 }
@@ -40591,7 +41981,7 @@ static void vt_line_feed (VT *vt)
 
 //#include "vt-encodings.h"
 
-#ifndef NO_SDL
+#if CTX_SDL
 static void vt_state_apc_audio (VT *vt, int byte)
 {
   if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
@@ -43030,7 +44420,14 @@ void vt_destroy (VT *vt)
 
 int vt_get_line_count (VT *vt)
 {
-  return vt->line_count;
+  int max_pop = 0;
+  int no = 0;
+  for (CtxList *l = vt->lines; l; l = l->next, no++)
+  {
+    CtxString *str = l->data;
+    if (str->str[0]) max_pop = no;
+  }
+  return max_pop + 1;
 }
 
 const char *vt_get_line (VT *vt, int no)
@@ -45331,6 +46728,11 @@ static void scrollbar_leave (CtxEvent *event, void *data, void *data2)
 }
 #endif
 
+int ctx_vt_had_alt_screen (VT *vt)
+{
+  return vt?vt->had_alt_screen:0;
+}
+
 static void scrollbar_drag (CtxEvent *event, void *data, void *data2)
 {
   VT *vt = data;
@@ -46111,9 +47513,7 @@ void vt_set_ctx (VT *vt, Ctx *ctx)
 #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
@@ -46142,6 +47542,8 @@ extern Ctx *ctx;
 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_kms    (Ctx *ctx);
+int  ctx_renderer_is_tiled  (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);
@@ -46266,10 +47668,13 @@ 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)
+                           float font_size,
+                           CtxClientFlags flags,
+                           void *user_data,
+                           CtxClientFinalize finalize)
 {
   static int global_id = 0;
-  float font_size = ctx_get_font_size (ctx);
+  if (font_size <= 0.0) font_size = ctx_get_font_size (ctx);
   CtxClient *client = calloc (sizeof (CtxClient), 1);
   ctx_list_append (&clients, client);
   client->id = global_id++;
@@ -46279,6 +47684,8 @@ CtxClient *ctx_client_new (Ctx *ctx,
   client->ctx = ctx;
   client->width = width;
   client->height = height;
+  client->user_data = user_data;
+  client->finalize = finalize;
 
   if (ctx_renderer_is_term (ctx))
   {
@@ -46295,7 +47702,7 @@ CtxClient *ctx_client_new (Ctx *ctx,
   return client;
 }
 
-CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, 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, float 
font_size, CtxClientFlags flags, void *user_data, CtxClientFinalize finalize)
 {
   CtxString *string = ctx_string_new ("");
   for (int i = 0; argv[i]; i++)
@@ -46313,7 +47720,7 @@ CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int w
        }
     }
   }
-  CtxClient *ret = ctx_client_new (ctx, string->str, x, y, width, height, flags);
+  CtxClient *ret = ctx_client_new (ctx, string->str, x, y, width, height, font_size, flags, user_data, 
finalize);
   ctx_string_free (string, 1);
   return ret;
 }
@@ -46433,6 +47840,8 @@ void ctx_client_remove (Ctx *ctx, CtxClient *client)
   if (client->recording)
     ctx_free (client->recording);
 #endif
+  if (client->finalize)
+     client->finalize (client, client->user_data);
 
   ctx_list_remove (&clients, client);
 
@@ -46620,6 +48029,23 @@ void ctx_client_move (int id, int x, int y)
    }
 }
 
+void ctx_client_set_font_size (int id, float font_size)
+{
+   CtxClient *client = ctx_client_by_id (id);
+   if (client->vt)
+   {
+     if (vt_get_font_size (client->vt) != font_size)
+       vt_set_font_size (client->vt, font_size);
+   }
+}
+float ctx_client_get_font_size (int id)
+{
+   CtxClient *client = ctx_client_by_id (id);
+   if (client->vt)
+     return vt_get_font_size (client->vt);
+   return 14.0;
+}
+
 int ctx_client_resize (int id, int width, int height)
 {
    CtxClient *client = ctx_client_by_id (id);
@@ -46939,7 +48365,7 @@ void ctx_client_handle_event (Ctx *ctx, CtxEvent *ctx_event, const char *event)
 
   if (!strcmp (event, "F11"))
   {
-#ifndef NO_SDL
+#if CTX_SDL
     if (ctx_renderer_is_sdl (ctx))
     {
       ctx_sdl_set_fullscreen (ctx, !ctx_sdl_get_fullscreen (ctx));
@@ -46970,7 +48396,7 @@ void ctx_client_handle_event (Ctx *ctx, CtxEvent *ctx_event, const char *event)
         }
     }
   else if (!strcmp (event, "shift-control-t") ||
-           ((ctx_renderer_is_fb (ctx) || ctx_renderer_is_term (ctx))
+           ((ctx_renderer_is_fb (ctx) || ctx_renderer_is_term (ctx) || ctx_renderer_is_kms (ctx))
            &&   !strcmp (event, "control-t") ))
   {
     //XXX add_tab (vt_find_shell_command(), 1);
@@ -47153,7 +48579,7 @@ static void key_press (CtxEvent *event, void *data1, void *data2)
 }
 #endif
 
-int ctx_clients_draw (Ctx *ctx)
+int ctx_clients_draw (Ctx *ctx, int layer2)
 {
   _ctx_font_size = ctx_get_font_size (ctx);
   float titlebar_height = _ctx_font_size;
@@ -47186,6 +48612,18 @@ int ctx_clients_draw (Ctx *ctx)
   {
     CtxClient *client = l->data;
     VT *vt = client->vt;
+
+    if (layer2)
+    {
+      if (!flag_is_set (client->flags, ITK_CLIENT_LAYER2))
+        continue;
+    }
+    else
+    {
+      if (flag_is_set (client->flags, ITK_CLIENT_LAYER2))
+        continue;
+    }
+
     if (vt && !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED))
     {
       if (flag_is_set(client->flags, ITK_CLIENT_SHADED))
@@ -47195,7 +48633,6 @@ int ctx_clients_draw (Ctx *ctx)
       else
       {
         ctx_client_draw (ctx, client, client->x, client->y);
-      }
 
       // resize regions
       if (client == active &&
@@ -47271,6 +48708,8 @@ int ctx_clients_draw (Ctx *ctx)
 
       }
 
+      }
+
       if (client->flags & ITK_CLIENT_TITLEBAR)
         ctx_client_titlebar_draw (ctx, client, client->x, client->y, client->width, titlebar_height);
     }
@@ -47330,16 +48769,22 @@ int ctx_clients_need_redraw (Ctx *ctx)
      if (client->vt)
        {
          if (vt_is_done (client->vt))
-           ctx_list_prepend (&to_remove, client);
+         {
+           if ((client->flags & ITK_CLIENT_KEEP_ALIVE))
+           {
+             client->flags |= ITK_CLIENT_FINISHED;
+           }
+           else
+           {
+             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)
    {
+     changes++;
+     ctx_client_remove (ctx, to_remove->data);
      ctx_list_remove (&to_remove, to_remove->data);
    }
 
@@ -47459,7 +48904,7 @@ static int ctx_clients_handle_events_fun (void *data)
 
 void ctx_clients_handle_events (Ctx *ctx)
 {
-#if CTX_THREADS==0
+#if 1 //#if CTX_THREADS==0
     ctx_client_handle_events_iteration (ctx);
 #else
     static thrd_t tid = 0;


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