"Fossies" - the Fresh Open Source Software Archive

Member "libcaca-0.99.beta20/caca/driver/x11.c" (21 Apr 2017, 32260 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "x11.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.99.beta19_vs_0.99.beta20.

    1 /*
    2  *  libcaca     Colour ASCII-Art library
    3  *  Copyright © 2002—2016 Sam Hocevar <sam@hocevar.net>
    4  *              2007 Ben Wiley Sittler <bsittler@gmail.com>
    5  *              All Rights Reserved
    6  *
    7  *  This library is free software. It comes without any warranty, to
    8  *  the extent permitted by applicable law. You can redistribute it
    9  *  and/or modify it under the terms of the Do What the Fuck You Want
   10  *  to Public License, Version 2, as published by Sam Hocevar. See
   11  *  http://www.wtfpl.net/ for more details.
   12  */
   13 
   14 /*
   15  *  This file contains the libcaca X11 input and output driver
   16  */
   17 
   18 #include "config.h"
   19 
   20 #if defined(USE_X11)
   21 
   22 #include <X11/Xlib.h>
   23 #include <X11/Xutil.h>
   24 #include <X11/keysym.h>
   25 #if defined HAVE_X11_XKBLIB_H
   26 #   include <X11/XKBlib.h>
   27 #endif
   28 
   29 #include <stdio.h> /* BUFSIZ */
   30 #include <stdlib.h>
   31 #include <string.h>
   32 #if defined HAVE_LOCALE_H
   33 #   include <locale.h>
   34 #endif
   35 
   36 #include "caca.h"
   37 #include "caca_internals.h"
   38 
   39 /*
   40  * Local functions
   41  */
   42 static int x11_error_handler(Display *, XErrorEvent *);
   43 static void x11_put_glyph(caca_display_t *, int, int, int, int, int,
   44                           uint32_t, uint32_t);
   45 
   46 struct driver_private
   47 {
   48     Display *dpy;
   49     Window window;
   50     Pixmap pixmap;
   51     GC gc;
   52     long int event_mask;
   53     int font_width, font_height;
   54     int colors[4096];
   55 #if defined X_HAVE_UTF8_STRING
   56     XFontSet font_set;
   57 #endif
   58     Font font;
   59     XFontStruct *font_struct;
   60     int font_offset;
   61     Cursor pointer;
   62     Atom wm_protocols;
   63     Atom wm_delete_window;
   64 #if defined HAVE_X11_XKBLIB_H
   65     Bool autorepeat;
   66 #endif
   67     uint32_t max_char;
   68     int cursor_flags;
   69     int dirty_cursor_x, dirty_cursor_y;
   70 #if defined X_HAVE_UTF8_STRING
   71     XIM im;
   72     XIC ic;
   73 #endif
   74 };
   75 
   76 #define UNICODE_XLFD_SUFFIX "-iso10646-1"
   77 #define LATIN_1_XLFD_SUFFIX "-iso8859-1"
   78 
   79 static int x11_init_graphics(caca_display_t *dp)
   80 {
   81     Colormap colormap;
   82     XSetWindowAttributes x11_winattr;
   83 #if defined X_HAVE_UTF8_STRING
   84     XVaNestedList list;
   85 #endif
   86     int (*old_error_handler)(Display *, XErrorEvent *);
   87     char const *fonts[] = { NULL, "8x13bold", "fixed", NULL }, **parser;
   88     char const *geometry;
   89     int width = caca_get_canvas_width(dp->cv);
   90     int height = caca_get_canvas_height(dp->cv);
   91     int i;
   92 
   93     dp->drv.p = malloc(sizeof(struct driver_private));
   94 
   95 #if defined HAVE_GETENV
   96     geometry = getenv("CACA_GEOMETRY");
   97     if(geometry && *geometry)
   98         sscanf(geometry, "%ux%u", &width, &height);
   99 #endif
  100 
  101     caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height);
  102     dp->resize.allow = 1;
  103     caca_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
  104     width = caca_get_canvas_width(dp->cv);
  105     height = caca_get_canvas_height(dp->cv);
  106     dp->resize.allow = 0;
  107 
  108 #if defined HAVE_LOCALE_H
  109     /* FIXME: some better code here would be:
  110      * locale_t old_locale = uselocale(newlocale(LC_CTYPE_MASK,
  111      *                                           "", (locale_t)0);
  112      * … but XOpenDisplay only works properly with setlocale(),
  113      * not uselocale(). */
  114     char const *old_locale = setlocale(LC_CTYPE, "");
  115 #endif
  116 
  117     dp->drv.p->dpy = XOpenDisplay(NULL);
  118     if(dp->drv.p->dpy == NULL)
  119         return -1;
  120 
  121 #if defined HAVE_LOCALE_H
  122     setlocale(LC_CTYPE, old_locale);
  123 #endif
  124 
  125 #if defined HAVE_GETENV
  126     fonts[0] = getenv("CACA_FONT");
  127     if(fonts[0] && *fonts[0])
  128         parser = fonts;
  129     else
  130 #endif
  131         parser = fonts + 1;
  132 
  133     /* Ignore font errors */
  134     old_error_handler = XSetErrorHandler(x11_error_handler);
  135 
  136     /* Parse our font list */
  137     for( ; ; parser++)
  138     {
  139 #if defined X_HAVE_UTF8_STRING
  140         char **missing_charset_list;
  141         char *def_string;
  142         int missing_charset_count;
  143 #endif
  144         uint32_t font_max_char;
  145 
  146         if(!*parser)
  147         {
  148             XSetErrorHandler(old_error_handler);
  149             XCloseDisplay(dp->drv.p->dpy);
  150             return -1;
  151         }
  152 
  153 #if defined X_HAVE_UTF8_STRING
  154         dp->drv.p->font_set = XCreateFontSet(dp->drv.p->dpy, *parser,
  155                                              &missing_charset_list,
  156                                              &missing_charset_count,
  157                                              &def_string);
  158         if (missing_charset_list)
  159             XFreeStringList(missing_charset_list);
  160 
  161         if (!dp->drv.p->font_set || missing_charset_count)
  162         {
  163             char buf[BUFSIZ];
  164 
  165             if (dp->drv.p->font_set)
  166                 XFreeFontSet(dp->drv.p->dpy, dp->drv.p->font_set);
  167             snprintf(buf, BUFSIZ - 1, "%s,*", *parser);
  168             dp->drv.p->font_set = XCreateFontSet(dp->drv.p->dpy, buf,
  169                                                  &missing_charset_list,
  170                                                  &missing_charset_count,
  171                                                  &def_string);
  172             if (missing_charset_list)
  173                 XFreeStringList(missing_charset_list);
  174         }
  175 
  176         if (dp->drv.p->font_set)
  177             break;
  178 #endif
  179 
  180         dp->drv.p->font = XLoadFont(dp->drv.p->dpy, *parser);
  181         if(!dp->drv.p->font)
  182             continue;
  183 
  184         dp->drv.p->font_struct = XQueryFont(dp->drv.p->dpy, dp->drv.p->font);
  185         if(!dp->drv.p->font_struct)
  186         {
  187             XUnloadFont(dp->drv.p->dpy, dp->drv.p->font);
  188             continue;
  189         }
  190 
  191         if((strlen(*parser) > sizeof(UNICODE_XLFD_SUFFIX))
  192              && !strcasecmp(*parser + strlen(*parser)
  193                           - strlen(UNICODE_XLFD_SUFFIX), UNICODE_XLFD_SUFFIX))
  194             dp->drv.p->max_char = 0xffff;
  195         else if((strlen(*parser) > sizeof(LATIN_1_XLFD_SUFFIX))
  196                  && !strcasecmp(*parser + strlen(*parser)
  197                         - strlen(LATIN_1_XLFD_SUFFIX), LATIN_1_XLFD_SUFFIX))
  198             dp->drv.p->max_char = 0xff;
  199         else
  200             dp->drv.p->max_char = 0x7f;
  201 
  202         font_max_char =
  203             (dp->drv.p->font_struct->max_byte1 << 8)
  204              | dp->drv.p->font_struct->max_char_or_byte2;
  205         if(font_max_char && (font_max_char < dp->drv.p->max_char))
  206             dp->drv.p->max_char = font_max_char;
  207 
  208         break;
  209     }
  210 
  211     /* Reset the default X11 error handler */
  212     XSetErrorHandler(old_error_handler);
  213 
  214     /* Set font width to the largest character in the set */
  215 #if defined X_HAVE_UTF8_STRING
  216     if (dp->drv.p->font_set)
  217     {
  218         /* Do not trust the fontset, because some fonts may have
  219          * irrelevantly large glyphs. Pango and rxvt use the same method. */
  220         static wchar_t const test[] =
  221         {
  222             '0', '1', '8', 'a', 'd', 'x', 'm', 'y', 'g', 'W', 'X', '\'', '_',
  223             0x00cd, 0x00e7, 0x00d5, 0x0114, 0x0177, /* Í ç Õ Ĕ ŷ */
  224             /* 0x0643, */ /* ﻙ */
  225             0x304c, 0x672c, /* が本 */
  226             0,
  227         };
  228 
  229         XRectangle ink, logical;
  230 
  231         dp->drv.p->font_width =
  232              (XmbTextEscapement(dp->drv.p->font_set, "CAca", 4) + 2) / 4;
  233         XwcTextExtents(dp->drv.p->font_set, test, sizeof(test) / sizeof(*test),
  234                        &ink, &logical);
  235         dp->drv.p->font_height = ink.height;
  236         dp->drv.p->font_offset = ink.height + ink.y;
  237     }
  238     else
  239 #endif
  240     {
  241         dp->drv.p->font_width = 0;
  242         if(dp->drv.p->font_struct->per_char
  243             && !dp->drv.p->font_struct->min_byte1
  244             && dp->drv.p->font_struct->min_char_or_byte2 <= 0x21
  245             && dp->drv.p->font_struct->max_char_or_byte2 >= 0x7e)
  246         {
  247             for(i = 0x21; i < 0x7f; i++)
  248             {
  249                 int cw = dp->drv.p->font_struct->per_char[i
  250                             - dp->drv.p->font_struct->min_char_or_byte2].width;
  251                 if(cw > dp->drv.p->font_width)
  252                     dp->drv.p->font_width = cw;
  253             }
  254         }
  255 
  256         if(!dp->drv.p->font_width)
  257             dp->drv.p->font_width = dp->drv.p->font_struct->max_bounds.width;
  258 
  259         dp->drv.p->font_height = dp->drv.p->font_struct->max_bounds.ascent
  260                                   + dp->drv.p->font_struct->max_bounds.descent;
  261         dp->drv.p->font_offset = dp->drv.p->font_struct->max_bounds.descent;
  262     }
  263 
  264     colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
  265     for(i = 0x000; i < 0x1000; i++)
  266     {
  267         XColor color;
  268         color.red = ((i & 0xf00) >> 8) * 0x1111;
  269         color.green = ((i & 0x0f0) >> 4) * 0x1111;
  270         color.blue = (i & 0x00f) * 0x1111;
  271         XAllocColor(dp->drv.p->dpy, colormap, &color);
  272         dp->drv.p->colors[i] = color.pixel;
  273     }
  274 
  275     x11_winattr.backing_store = Always;
  276     x11_winattr.background_pixel = dp->drv.p->colors[0x000];
  277     x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  278 
  279     dp->drv.p->window =
  280         XCreateWindow(dp->drv.p->dpy, DefaultRootWindow(dp->drv.p->dpy), 0, 0,
  281                       width * dp->drv.p->font_width,
  282                       height * dp->drv.p->font_height,
  283                       0, 0, InputOutput, 0,
  284                       CWBackingStore | CWBackPixel | CWEventMask,
  285                       &x11_winattr);
  286 
  287     dp->drv.p->wm_protocols =
  288         XInternAtom(dp->drv.p->dpy, "WM_PROTOCOLS", True);
  289     dp->drv.p->wm_delete_window =
  290         XInternAtom(dp->drv.p->dpy, "WM_DELETE_WINDOW", True);
  291 
  292     if(dp->drv.p->wm_protocols != None && dp->drv.p->wm_delete_window != None)
  293         XSetWMProtocols(dp->drv.p->dpy, dp->drv.p->window,
  294                         &dp->drv.p->wm_delete_window, 1);
  295 
  296     XStoreName(dp->drv.p->dpy, dp->drv.p->window, "caca for X");
  297 
  298     XSelectInput(dp->drv.p->dpy, dp->drv.p->window, StructureNotifyMask);
  299     XMapWindow(dp->drv.p->dpy, dp->drv.p->window);
  300 
  301     dp->drv.p->gc = XCreateGC(dp->drv.p->dpy, dp->drv.p->window, 0, NULL);
  302     XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->colors[0x888]);
  303 #if defined X_HAVE_UTF8_STRING
  304     if (!dp->drv.p->font_set)
  305 #endif
  306         XSetFont(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->font);
  307 
  308     for(;;)
  309     {
  310         XEvent xevent;
  311         XNextEvent(dp->drv.p->dpy, &xevent);
  312         if(xevent.type == MapNotify)
  313             break;
  314     }
  315 
  316 #if defined HAVE_X11_XKBLIB_H
  317     /* Disable autorepeat */
  318     XkbSetDetectableAutoRepeat(dp->drv.p->dpy, True, &dp->drv.p->autorepeat);
  319     if(!dp->drv.p->autorepeat)
  320         XAutoRepeatOff(dp->drv.p->dpy);
  321 #endif
  322 
  323     dp->drv.p->event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
  324           | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
  325           | ExposureMask;
  326 
  327     XSelectInput(dp->drv.p->dpy, dp->drv.p->window, dp->drv.p->event_mask);
  328 
  329     XSync(dp->drv.p->dpy, False);
  330 
  331     dp->drv.p->pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
  332                                       width * dp->drv.p->font_width,
  333                                       height * dp->drv.p->font_height,
  334                                       DefaultDepth(dp->drv.p->dpy,
  335                                       DefaultScreen(dp->drv.p->dpy)));
  336     dp->drv.p->pointer = None;
  337 
  338     dp->drv.p->cursor_flags = 0;
  339     dp->drv.p->dirty_cursor_x = -1;
  340     dp->drv.p->dirty_cursor_y = -1;
  341 
  342 #if defined X_HAVE_UTF8_STRING
  343     list = XVaCreateNestedList(0, XNFontSet, dp->drv.p->font_set, NULL);
  344     dp->drv.p->im = XOpenIM(dp->drv.p->dpy, NULL, NULL, NULL);
  345 
  346     if (dp->drv.p->im == NULL) {
  347       fprintf(stderr, "x11 driver error: unable to open input method\n");
  348       return -1;
  349     }
  350 
  351     dp->drv.p->ic = XCreateIC(dp->drv.p->im,
  352                           XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
  353                           XNClientWindow, dp->drv.p->window,
  354                           XNPreeditAttributes, list,
  355                           XNStatusAttributes, list,
  356                           NULL);
  357 
  358     if (dp->drv.p->ic == NULL) {
  359       fprintf(stderr, "x11 driver error: unable to create input context\n");
  360       return -1;
  361     }
  362 #endif
  363 
  364     return 0;
  365 }
  366 
  367 static int x11_end_graphics(caca_display_t *dp)
  368 {
  369     XSync(dp->drv.p->dpy, False);
  370 #if defined HAVE_X11_XKBLIB_H
  371     if(!dp->drv.p->autorepeat)
  372         XAutoRepeatOn(dp->drv.p->dpy);
  373 #endif
  374     XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
  375 #if defined X_HAVE_UTF8_STRING
  376     if (dp->drv.p->font_set)
  377         XFreeFontSet(dp->drv.p->dpy, dp->drv.p->font_set);
  378     else
  379 #endif
  380         XFreeFont(dp->drv.p->dpy, dp->drv.p->font_struct);
  381     XFreeGC(dp->drv.p->dpy, dp->drv.p->gc);
  382     XUnmapWindow(dp->drv.p->dpy, dp->drv.p->window);
  383     XDestroyWindow(dp->drv.p->dpy, dp->drv.p->window);
  384 #if defined X_HAVE_UTF8_STRING
  385     XDestroyIC(dp->drv.p->ic);
  386     XCloseIM(dp->drv.p->im);
  387 #endif
  388     XCloseDisplay(dp->drv.p->dpy);
  389 
  390     free(dp->drv.p);
  391 
  392     return 0;
  393 }
  394 
  395 static int x11_set_display_title(caca_display_t *dp, char const *title)
  396 {
  397     XStoreName(dp->drv.p->dpy, dp->drv.p->window, title);
  398     return 0;
  399 }
  400 
  401 static int x11_get_display_width(caca_display_t const *dp)
  402 {
  403     return caca_get_canvas_width(dp->cv) * dp->drv.p->font_width;
  404 }
  405 
  406 static int x11_get_display_height(caca_display_t const *dp)
  407 {
  408     return caca_get_canvas_height(dp->cv) * dp->drv.p->font_height;
  409 }
  410 
  411 static void x11_display(caca_display_t *dp)
  412 {
  413     uint32_t const *cvchars = caca_get_canvas_chars(dp->cv);
  414     uint32_t const *cvattrs = caca_get_canvas_attrs(dp->cv);
  415     int width = caca_get_canvas_width(dp->cv);
  416     int height = caca_get_canvas_height(dp->cv);
  417     int x, y, i, len;
  418 
  419     /* XXX: the magic value -1 is used to handle the cursor area */
  420     for(i = -1; i < caca_get_dirty_rect_count(dp->cv); i++)
  421     {
  422         int dx, dy, dw, dh;
  423 
  424         /* Get the dirty rectangle coordinates, either from the previous
  425          * cursor position, or from the canvas's list. */
  426         if(i == -1)
  427         {
  428             if(dp->drv.p->dirty_cursor_x < 0 || dp->drv.p->dirty_cursor_y < 0
  429                 || dp->drv.p->dirty_cursor_x >= width
  430                 || dp->drv.p->dirty_cursor_y >= height)
  431                 continue;
  432 
  433             dx = dp->drv.p->dirty_cursor_x;
  434             dy = dp->drv.p->dirty_cursor_y;
  435             dw = dh = 1;
  436 
  437             dp->drv.p->dirty_cursor_x = -1;
  438             dp->drv.p->dirty_cursor_y = -1;
  439         }
  440         else
  441         {
  442             caca_get_dirty_rect(dp->cv, i, &dx, &dy, &dw, &dh);
  443         }
  444 
  445         /* First draw the background colours. Splitting the process in two
  446          * loops like this is actually slightly faster. */
  447         for(y = dy; y < dy + dh; y++)
  448         {
  449             for(x = dx; x < dx + dw; x += len)
  450             {
  451                 uint32_t const *attrs = cvattrs + x + y * width;
  452                 uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
  453 
  454                 len = 1;
  455                 while(x + len < dx + dw
  456                        && caca_attr_to_rgb12_bg(attrs[len]) == bg)
  457                     len++;
  458 
  459                 XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  460                                dp->drv.p->colors[bg]);
  461                 XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
  462                                dp->drv.p->gc,
  463                                x * dp->drv.p->font_width,
  464                                y * dp->drv.p->font_height,
  465                                len * dp->drv.p->font_width,
  466                                dp->drv.p->font_height);
  467             }
  468         }
  469 
  470         /* Then print the foreground characters */
  471         for(y = dy; y < dy + dh; y++)
  472         {
  473             int yoff = (y + 1) * dp->drv.p->font_height
  474                                         - dp->drv.p->font_offset;
  475             uint32_t const *chars = cvchars + dx + y * width;
  476             uint32_t const *attrs = cvattrs + dx + y * width;
  477 
  478             for(x = dx; x < dx + dw; x++, chars++, attrs++)
  479             {
  480                 XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  481                            dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]);
  482 
  483                 x11_put_glyph(dp, x * dp->drv.p->font_width,
  484                               y * dp->drv.p->font_height, yoff,
  485                               dp->drv.p->font_width, dp->drv.p->font_height,
  486                               *attrs, *chars);
  487             }
  488         }
  489     }
  490 
  491     /* Print the cursor if necessary. */
  492     if(dp->drv.p->cursor_flags)
  493     {
  494         XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  495                        dp->drv.p->colors[0xfff]);
  496         x = caca_wherex(dp->cv);
  497         y = caca_wherey(dp->cv);
  498         XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
  499                        x * dp->drv.p->font_width, y * dp->drv.p->font_height,
  500                        dp->drv.p->font_width, dp->drv.p->font_height);
  501 
  502         /* Mark the area as dirty */
  503         dp->drv.p->dirty_cursor_x = x;
  504         dp->drv.p->dirty_cursor_y = y;
  505     }
  506 
  507     XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->window,
  508               dp->drv.p->gc, 0, 0,
  509               width * dp->drv.p->font_width,
  510               height * dp->drv.p->font_height,
  511               0, 0);
  512     XFlush(dp->drv.p->dpy);
  513 }
  514 
  515 static void x11_handle_resize(caca_display_t *dp)
  516 {
  517     Pixmap new_pixmap;
  518 
  519     new_pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
  520                                dp->resize.w * dp->drv.p->font_width,
  521                                dp->resize.h * dp->drv.p->font_height,
  522                                DefaultDepth(dp->drv.p->dpy,
  523                                             DefaultScreen(dp->drv.p->dpy)));
  524     XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, new_pixmap,
  525               dp->drv.p->gc, 0, 0,
  526               dp->resize.w * dp->drv.p->font_width,
  527               dp->resize.h * dp->drv.p->font_height, 0, 0);
  528     XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
  529     dp->drv.p->pixmap = new_pixmap;
  530 }
  531 
  532 static int x11_get_event(caca_display_t *dp, caca_privevent_t *ev)
  533 {
  534     int width = caca_get_canvas_width(dp->cv);
  535     int height = caca_get_canvas_height(dp->cv);
  536     XEvent xevent;
  537     char key;
  538 
  539     while(XCheckWindowEvent(dp->drv.p->dpy, dp->drv.p->window,
  540                             dp->drv.p->event_mask, &xevent) == True)
  541     {
  542         KeySym keysym;
  543 #if defined X_HAVE_UTF8_STRING
  544         int len;
  545 #endif
  546 
  547         /* Expose event */
  548         if(xevent.type == Expose)
  549         {
  550             XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap,
  551                       dp->drv.p->window, dp->drv.p->gc, 0, 0,
  552                       width * dp->drv.p->font_width,
  553                       height * dp->drv.p->font_height, 0, 0);
  554             continue;
  555         }
  556 
  557         /* Resize event */
  558         if(xevent.type == ConfigureNotify)
  559         {
  560             int w, h;
  561 
  562             w = (xevent.xconfigure.width + dp->drv.p->font_width / 3)
  563                   / dp->drv.p->font_width;
  564             h = (xevent.xconfigure.height + dp->drv.p->font_height / 3)
  565                   / dp->drv.p->font_height;
  566 
  567             if(!w || !h || (w == width && h == height))
  568                 continue;
  569 
  570             dp->resize.w = w;
  571             dp->resize.h = h;
  572             dp->resize.resized = 1;
  573 
  574             continue;
  575         }
  576 
  577         /* Check for mouse motion events */
  578         if(xevent.type == MotionNotify)
  579         {
  580             int newx = xevent.xmotion.x / dp->drv.p->font_width;
  581             int newy = xevent.xmotion.y / dp->drv.p->font_height;
  582 
  583             if(newx >= width)
  584                 newx = width - 1;
  585             if(newy >= height)
  586                 newy = height - 1;
  587 
  588             if(dp->mouse.x == newx && dp->mouse.y == newy)
  589                 continue;
  590 
  591             dp->mouse.x = newx;
  592             dp->mouse.y = newy;
  593 
  594             ev->type = CACA_EVENT_MOUSE_MOTION;
  595             ev->data.mouse.x = dp->mouse.x;
  596             ev->data.mouse.y = dp->mouse.y;
  597             return 1;
  598         }
  599 
  600         /* Check for mouse press and release events */
  601         if(xevent.type == ButtonPress)
  602         {
  603             ev->type = CACA_EVENT_MOUSE_PRESS;
  604             ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  605             return 1;
  606         }
  607 
  608         if(xevent.type == ButtonRelease)
  609         {
  610             ev->type = CACA_EVENT_MOUSE_RELEASE;
  611             ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  612             return 1;
  613         }
  614 
  615         /* Check for key press and release events */
  616         if(xevent.type == KeyPress)
  617             ev->type = CACA_EVENT_KEY_PRESS;
  618         else if(xevent.type == KeyRelease)
  619             ev->type = CACA_EVENT_KEY_RELEASE;
  620         else
  621             continue;
  622 
  623         if(XFilterEvent(&xevent, None) == True)
  624             continue;
  625 
  626 #if defined X_HAVE_UTF8_STRING
  627         len = Xutf8LookupString(dp->drv.p->ic, &xevent.xkey,
  628                                 ev->data.key.utf8, 8, NULL, NULL);
  629         ev->data.key.utf8[len] = 0;
  630         if (len)
  631         {
  632             ev->data.key.utf32 = caca_utf8_to_utf32(ev->data.key.utf8, NULL);
  633             if(ev->data.key.utf32 <= 0xff)
  634                 ev->data.key.ch = ev->data.key.utf32;
  635             else
  636                 ev->data.key.ch = 0;
  637             return 1;
  638         }
  639 #endif
  640         if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
  641         {
  642             ev->data.key.ch = key;
  643             ev->data.key.utf32 = key;
  644             ev->data.key.utf8[0] = key;
  645             ev->data.key.utf8[1] = '\0';
  646             return 1;
  647         }
  648 
  649         keysym = XLookupKeysym(&xevent.xkey, 0);
  650         switch(keysym)
  651         {
  652             case XK_F1:    ev->data.key.ch = CACA_KEY_F1;    break;
  653             case XK_F2:    ev->data.key.ch = CACA_KEY_F2;    break;
  654             case XK_F3:    ev->data.key.ch = CACA_KEY_F3;    break;
  655             case XK_F4:    ev->data.key.ch = CACA_KEY_F4;    break;
  656             case XK_F5:    ev->data.key.ch = CACA_KEY_F5;    break;
  657             case XK_F6:    ev->data.key.ch = CACA_KEY_F6;    break;
  658             case XK_F7:    ev->data.key.ch = CACA_KEY_F7;    break;
  659             case XK_F8:    ev->data.key.ch = CACA_KEY_F8;    break;
  660             case XK_F9:    ev->data.key.ch = CACA_KEY_F9;    break;
  661             case XK_F10:   ev->data.key.ch = CACA_KEY_F10;   break;
  662             case XK_F11:   ev->data.key.ch = CACA_KEY_F11;   break;
  663             case XK_F12:   ev->data.key.ch = CACA_KEY_F12;   break;
  664             case XK_F13:   ev->data.key.ch = CACA_KEY_F13;   break;
  665             case XK_F14:   ev->data.key.ch = CACA_KEY_F14;   break;
  666             case XK_F15:   ev->data.key.ch = CACA_KEY_F15;   break;
  667             case XK_Left:  ev->data.key.ch = CACA_KEY_LEFT;  break;
  668             case XK_Right: ev->data.key.ch = CACA_KEY_RIGHT; break;
  669             case XK_Up:    ev->data.key.ch = CACA_KEY_UP;    break;
  670             case XK_Down:  ev->data.key.ch = CACA_KEY_DOWN;  break;
  671             case XK_KP_Page_Up:
  672             case XK_Page_Up:      ev->data.key.ch = CACA_KEY_PAGEUP;   break;
  673             case XK_KP_Page_Down:
  674             case XK_Page_Down:    ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
  675             case XK_KP_Home:
  676             case XK_Home:         ev->data.key.ch = CACA_KEY_HOME;     break;
  677             case XK_KP_End:
  678             case XK_End:          ev->data.key.ch = CACA_KEY_END;      break;
  679 
  680             default: ev->type = CACA_EVENT_NONE; return 0;
  681         }
  682 
  683         ev->data.key.utf32 = 0;
  684         ev->data.key.utf8[0] = '\0';
  685         return 1;
  686     }
  687 
  688     while(XCheckTypedEvent(dp->drv.p->dpy, ClientMessage, &xevent))
  689     {
  690         if(xevent.xclient.message_type != dp->drv.p->wm_protocols)
  691             continue;
  692 
  693         if((Atom)xevent.xclient.data.l[0] == dp->drv.p->wm_delete_window)
  694         {
  695             ev->type = CACA_EVENT_QUIT;
  696             return 1;
  697         }
  698     }
  699 
  700     ev->type = CACA_EVENT_NONE;
  701     return 0;
  702 }
  703 
  704 static void x11_set_mouse(caca_display_t *dp, int flags)
  705 {
  706     Cursor no_ptr;
  707     Pixmap bm_no;
  708     XColor black, dummy;
  709     Colormap colormap;
  710     static char const empty[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  711 
  712     if(flags)
  713     {
  714         XDefineCursor(dp->drv.p->dpy,dp->drv.p->window, 0);
  715         return;
  716     }
  717 
  718     colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
  719     if(!XAllocNamedColor(dp->drv.p->dpy, colormap, "black", &black, &dummy))
  720     {
  721         return;
  722     }
  723     bm_no = XCreateBitmapFromData(dp->drv.p->dpy, dp->drv.p->window,
  724                                   empty, 8, 8);
  725     no_ptr = XCreatePixmapCursor(dp->drv.p->dpy, bm_no, bm_no,
  726                                  &black, &black, 0, 0);
  727     XDefineCursor(dp->drv.p->dpy, dp->drv.p->window, no_ptr);
  728     XFreeCursor(dp->drv.p->dpy, no_ptr);
  729     if(bm_no != None)
  730         XFreePixmap(dp->drv.p->dpy, bm_no);
  731     XFreeColors(dp->drv.p->dpy, colormap, &black.pixel, 1, 0);
  732 
  733     XSync(dp->drv.p->dpy, False);
  734 }
  735 
  736 static void x11_set_cursor(caca_display_t *dp, int flags)
  737 {
  738     dp->drv.p->cursor_flags = flags;
  739 }
  740 
  741 /*
  742  * XXX: following functions are local
  743  */
  744 
  745 static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
  746 {
  747     /* Ignore the error */
  748     return 0;
  749 }
  750 
  751 static void x11_put_glyph(caca_display_t *dp, int x, int y, int yoff,
  752                           int w, int h, uint32_t attr, uint32_t ch)
  753 {
  754     static uint8_t const udlr[] =
  755     {
  756         /* 0x2500 - 0x250f: ─ . │ . . . . . . . . . ┌ . . . */
  757         0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
  758         0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
  759         /* 0x2510 - 0x251f: ┐ . . . └ . . . ┘ . . . ├ . . . */
  760         0x14, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
  761         0x44, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
  762         /* 0x2520 - 0x252f: . . . . ┤ . . . . . . . ┬ . . . */
  763         0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
  764         0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
  765         /* 0x2530 - 0x253f: . . . . ┴ . . . . . . . ┼ . . . */
  766         0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
  767         0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
  768         /* 0x2540 - 0x254f: . . . . . . . . . . . . . . . . */
  769         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  770         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  771         /* 0x2550 - 0x255f: ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ */
  772         0x0a, 0xa0, 0x12, 0x21, 0x22, 0x18, 0x24, 0x28,
  773         0x42, 0x81, 0x82, 0x48, 0x84, 0x88, 0x52, 0xa1,
  774         /* 0x2560 - 0x256c: ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ */
  775         0xa2, 0x58, 0xa4, 0xa8, 0x1a, 0x25, 0x2a, 0x4a,
  776         0x85, 0x8a, 0x5a, 0xa5, 0xaa,
  777     };
  778 
  779     Display *dpy = dp->drv.p->dpy;
  780     Pixmap px = dp->drv.p->pixmap;
  781     GC gc = dp->drv.p->gc;
  782     int fw;
  783 
  784     /* Underline */
  785     if(attr & CACA_UNDERLINE)
  786         XFillRectangle(dpy, px, gc, x, y + h - 1, w, 1);
  787 
  788     /* Skip spaces and magic stuff */
  789     if(ch <= 0x00000020)
  790         return;
  791 
  792     if(ch == CACA_MAGIC_FULLWIDTH)
  793         return;
  794 
  795     fw = w;
  796     if(caca_utf32_is_fullwidth(ch))
  797         fw *= 2;
  798 
  799     /* We want to be able to print a few special Unicode characters
  800      * such as the CP437 gradients and half blocks. For unknown
  801      * characters, print what caca_utf32_to_ascii() returns. */
  802 
  803     if(ch >= 0x2500 && ch <= 0x256c && udlr[ch - 0x2500])
  804     {
  805         uint16_t D = udlr[ch - 0x2500];
  806 
  807         if(D & 0x04)
  808             XFillRectangle(dpy, px, gc, x, y + h / 2, fw / 2 + 1, 1);
  809 
  810         if(D & 0x01)
  811             XFillRectangle(dpy, px, gc,
  812                            x + fw / 2, y + h / 2, (fw + 1) / 2, 1);
  813 
  814         if(D & 0x40)
  815             XFillRectangle(dpy, px, gc, x + fw / 2, y, 1, h / 2 + 1);
  816 
  817         if(D & 0x10)
  818             XFillRectangle(dpy, px, gc, x + fw / 2, y + h / 2, 1, (h + 1) / 2);
  819 
  820 #define STEPIF(a,b) (D&(a)?-1:(D&(b))?1:0)
  821 
  822         if(D & 0x08)
  823         {
  824             XFillRectangle(dpy, px, gc, x, y - 1 + h / 2,
  825                            fw / 2 + 1 + STEPIF(0xc0,0x20), 1);
  826             XFillRectangle(dpy, px, gc, x, y + 1 + h / 2,
  827                            fw / 2 + 1 + STEPIF(0x30,0x80), 1);
  828         }
  829 
  830         if(D & 0x02)
  831         {
  832             XFillRectangle(dpy, px, gc, x - STEPIF(0xc0,0x20) + fw / 2,
  833                            y - 1 + h / 2, (fw + 1) / 2 + STEPIF(0xc0,0x20), 1);
  834             XFillRectangle(dpy, px, gc, x - STEPIF(0x30,0x80) + fw / 2,
  835                            y + 1 + h / 2, (fw + 1) / 2 + STEPIF(0x30,0x80), 1);
  836         }
  837 
  838         if(D & 0x80)
  839         {
  840             XFillRectangle(dpy, px, gc, x - 1 + fw / 2, y,
  841                            1, h / 2 + 1 + STEPIF(0x0c,0x02));
  842             XFillRectangle(dpy, px, gc, x + 1 + fw / 2, y,
  843                            1, h / 2 + 1 + STEPIF(0x03,0x08));
  844         }
  845 
  846         if(D & 0x20)
  847         {
  848             XFillRectangle(dpy, px, gc, x - 1 + fw / 2,
  849                            y - STEPIF(0x0c,0x02) + h / 2,
  850                            1, (h + 1) / 2 + STEPIF(0x0c,0x02));
  851             XFillRectangle(dpy, px, gc, x + 1 + fw / 2,
  852                            y - STEPIF(0x03,0x08) + h / 2,
  853                            1, (h + 1) / 2 + STEPIF(0x03,0x08));
  854         }
  855 
  856         return;
  857     }
  858 
  859     switch(ch)
  860     {
  861         case 0x000000b7: /* · */
  862         case 0x00002219: /* ∙ */
  863         case 0x000030fb: /* ・ */
  864             XFillRectangle(dpy, px, gc, x + fw / 2 - 1, y + h / 2 - 1, 2, 2);
  865             return;
  866 
  867         case 0x00002261: /* ≡ */
  868             XFillRectangle(dpy, px, gc, x + 1, y - 2 + h / 2, fw - 1, 1);
  869             XFillRectangle(dpy, px, gc, x + 1, y + h / 2, fw - 1, 1);
  870             XFillRectangle(dpy, px, gc, x + 1, y + 2 + h / 2, fw - 1, 1);
  871             return;
  872 
  873         case 0x00002580: /* ▀ */
  874             XFillRectangle(dpy, px, gc, x, y, fw, h / 2);
  875             return;
  876 
  877         case 0x00002584: /* ▄ */
  878             XFillRectangle(dpy, px, gc, x, y + h - h / 2, fw, h / 2);
  879             return;
  880 
  881         case 0x00002588: /* █ */
  882         case 0x000025ae: /* ▮ */
  883             XFillRectangle(dpy, px, gc, x, y, fw, h);
  884             return;
  885 
  886         case 0x0000258c: /* ▌ */
  887             XFillRectangle(dpy, px, gc, x, y, fw / 2, h);
  888             return;
  889 
  890         case 0x00002590: /* ▐ */
  891             XFillRectangle(dpy, px, gc, x + fw - fw / 2, y, fw / 2, h);
  892             return;
  893 
  894         case 0x000025a0: /* ■ */
  895         case 0x000025ac: /* ▬ */
  896             XFillRectangle(dpy, px, gc, x, y + h / 4, fw, h / 2);
  897             return;
  898 
  899         case 0x00002593: /* ▓ */
  900         case 0x00002592: /* ▒ */
  901         case 0x00002591: /* ░ */
  902         {
  903             /* FIXME: this sucks utterly */
  904             int i, j, k = ch - 0x00002591;
  905             for(j = h; j--; )
  906                 for(i = fw; i--; )
  907             {
  908                 if(((i + 2 * (j & 1)) & 3) > k)
  909                     continue;
  910 
  911                 XDrawPoint(dpy, px, gc, x + i, y + j);
  912             }
  913             return;
  914         }
  915 
  916         case 0x000025cb: /* ○ */
  917         case 0x00002022: /* • */
  918         case 0x000025cf: /* ● */
  919         {
  920             int d, xo, yo;
  921 
  922             d = fw >> (~ch & 0x1); /* XXX: hack */
  923             if(h < fw)
  924                 d = h;
  925             if(d < 1)
  926                 d = 1;
  927             xo = (fw - d) / 2;
  928             yo = (h - d) / 2;
  929             if(ch == 0x000025cb)
  930                 XDrawArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
  931             else
  932                 XFillArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
  933             return;
  934         }
  935     }
  936 
  937 #if defined X_HAVE_UTF8_STRING
  938     if (dp->drv.p->font_set)
  939     {
  940         wchar_t wch = ch;
  941         XwcDrawString(dpy, px, dp->drv.p->font_set, gc, x, yoff, &wch, 1);
  942     }
  943     else
  944 #endif
  945     {
  946         XChar2b ch16;
  947 
  948 #if !defined X_HAVE_UTF8_STRING
  949         if(ch > dp->drv.p->max_char)
  950         {
  951             ch16.byte1 = 0;
  952             ch16.byte2 = caca_utf32_to_ascii(ch);
  953         }
  954         else
  955 #endif
  956         {
  957             ch16.byte1 = (uint8_t)(ch >> 8);
  958             ch16.byte2 = (uint8_t)ch;
  959         }
  960 
  961         XDrawString16(dpy, px, gc, x, yoff, &ch16, 1);
  962     }
  963 }
  964 
  965 /*
  966  * Driver initialisation
  967  */
  968 
  969 int x11_install(caca_display_t *dp)
  970 {
  971 #if defined HAVE_GETENV
  972     if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
  973         return -1;
  974 #endif
  975 
  976     dp->drv.id = CACA_DRIVER_X11;
  977     dp->drv.driver = "x11";
  978 
  979     dp->drv.init_graphics = x11_init_graphics;
  980     dp->drv.end_graphics = x11_end_graphics;
  981     dp->drv.set_display_title = x11_set_display_title;
  982     dp->drv.get_display_width = x11_get_display_width;
  983     dp->drv.get_display_height = x11_get_display_height;
  984     dp->drv.display = x11_display;
  985     dp->drv.handle_resize = x11_handle_resize;
  986     dp->drv.get_event = x11_get_event;
  987     dp->drv.set_mouse = x11_set_mouse;
  988     dp->drv.set_cursor = x11_set_cursor;
  989 
  990     return 0;
  991 }
  992 
  993 #endif /* USE_X11 */
  994