From 8ed49158e800164a32c77945d0badf0796b093cb Mon Sep 17 00:00:00 2001
From: Sam Hocevar <sam@hocevar.net>
Date: Wed, 27 Jun 2007 17:40:14 +0000
Subject: [PATCH]   * X11 driver improvements courtesy of Ben Wiley Sittler.
 Reworked them a     bit myself, too.

---
 caca/driver_x11.c | 396 ++++++++++++++++++++++++++++++----------------
 1 file changed, 263 insertions(+), 133 deletions(-)

diff --git a/caca/driver_x11.c b/caca/driver_x11.c
index c02ce44..372ae02 100644
--- a/caca/driver_x11.c
+++ b/caca/driver_x11.c
@@ -1,6 +1,7 @@
 /*
  *  libcaca       Colour ASCII-Art library
- *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
+ *  Copyright (c) 2002-2007 Sam Hocevar <sam@zoy.org>
+ *                2007 Ben Wiley Sittler <bsittler@gmail.com>
  *                All Rights Reserved
  *
  *  $Id$
@@ -30,6 +31,7 @@
 
 #include <stdio.h> /* BUFSIZ */
 #include <stdlib.h>
+#include <string.h>
 
 #include "caca.h"
 #include "caca_internals.h"
@@ -40,6 +42,8 @@
  * Local functions
  */
 static int x11_error_handler(Display *, XErrorEvent *);
+static void x11_put_glyph(caca_display_t *, int, int, int, int, int,
+                          uint32_t, uint32_t);
 
 struct driver_private
 {
@@ -59,8 +63,12 @@ struct driver_private
 #if defined(HAVE_X11_XKBLIB_H)
     Bool autorepeat;
 #endif
+    uint32_t max_char;
 };
 
+#define UNICODE_XLFD_SUFFIX "-iso10646-1"
+#define LATIN_1_XLFD_SUFFIX "-iso8859-1"
+
 static int x11_init_graphics(caca_display_t *dp)
 {
     Colormap colormap;
@@ -99,6 +107,8 @@ static int x11_init_graphics(caca_display_t *dp)
     /* Parse our font list */
     for( ; ; parser++)
     {
+        unsigned int font_max_char;
+
         if(!*parser)
         {
             XSetErrorHandler(old_error_handler);
@@ -117,13 +127,47 @@ static int x11_init_graphics(caca_display_t *dp)
             continue;
         }
 
+        if((strlen(*parser) > sizeof(UNICODE_XLFD_SUFFIX))
+             && !strcasecmp(*parser + strlen(*parser)
+                          - strlen(UNICODE_XLFD_SUFFIX), UNICODE_XLFD_SUFFIX))
+            dp->drv.p->max_char = 0xffff;
+        else if((strlen(*parser) > sizeof(LATIN_1_XLFD_SUFFIX))
+                 && !strcasecmp(*parser + strlen(*parser)
+                        - strlen(LATIN_1_XLFD_SUFFIX), LATIN_1_XLFD_SUFFIX))
+            dp->drv.p->max_char = 0xff;
+        else
+            dp->drv.p->max_char = 0x7f;
+
+        font_max_char = 
+            (((unsigned int)dp->drv.p->font_struct->max_byte1) << 8)
+             | dp->drv.p->font_struct->max_char_or_byte2;
+        if(font_max_char && (font_max_char < dp->drv.p->max_char))
+            dp->drv.p->max_char = font_max_char;
+
         break;
     }
 
     /* Reset the default X11 error handler */
     XSetErrorHandler(old_error_handler);
+    
+    dp->drv.p->font_width = 0;
+    if(dp->drv.p->font_struct->per_char
+        && !dp->drv.p->font_struct->min_byte1
+        && dp->drv.p->font_struct->min_char_or_byte2 <= 0x21
+        && dp->drv.p->font_struct->max_char_or_byte2 >= 0x7e)
+    {
+        for(i = 0x21; i < 0x7f; i++)
+        {
+            int cw = dp->drv.p->font_struct->per_char[i
+                           - dp->drv.p->font_struct->min_char_or_byte2].width;
+            if(cw > dp->drv.p->font_width)
+                dp->drv.p->font_width = cw;
+        }
+    }
+
+    if(!dp->drv.p->font_width)
+        dp->drv.p->font_width = dp->drv.p->font_struct->max_bounds.width;
 
-    dp->drv.p->font_width = dp->drv.p->font_struct->max_bounds.width;
     dp->drv.p->font_height = dp->drv.p->font_struct->max_bounds.ascent
                          + dp->drv.p->font_struct->max_bounds.descent;
     dp->drv.p->font_offset = dp->drv.p->font_struct->max_bounds.descent;
@@ -173,7 +217,7 @@ static int x11_init_graphics(caca_display_t *dp)
     {
         XEvent xevent;
         XNextEvent(dp->drv.p->dpy, &xevent);
-        if (xevent.type == MapNotify)
+        if(xevent.type == MapNotify)
             break;
     }
 
@@ -258,8 +302,10 @@ static void x11_display(caca_display_t *dp)
             XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
                            dp->drv.p->colors[bg]);
             XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
-                           x * dp->drv.p->font_width, y * dp->drv.p->font_height,
-                           len * dp->drv.p->font_width, dp->drv.p->font_height);
+                           x * dp->drv.p->font_width,
+                           y * dp->drv.p->font_height,
+                           len * dp->drv.p->font_width,
+                           dp->drv.p->font_height);
         }
     }
 
@@ -269,139 +315,17 @@ static void x11_display(caca_display_t *dp)
         unsigned int yoff = (y + 1) * dp->drv.p->font_height
                                     - dp->drv.p->font_offset;
         uint32_t *chars = dp->cv->chars + y * dp->cv->width;
+        uint32_t *attrs = dp->cv->attrs + y * dp->cv->width;
 
-        for(x = 0; x < dp->cv->width; x++, chars++)
+        for(x = 0; x < dp->cv->width; x++, chars++, attrs++)
         {
-            uint32_t *attrs = dp->cv->attrs + x + y * dp->cv->width;
-
-            /* Underline */
-            if(*attrs & CUCUL_UNDERLINE)
-                XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   (y + 1) * dp->drv.p->font_height - 1,
-                                   dp->drv.p->font_width, 1);
-
-            /* Skip spaces */
-            if(*chars <= 0x00000020)
-                continue;
-
-            if(*chars == CUCUL_MAGIC_FULLWIDTH)
-                continue;
-
             XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
                            dp->drv.p->colors[_cucul_attr_to_rgb12fg(*attrs)]);
 
-            /* Plain ASCII, no problem. */
-            if(*chars > 0x00000020 && *chars < 0x00000080)
-            {
-                char ch = (uint8_t)*chars;
-                XDrawString(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
-                            x * dp->drv.p->font_width, yoff, &ch, 1);
-                continue;
-            }
-
-            /* We want to be able to print a few special Unicode characters
-             * such as the CP437 gradients and half blocks. For unknown
-             * characters, just print '?'. */
-            switch(*chars)
-            {
-                case 0x000000b7: /* · */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width
-                                     + dp->drv.p->font_width / 2,
-                                   y * dp->drv.p->font_height
-                                     + dp->drv.p->font_height / 2, 2, 2);
-                    break;
-                case 0x00002500: /* ─ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   y * dp->drv.p->font_height
-                                     + dp->drv.p->font_height / 2 + 1,
-                                   dp->drv.p->font_width, 1);
-                    break;
-                case 0x00002580: /* ▀ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   y * dp->drv.p->font_height,
-                                   dp->drv.p->font_width,
-                                   dp->drv.p->font_height / 2);
-                    break;
-                case 0x00002584: /* ▄ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   (y + 1) * dp->drv.p->font_height
-                                           - dp->drv.p->font_height / 2,
-                                   dp->drv.p->font_width,
-                                   dp->drv.p->font_height / 2);
-                    break;
-                case 0x00002588: /* █ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   y * dp->drv.p->font_height,
-                                   dp->drv.p->font_width,
-                                   dp->drv.p->font_height);
-                    break;
-                case 0x0000258c: /* ▌ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   y * dp->drv.p->font_height,
-                                   dp->drv.p->font_width / 2,
-                                   dp->drv.p->font_height);
-                    break;
-                case 0x00002590: /* ▐ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   (x + 1) * dp->drv.p->font_width
-                                           - dp->drv.p->font_width / 2,
-                                   y * dp->drv.p->font_height,
-                                   dp->drv.p->font_width / 2,
-                                   dp->drv.p->font_height);
-                    break;
-                case 0x000025a0: /* ■ */
-                    XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width,
-                                   y * dp->drv.p->font_height
-                                     + dp->drv.p->font_height / 4,
-                                   dp->drv.p->font_width,
-                                   dp->drv.p->font_height / 2);
-                    break;
-                case 0x00002593: /* ▓ */
-                case 0x00002592: /* ▒ */
-                case 0x00002591: /* ░ */
-                {
-                    /* FIXME: this sucks utterly */
-                    int i, j, k = *chars - 0x00002591;
-                    for(j = dp->drv.p->font_height; j--; )
-                        for(i = dp->drv.p->font_width; i--; )
-                    {
-                        if(((i + 2 * (j & 1)) & 3) > k)
-                            continue;
-
-                        XDrawPoint(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                   dp->drv.p->gc,
-                                   x * dp->drv.p->font_width + i,
-                                   y * dp->drv.p->font_height + j);
-                    }
-                    break;
-                }
-                default:
-                {
-                    char ch;
-                    ch = '?';
-                    XDrawString(dp->drv.p->dpy, dp->drv.p->pixmap,
-                                dp->drv.p->gc,
-                                x * dp->drv.p->font_width, yoff, &ch, 1);
-                    break;
-                }
-            }
+            x11_put_glyph(dp, x * dp->drv.p->font_width,
+                          y * dp->drv.p->font_height, yoff,
+                          dp->drv.p->font_width, dp->drv.p->font_height,
+                          *attrs, *chars);
         }
     }
 
@@ -622,6 +546,212 @@ static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
     return 0;
 }
 
+static void x11_put_glyph(caca_display_t *dp, int x, int y, int yoff,
+                          int w, int h, uint32_t attr, uint32_t ch)
+{
+    Display *dpy = dp->drv.p->dpy;
+    Pixmap px = dp->drv.p->pixmap;
+    GC gc = dp->drv.p->gc;
+    int fw;
+    XChar2b ch16;
+
+    /* Underline */
+    if(attr & CUCUL_UNDERLINE)
+        XFillRectangle(dpy, px, gc, x, y + h - 1, w, 1);
+
+    /* Skip spaces and magic stuff */
+    if(ch <= 0x00000020)
+        return;
+
+    if(ch == CUCUL_MAGIC_FULLWIDTH)
+        return;
+
+    fw = w;
+    if(cucul_utf32_is_fullwidth(ch))
+        fw *= 2;
+
+    /* We want to be able to print a few special Unicode characters
+     * such as the CP437 gradients and half blocks. For unknown
+     * characters, print what cucul_utf32_to_ascii() returns. */
+
+    if(ch >= 0x2500 && ch <= 0x256c)
+    {
+        static uint8_t const udlr[] =
+        {
+            /* 0x2500 - 0x250f: ─ . │ . . . . . . . . . ┌ . . . */
+            0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+            /* 0x2510 - 0x251f: ┐ . . . └ . . . ┘ . . . ├ . . . */
+            0x14, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
+            0x44, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+            /* 0x2520 - 0x252f: . . . . ┤ . . . . . . . ┬ . . . */
+            0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+            /* 0x2530 - 0x253f: . . . . ┴ . . . . . . . ┼ . . . */
+            0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
+            /* 0x2540 - 0x254f: . . . . . . . . . . . . . . . . */
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            /* 0x2550 - 0x255f: ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ */
+            0x0a, 0xa0, 0x12, 0x21, 0x22, 0x18, 0x24, 0x28,
+            0x42, 0x81, 0x82, 0x48, 0x84, 0x88, 0x52, 0xa1,
+            /* 0x2560 - 0x256c: ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ */
+            0xa2, 0x58, 0xa4, 0xa8, 0x1a, 0x25, 0x2a, 0x4a,
+            0x85, 0x8a, 0x5a, 0xa5, 0xaa,
+        };
+
+        uint16_t D = udlr[ch - 0x2500];
+
+        if(!D)
+            goto next_try;
+
+        if(D & 0x04)
+            XFillRectangle(dpy, px, gc, x, y + h / 2, fw / 2 + 1, 1);
+
+        if(D & 0x01)
+            XFillRectangle(dpy, px, gc,
+                           x + fw / 2, y + h / 2, (fw + 1) / 2, 1);
+
+        if(D & 0x40)
+            XFillRectangle(dpy, px, gc, x + fw / 2, y, 1, h / 2 + 1);
+
+        if(D & 0x10)
+            XFillRectangle(dpy, px, gc, x + fw / 2, y + h / 2, 1, (h + 1) / 2);
+
+#define STEPIF(a,b) (D&(a)?-1:(D&(b))?1:0)
+
+        if(D & 0x08)
+        {
+            XFillRectangle(dpy, px, gc, x, y - 1 + h / 2,
+                           fw / 2 + 1 + STEPIF(0xc0,0x20), 1);
+            XFillRectangle(dpy, px, gc, x, y + 1 + h / 2,
+                           fw / 2 + 1 + STEPIF(0x30,0x80), 1);
+        }
+
+        if(D & 0x02)
+        {
+            XFillRectangle(dpy, px, gc, x - STEPIF(0xc0,0x20) + fw / 2,
+                           y - 1 + h / 2, (fw + 1) / 2 + STEPIF(0xc0,0x20), 1);
+            XFillRectangle(dpy, px, gc, x - STEPIF(0x30,0x80) + fw / 2,
+                           y + 1 + h / 2, (fw + 1) / 2 + STEPIF(0x30,0x80), 1);
+        }
+
+        if(D & 0x80)
+        {
+            XFillRectangle(dpy, px, gc, x - 1 + fw / 2, y,
+                           1, h / 2 + 1 + STEPIF(0x0c,0x02));
+            XFillRectangle(dpy, px, gc, x + 1 + fw / 2, y,
+                           1, h / 2 + 1 + STEPIF(0x03,0x08));
+        }
+
+        if(D & 0x20)
+        {
+            XFillRectangle(dpy, px, gc, x - 1 + fw / 2,
+                           y - STEPIF(0x0c,0x02) + h / 2,
+                           1, (h + 1) / 2 + STEPIF(0x0c,0x02));
+            XFillRectangle(dpy, px, gc, x + 1 + fw / 2,
+                           y - STEPIF(0x03,0x08) + h / 2,
+                           1, (h + 1) / 2 + STEPIF(0x03,0x08));
+        }
+
+        return;
+    }
+next_try:
+
+    switch(ch)
+    {
+        case 0x000000b7: /* · */
+        case 0x00002219: /* ∙ */
+        case 0x000030fb: /* ・ */
+            XFillRectangle(dpy, px, gc, x + fw / 2 - 1, y + h / 2 - 1, 2, 2);
+            return;
+
+        case 0x00002261: /* ≡ */
+            XFillRectangle(dpy, px, gc, x + 1, y - 2 + h / 2, fw - 1, 1);
+            XFillRectangle(dpy, px, gc, x + 1, y + h / 2, fw - 1, 1);
+            XFillRectangle(dpy, px, gc, x + 1, y + 2 + h / 2, fw - 1, 1);
+            return;
+
+        case 0x00002580: /* ▀ */
+            XFillRectangle(dpy, px, gc, x, y, fw, h / 2);
+            return;
+
+        case 0x00002584: /* ▄ */
+            XFillRectangle(dpy, px, gc, x, y + h - h / 2, fw, h / 2);
+            return;
+
+        case 0x00002588: /* █ */
+        case 0x000025ae: /* ▮ */
+            XFillRectangle(dpy, px, gc, x, y, fw, h);
+            return;
+
+        case 0x0000258c: /* ▌ */
+            XFillRectangle(dpy, px, gc, x, y, fw / 2, h);
+            return;
+
+        case 0x00002590: /* ▐ */
+            XFillRectangle(dpy, px, gc, x + fw - fw / 2, y, fw / 2, h);
+            return;
+
+        case 0x000025a0: /* ■ */
+        case 0x000025ac: /* ▬ */
+            XFillRectangle(dpy, px, gc, x, y + h / 4, fw, h / 2);
+            return;
+
+        case 0x00002593: /* ▓ */
+        case 0x00002592: /* ▒ */
+        case 0x00002591: /* ░ */
+        {
+            /* FIXME: this sucks utterly */
+            int i, j, k = ch - 0x00002591;
+            for(j = h; j--; )
+                for(i = fw; i--; )
+            {
+                if(((i + 2 * (j & 1)) & 3) > k)
+                    continue;
+
+                XDrawPoint(dpy, px, gc, x + i, y + j);
+            }
+            return;
+        }
+
+        case 0x000025cb: /* ○ */
+        case 0x00002022: /* • */
+        case 0x000025cf: /* ● */
+        {
+            int d, xo, yo;
+
+            d = fw;
+            if(h < fw)
+                d = h;
+            if(d < 1)
+                d = 1;
+            xo = (fw - d) / 2;
+            yo = (h - d) / 2;
+            if(ch == 0x000025cb)
+                XDrawArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
+            else
+                XFillArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
+            return;
+        }
+    }
+
+    if(ch >= 0x00000020 && ch <= dp->drv.p->max_char)
+    {
+        /* ascii, latin-1 or unicode font (might draw a blank square) */
+        ch16.byte1 = (ch) >> 8;
+        ch16.byte2 = (ch) & 0xff;
+    }
+    else
+    {
+        ch16.byte1 = 0;
+        ch16.byte2 = cucul_utf32_to_ascii(ch);
+    }
+
+    XDrawString16(dpy, px, gc, x + (ch16.byte1 ? 0 : (fw - w) / 2), yoff, &ch16, 1);
+}
+
 /*
  * Driver initialisation
  */