source: libcaca/trunk/caca/driver_x11.c @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

  • Property svn:keywords set to Id
File size: 25.6 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2007 Sam Hocevar <sam@zoy.org>
4 *                2007 Ben Wiley Sittler <bsittler@gmail.com>
5 *                All Rights Reserved
6 *
7 *  $Id: driver_x11.c 2821 2008-09-27 13:12:46Z sam $
8 *
9 *  This library is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16/*
17 *  This file contains the libcaca X11 input and output driver
18 */
19
20#include "config.h"
21
22#if defined(USE_X11)
23
24#include <X11/Xlib.h>
25#include <X11/Xutil.h>
26#include <X11/keysym.h>
27#if defined(HAVE_X11_XKBLIB_H)
28#   include <X11/XKBlib.h>
29#endif
30
31#include <stdio.h> /* BUFSIZ */
32#include <stdlib.h>
33#include <string.h>
34
35#include "caca.h"
36#include "caca.h"
37#include "caca_internals.h"
38
39/*
40 * Local functions
41 */
42static int x11_error_handler(Display *, XErrorEvent *);
43static void x11_put_glyph(caca_display_t *, int, int, int, int, int,
44                          uint32_t, uint32_t);
45
46struct 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    Font font;
56    XFontStruct *font_struct;
57    int font_offset;
58    Cursor pointer;
59    Atom wm_protocols;
60    Atom wm_delete_window;
61#if defined(HAVE_X11_XKBLIB_H)
62    Bool autorepeat;
63#endif
64    uint32_t max_char;
65    int cursor_flags;
66};
67
68#define UNICODE_XLFD_SUFFIX "-iso10646-1"
69#define LATIN_1_XLFD_SUFFIX "-iso8859-1"
70
71static int x11_init_graphics(caca_display_t *dp)
72{
73    Colormap colormap;
74    XSetWindowAttributes x11_winattr;
75    int (*old_error_handler)(Display *, XErrorEvent *);
76    char const *fonts[] = { NULL, "8x13bold", "fixed" }, **parser;
77    char const *geometry;
78    int width = caca_get_canvas_width(dp->cv);
79    int height = caca_get_canvas_height(dp->cv);
80    int i;
81
82    dp->drv.p = malloc(sizeof(struct driver_private));
83
84#if defined(HAVE_GETENV)
85    geometry = getenv("CACA_GEOMETRY");
86    if(geometry && *geometry)
87        sscanf(geometry, "%ux%u", &width, &height);
88#endif
89
90    dp->resize.allow = 1;
91    caca_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
92    width = caca_get_canvas_width(dp->cv);
93    height = caca_get_canvas_height(dp->cv);
94    dp->resize.allow = 0;
95
96    dp->drv.p->dpy = XOpenDisplay(NULL);
97    if(dp->drv.p->dpy == NULL)
98        return -1;
99
100#if defined(HAVE_GETENV)
101    fonts[0] = getenv("CACA_FONT");
102    if(fonts[0] && *fonts[0])
103        parser = fonts;
104    else
105#endif
106        parser = fonts + 1;
107
108    /* Ignore font errors */
109    old_error_handler = XSetErrorHandler(x11_error_handler);
110
111    /* Parse our font list */
112    for( ; ; parser++)
113    {
114        uint32_t font_max_char;
115
116        if(!*parser)
117        {
118            XSetErrorHandler(old_error_handler);
119            XCloseDisplay(dp->drv.p->dpy);
120            return -1;
121        }
122
123        dp->drv.p->font = XLoadFont(dp->drv.p->dpy, *parser);
124        if(!dp->drv.p->font)
125            continue;
126
127        dp->drv.p->font_struct = XQueryFont(dp->drv.p->dpy, dp->drv.p->font);
128        if(!dp->drv.p->font_struct)
129        {
130            XUnloadFont(dp->drv.p->dpy, dp->drv.p->font);
131            continue;
132        }
133
134        if((strlen(*parser) > sizeof(UNICODE_XLFD_SUFFIX))
135             && !strcasecmp(*parser + strlen(*parser)
136                          - strlen(UNICODE_XLFD_SUFFIX), UNICODE_XLFD_SUFFIX))
137            dp->drv.p->max_char = 0xffff;
138        else if((strlen(*parser) > sizeof(LATIN_1_XLFD_SUFFIX))
139                 && !strcasecmp(*parser + strlen(*parser)
140                        - strlen(LATIN_1_XLFD_SUFFIX), LATIN_1_XLFD_SUFFIX))
141            dp->drv.p->max_char = 0xff;
142        else
143            dp->drv.p->max_char = 0x7f;
144
145        font_max_char =
146            (dp->drv.p->font_struct->max_byte1 << 8)
147             | dp->drv.p->font_struct->max_char_or_byte2;
148        if(font_max_char && (font_max_char < dp->drv.p->max_char))
149            dp->drv.p->max_char = font_max_char;
150
151        break;
152    }
153
154    /* Reset the default X11 error handler */
155    XSetErrorHandler(old_error_handler);
156   
157    dp->drv.p->font_width = 0;
158    if(dp->drv.p->font_struct->per_char
159        && !dp->drv.p->font_struct->min_byte1
160        && dp->drv.p->font_struct->min_char_or_byte2 <= 0x21
161        && dp->drv.p->font_struct->max_char_or_byte2 >= 0x7e)
162    {
163        for(i = 0x21; i < 0x7f; i++)
164        {
165            int cw = dp->drv.p->font_struct->per_char[i
166                           - dp->drv.p->font_struct->min_char_or_byte2].width;
167            if(cw > dp->drv.p->font_width)
168                dp->drv.p->font_width = cw;
169        }
170    }
171
172    if(!dp->drv.p->font_width)
173        dp->drv.p->font_width = dp->drv.p->font_struct->max_bounds.width;
174
175    dp->drv.p->font_height = dp->drv.p->font_struct->max_bounds.ascent
176                         + dp->drv.p->font_struct->max_bounds.descent;
177    dp->drv.p->font_offset = dp->drv.p->font_struct->max_bounds.descent;
178
179    colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
180    for(i = 0x000; i < 0x1000; i++)
181    {
182        XColor color;
183        color.red = ((i & 0xf00) >> 8) * 0x1111;
184        color.green = ((i & 0x0f0) >> 4) * 0x1111;
185        color.blue = (i & 0x00f) * 0x1111;
186        XAllocColor(dp->drv.p->dpy, colormap, &color);
187        dp->drv.p->colors[i] = color.pixel;
188    }
189
190    x11_winattr.backing_store = Always;
191    x11_winattr.background_pixel = dp->drv.p->colors[0x000];
192    x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
193
194    dp->drv.p->window =
195        XCreateWindow(dp->drv.p->dpy, DefaultRootWindow(dp->drv.p->dpy), 0, 0,
196                      width * dp->drv.p->font_width,
197                      height * dp->drv.p->font_height,
198                      0, 0, InputOutput, 0,
199                      CWBackingStore | CWBackPixel | CWEventMask,
200                      &x11_winattr);
201
202    dp->drv.p->wm_protocols =
203        XInternAtom(dp->drv.p->dpy, "WM_PROTOCOLS", True);
204    dp->drv.p->wm_delete_window =
205        XInternAtom(dp->drv.p->dpy, "WM_DELETE_WINDOW", True);
206
207    if(dp->drv.p->wm_protocols != None && dp->drv.p->wm_delete_window != None)
208        XSetWMProtocols(dp->drv.p->dpy, dp->drv.p->window,
209                        &dp->drv.p->wm_delete_window, 1);
210
211    XStoreName(dp->drv.p->dpy, dp->drv.p->window, "caca for X");
212
213    XSelectInput(dp->drv.p->dpy, dp->drv.p->window, StructureNotifyMask);
214    XMapWindow(dp->drv.p->dpy, dp->drv.p->window);
215
216    dp->drv.p->gc = XCreateGC(dp->drv.p->dpy, dp->drv.p->window, 0, NULL);
217    XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->colors[0x888]);
218    XSetFont(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->font);
219
220    for(;;)
221    {
222        XEvent xevent;
223        XNextEvent(dp->drv.p->dpy, &xevent);
224        if(xevent.type == MapNotify)
225            break;
226    }
227
228#if defined(HAVE_X11_XKBLIB_H)
229    /* Disable autorepeat */
230    XkbSetDetectableAutoRepeat(dp->drv.p->dpy, True, &dp->drv.p->autorepeat);
231    if(!dp->drv.p->autorepeat)
232        XAutoRepeatOff(dp->drv.p->dpy);
233#endif
234
235    dp->drv.p->event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
236          | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
237          | ExposureMask;
238
239    XSelectInput(dp->drv.p->dpy, dp->drv.p->window, dp->drv.p->event_mask);
240
241    XSync(dp->drv.p->dpy, False);
242
243    dp->drv.p->pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
244                                      width * dp->drv.p->font_width,
245                                      height * dp->drv.p->font_height,
246                                      DefaultDepth(dp->drv.p->dpy,
247                                      DefaultScreen(dp->drv.p->dpy)));
248    dp->drv.p->pointer = None;
249
250    dp->drv.p->cursor_flags = 0;
251
252    return 0;
253}
254
255static int x11_end_graphics(caca_display_t *dp)
256{
257    XSync(dp->drv.p->dpy, False);
258#if defined(HAVE_X11_XKBLIB_H)
259    if(!dp->drv.p->autorepeat)
260        XAutoRepeatOn(dp->drv.p->dpy);
261#endif
262    XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
263    XFreeFont(dp->drv.p->dpy, dp->drv.p->font_struct);
264    XFreeGC(dp->drv.p->dpy, dp->drv.p->gc);
265    XUnmapWindow(dp->drv.p->dpy, dp->drv.p->window);
266    XDestroyWindow(dp->drv.p->dpy, dp->drv.p->window);
267    XCloseDisplay(dp->drv.p->dpy);
268
269    free(dp->drv.p);
270
271    return 0;
272}
273
274static int x11_set_display_title(caca_display_t *dp, char const *title)
275{
276    XStoreName(dp->drv.p->dpy, dp->drv.p->window, title);
277    return 0;
278}
279
280static int x11_get_display_width(caca_display_t const *dp)
281{
282    return caca_get_canvas_width(dp->cv) * dp->drv.p->font_width;
283}
284
285static int x11_get_display_height(caca_display_t const *dp)
286{
287    return caca_get_canvas_height(dp->cv) * dp->drv.p->font_height;
288}
289
290static void x11_display(caca_display_t *dp)
291{
292    uint32_t const *cvchars = (uint32_t const *)caca_get_canvas_chars(dp->cv);
293    uint32_t const *cvattrs = (uint32_t const *)caca_get_canvas_attrs(dp->cv);
294    int width = caca_get_canvas_width(dp->cv);
295    int height = caca_get_canvas_height(dp->cv);
296    int x, y, len;
297
298    /* First draw the background colours. Splitting the process in two
299     * loops like this is actually slightly faster. */
300    for(y = 0; y < height; y++)
301    {
302        for(x = 0; x < width; x += len)
303        {
304            uint32_t const *attrs = cvattrs + x + y * width;
305            uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
306
307            len = 1;
308            while(x + len < width
309                   && caca_attr_to_rgb12_bg(attrs[len]) == bg)
310                len++;
311
312            XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
313                           dp->drv.p->colors[bg]);
314            XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
315                           x * dp->drv.p->font_width,
316                           y * dp->drv.p->font_height,
317                           len * dp->drv.p->font_width,
318                           dp->drv.p->font_height);
319        }
320    }
321
322    /* Then print the foreground characters */
323    for(y = 0; y < height; y++)
324    {
325        int yoff = (y + 1) * dp->drv.p->font_height
326                                    - dp->drv.p->font_offset;
327        uint32_t const *chars = cvchars + y * width;
328        uint32_t const *attrs = cvattrs + y * width;
329
330        for(x = 0; x < width; x++, chars++, attrs++)
331        {
332            XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
333                           dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]);
334
335            x11_put_glyph(dp, x * dp->drv.p->font_width,
336                          y * dp->drv.p->font_height, yoff,
337                          dp->drv.p->font_width, dp->drv.p->font_height,
338                          *attrs, *chars);
339        }
340    }
341
342    /* Print the cursor if necessary */
343    if(dp->drv.p->cursor_flags)
344    {
345        XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
346                       dp->drv.p->colors[0xfff]);
347        x = caca_get_cursor_x(dp->cv);
348        y = caca_get_cursor_y(dp->cv);
349        XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
350                       x * dp->drv.p->font_width, y * dp->drv.p->font_height,
351                       dp->drv.p->font_width, dp->drv.p->font_height);
352    }
353
354    XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->window,
355              dp->drv.p->gc, 0, 0,
356              width * dp->drv.p->font_width,
357              height * dp->drv.p->font_height,
358              0, 0);
359    XFlush(dp->drv.p->dpy);
360}
361
362static void x11_handle_resize(caca_display_t *dp)
363{
364    Pixmap new_pixmap;
365
366    new_pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
367                               dp->resize.w * dp->drv.p->font_width,
368                               dp->resize.h * dp->drv.p->font_height,
369                               DefaultDepth(dp->drv.p->dpy,
370                                            DefaultScreen(dp->drv.p->dpy)));
371    XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, new_pixmap,
372              dp->drv.p->gc, 0, 0,
373              dp->resize.w * dp->drv.p->font_width,
374              dp->resize.h * dp->drv.p->font_height, 0, 0);
375    XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
376    dp->drv.p->pixmap = new_pixmap;
377}
378
379static int x11_get_event(caca_display_t *dp, caca_privevent_t *ev)
380{
381    int width = caca_get_canvas_width(dp->cv);
382    int height = caca_get_canvas_height(dp->cv);
383    XEvent xevent;
384    char key;
385
386    while(XCheckWindowEvent(dp->drv.p->dpy, dp->drv.p->window,
387                            dp->drv.p->event_mask, &xevent) == True)
388    {
389        KeySym keysym;
390
391        /* Expose event */
392        if(xevent.type == Expose)
393        {
394            XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap,
395                      dp->drv.p->window, dp->drv.p->gc, 0, 0,
396                      width * dp->drv.p->font_width,
397                      height * dp->drv.p->font_height, 0, 0);
398            continue;
399        }
400
401        /* Resize event */
402        if(xevent.type == ConfigureNotify)
403        {
404            int w, h;
405
406            w = (xevent.xconfigure.width + dp->drv.p->font_width / 3)
407                  / dp->drv.p->font_width;
408            h = (xevent.xconfigure.height + dp->drv.p->font_height / 3)
409                  / dp->drv.p->font_height;
410
411            if(!w || !h || (w == width && h == height))
412                continue;
413
414            dp->resize.w = w;
415            dp->resize.h = h;
416            dp->resize.resized = 1;
417
418            continue;
419        }
420
421        /* Check for mouse motion events */
422        if(xevent.type == MotionNotify)
423        {
424            int newx = xevent.xmotion.x / dp->drv.p->font_width;
425            int newy = xevent.xmotion.y / dp->drv.p->font_height;
426
427            if(newx >= width)
428                newx = width - 1;
429            if(newy >= height)
430                newy = height - 1;
431
432            if(dp->mouse.x == newx && dp->mouse.y == newy)
433                continue;
434
435            dp->mouse.x = newx;
436            dp->mouse.y = newy;
437
438            ev->type = CACA_EVENT_MOUSE_MOTION;
439            ev->data.mouse.x = dp->mouse.x;
440            ev->data.mouse.y = dp->mouse.y;
441            return 1;
442        }
443
444        /* Check for mouse press and release events */
445        if(xevent.type == ButtonPress)
446        {
447            ev->type = CACA_EVENT_MOUSE_PRESS;
448            ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
449            return 1;
450        }
451
452        if(xevent.type == ButtonRelease)
453        {
454            ev->type = CACA_EVENT_MOUSE_RELEASE;
455            ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
456            return 1;
457        }
458
459        /* Check for key press and release events */
460        if(xevent.type == KeyPress)
461            ev->type = CACA_EVENT_KEY_PRESS;
462        else if(xevent.type == KeyRelease)
463            ev->type = CACA_EVENT_KEY_RELEASE;
464        else
465            continue;
466
467        if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
468        {
469            ev->data.key.ch = key;
470            ev->data.key.utf32 = key;
471            ev->data.key.utf8[0] = key;
472            ev->data.key.utf8[1] = '\0';
473            return 1;
474        }
475
476        keysym = XKeycodeToKeysym(dp->drv.p->dpy, xevent.xkey.keycode, 0);
477        switch(keysym)
478        {
479            case XK_F1:    ev->data.key.ch = CACA_KEY_F1;    break;
480            case XK_F2:    ev->data.key.ch = CACA_KEY_F2;    break;
481            case XK_F3:    ev->data.key.ch = CACA_KEY_F3;    break;
482            case XK_F4:    ev->data.key.ch = CACA_KEY_F4;    break;
483            case XK_F5:    ev->data.key.ch = CACA_KEY_F5;    break;
484            case XK_F6:    ev->data.key.ch = CACA_KEY_F6;    break;
485            case XK_F7:    ev->data.key.ch = CACA_KEY_F7;    break;
486            case XK_F8:    ev->data.key.ch = CACA_KEY_F8;    break;
487            case XK_F9:    ev->data.key.ch = CACA_KEY_F9;    break;
488            case XK_F10:   ev->data.key.ch = CACA_KEY_F10;   break;
489            case XK_F11:   ev->data.key.ch = CACA_KEY_F11;   break;
490            case XK_F12:   ev->data.key.ch = CACA_KEY_F12;   break;
491            case XK_F13:   ev->data.key.ch = CACA_KEY_F13;   break;
492            case XK_F14:   ev->data.key.ch = CACA_KEY_F14;   break;
493            case XK_F15:   ev->data.key.ch = CACA_KEY_F15;   break;
494            case XK_Left:  ev->data.key.ch = CACA_KEY_LEFT;  break;
495            case XK_Right: ev->data.key.ch = CACA_KEY_RIGHT; break;
496            case XK_Up:    ev->data.key.ch = CACA_KEY_UP;    break;
497            case XK_Down:  ev->data.key.ch = CACA_KEY_DOWN;  break;
498            case XK_KP_Page_Up:
499            case XK_Page_Up:      ev->data.key.ch = CACA_KEY_PAGEUP;   break;
500            case XK_KP_Page_Down:
501            case XK_Page_Down:    ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
502            case XK_KP_Home:
503            case XK_Home:         ev->data.key.ch = CACA_KEY_HOME;     break;
504            case XK_KP_End:
505            case XK_End:          ev->data.key.ch = CACA_KEY_END;      break;
506
507            default: ev->type = CACA_EVENT_NONE; return 0;
508        }
509
510        ev->data.key.utf32 = 0;
511        ev->data.key.utf8[0] = '\0';
512        return 1;
513    }
514
515    while(XCheckTypedEvent(dp->drv.p->dpy, ClientMessage, &xevent))
516    {
517        if(xevent.xclient.message_type != dp->drv.p->wm_protocols)
518            continue;
519
520        if((Atom)xevent.xclient.data.l[0] == dp->drv.p->wm_delete_window)
521        {
522            ev->type = CACA_EVENT_QUIT;
523            return 1;
524        }
525    }
526
527    ev->type = CACA_EVENT_NONE;
528    return 0;
529}
530
531static void x11_set_mouse(caca_display_t *dp, int flags)
532{
533    Cursor no_ptr;
534    Pixmap bm_no;
535    XColor black, dummy;
536    Colormap colormap;
537    static char const empty[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
538
539    if(flags)
540    {
541        XDefineCursor(dp->drv.p->dpy,dp->drv.p->window, 0);
542        return;
543    }
544
545    colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
546    if(!XAllocNamedColor(dp->drv.p->dpy, colormap, "black", &black, &dummy))
547    {
548        return;
549    }
550    bm_no = XCreateBitmapFromData(dp->drv.p->dpy, dp->drv.p->window,
551                                  empty, 8, 8);
552    no_ptr = XCreatePixmapCursor(dp->drv.p->dpy, bm_no, bm_no,
553                                 &black, &black, 0, 0);
554    XDefineCursor(dp->drv.p->dpy, dp->drv.p->window, no_ptr);
555    XFreeCursor(dp->drv.p->dpy, no_ptr);
556    if(bm_no != None)
557        XFreePixmap(dp->drv.p->dpy, bm_no);
558    XFreeColors(dp->drv.p->dpy, colormap, &black.pixel, 1, 0);
559
560    XSync(dp->drv.p->dpy, False);
561}
562
563static void x11_set_cursor(caca_display_t *dp, int flags)
564{
565    dp->drv.p->cursor_flags = flags;
566}
567
568/*
569 * XXX: following functions are local
570 */
571
572static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
573{
574    /* Ignore the error */
575    return 0;
576}
577
578static void x11_put_glyph(caca_display_t *dp, int x, int y, int yoff,
579                          int w, int h, uint32_t attr, uint32_t ch)
580{
581    static uint8_t const udlr[] =
582    {
583        /* 0x2500 - 0x250f: ─ . │ . . . . . . . . . ┌ . . . */
584        0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
585        0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
586        /* 0x2510 - 0x251f: ┐ . . . └ . . . ┘ . . . ├ . . . */
587        0x14, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
588        0x44, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
589        /* 0x2520 - 0x252f: . . . . ┤ . . . . . . . ┬ . . . */
590        0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
591        0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
592        /* 0x2530 - 0x253f: . . . . ┴ . . . . . . . ┼ . . . */
593        0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
594        0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
595        /* 0x2540 - 0x254f: . . . . . . . . . . . . . . . . */
596        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
597        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598        /* 0x2550 - 0x255f: ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ */
599        0x0a, 0xa0, 0x12, 0x21, 0x22, 0x18, 0x24, 0x28,
600        0x42, 0x81, 0x82, 0x48, 0x84, 0x88, 0x52, 0xa1,
601        /* 0x2560 - 0x256c: ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ */
602        0xa2, 0x58, 0xa4, 0xa8, 0x1a, 0x25, 0x2a, 0x4a,
603        0x85, 0x8a, 0x5a, 0xa5, 0xaa,
604    };
605
606    Display *dpy = dp->drv.p->dpy;
607    Pixmap px = dp->drv.p->pixmap;
608    GC gc = dp->drv.p->gc;
609    int fw;
610    XChar2b ch16;
611
612    /* Underline */
613    if(attr & CACA_UNDERLINE)
614        XFillRectangle(dpy, px, gc, x, y + h - 1, w, 1);
615
616    /* Skip spaces and magic stuff */
617    if(ch <= 0x00000020)
618        return;
619
620    if(ch == CACA_MAGIC_FULLWIDTH)
621        return;
622
623    fw = w;
624    if(caca_utf32_is_fullwidth(ch))
625        fw *= 2;
626
627    /* We want to be able to print a few special Unicode characters
628     * such as the CP437 gradients and half blocks. For unknown
629     * characters, print what caca_utf32_to_ascii() returns. */
630
631    if(ch >= 0x2500 && ch <= 0x256c && udlr[ch - 0x2500])
632    {
633        uint16_t D = udlr[ch - 0x2500];
634
635        if(D & 0x04)
636            XFillRectangle(dpy, px, gc, x, y + h / 2, fw / 2 + 1, 1);
637
638        if(D & 0x01)
639            XFillRectangle(dpy, px, gc,
640                           x + fw / 2, y + h / 2, (fw + 1) / 2, 1);
641
642        if(D & 0x40)
643            XFillRectangle(dpy, px, gc, x + fw / 2, y, 1, h / 2 + 1);
644
645        if(D & 0x10)
646            XFillRectangle(dpy, px, gc, x + fw / 2, y + h / 2, 1, (h + 1) / 2);
647
648#define STEPIF(a,b) (D&(a)?-1:(D&(b))?1:0)
649
650        if(D & 0x08)
651        {
652            XFillRectangle(dpy, px, gc, x, y - 1 + h / 2,
653                           fw / 2 + 1 + STEPIF(0xc0,0x20), 1);
654            XFillRectangle(dpy, px, gc, x, y + 1 + h / 2,
655                           fw / 2 + 1 + STEPIF(0x30,0x80), 1);
656        }
657
658        if(D & 0x02)
659        {
660            XFillRectangle(dpy, px, gc, x - STEPIF(0xc0,0x20) + fw / 2,
661                           y - 1 + h / 2, (fw + 1) / 2 + STEPIF(0xc0,0x20), 1);
662            XFillRectangle(dpy, px, gc, x - STEPIF(0x30,0x80) + fw / 2,
663                           y + 1 + h / 2, (fw + 1) / 2 + STEPIF(0x30,0x80), 1);
664        }
665
666        if(D & 0x80)
667        {
668            XFillRectangle(dpy, px, gc, x - 1 + fw / 2, y,
669                           1, h / 2 + 1 + STEPIF(0x0c,0x02));
670            XFillRectangle(dpy, px, gc, x + 1 + fw / 2, y,
671                           1, h / 2 + 1 + STEPIF(0x03,0x08));
672        }
673
674        if(D & 0x20)
675        {
676            XFillRectangle(dpy, px, gc, x - 1 + fw / 2,
677                           y - STEPIF(0x0c,0x02) + h / 2,
678                           1, (h + 1) / 2 + STEPIF(0x0c,0x02));
679            XFillRectangle(dpy, px, gc, x + 1 + fw / 2,
680                           y - STEPIF(0x03,0x08) + h / 2,
681                           1, (h + 1) / 2 + STEPIF(0x03,0x08));
682        }
683
684        return;
685    }
686
687    switch(ch)
688    {
689        case 0x000000b7: /* · */
690        case 0x00002219: /* ∙ */
691        case 0x000030fb: /* ・ */
692            XFillRectangle(dpy, px, gc, x + fw / 2 - 1, y + h / 2 - 1, 2, 2);
693            return;
694
695        case 0x00002261: /* ≡ */
696            XFillRectangle(dpy, px, gc, x + 1, y - 2 + h / 2, fw - 1, 1);
697            XFillRectangle(dpy, px, gc, x + 1, y + h / 2, fw - 1, 1);
698            XFillRectangle(dpy, px, gc, x + 1, y + 2 + h / 2, fw - 1, 1);
699            return;
700
701        case 0x00002580: /* ▀ */
702            XFillRectangle(dpy, px, gc, x, y, fw, h / 2);
703            return;
704
705        case 0x00002584: /* ▄ */
706            XFillRectangle(dpy, px, gc, x, y + h - h / 2, fw, h / 2);
707            return;
708
709        case 0x00002588: /* █ */
710        case 0x000025ae: /* ▮ */
711            XFillRectangle(dpy, px, gc, x, y, fw, h);
712            return;
713
714        case 0x0000258c: /* ▌ */
715            XFillRectangle(dpy, px, gc, x, y, fw / 2, h);
716            return;
717
718        case 0x00002590: /* ▐ */
719            XFillRectangle(dpy, px, gc, x + fw - fw / 2, y, fw / 2, h);
720            return;
721
722        case 0x000025a0: /* ■ */
723        case 0x000025ac: /* ▬ */
724            XFillRectangle(dpy, px, gc, x, y + h / 4, fw, h / 2);
725            return;
726
727        case 0x00002593: /* ▓ */
728        case 0x00002592: /* ▒ */
729        case 0x00002591: /* ░ */
730        {
731            /* FIXME: this sucks utterly */
732            int i, j, k = ch - 0x00002591;
733            for(j = h; j--; )
734                for(i = fw; i--; )
735            {
736                if(((i + 2 * (j & 1)) & 3) > k)
737                    continue;
738
739                XDrawPoint(dpy, px, gc, x + i, y + j);
740            }
741            return;
742        }
743
744        case 0x000025cb: /* ○ */
745        case 0x00002022: /* • */
746        case 0x000025cf: /* ● */
747        {
748            int d, xo, yo;
749
750            d = fw >> (~ch & 0x1); /* XXX: hack */
751            if(h < fw)
752                d = h;
753            if(d < 1)
754                d = 1;
755            xo = (fw - d) / 2;
756            yo = (h - d) / 2;
757            if(ch == 0x000025cb)
758                XDrawArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
759            else
760                XFillArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
761            return;
762        }
763    }
764
765    if(ch >= 0x00000020 && ch <= dp->drv.p->max_char)
766    {
767        /* ascii, latin-1 or unicode font (might draw a blank square) */
768        ch16.byte1 = (ch) >> 8;
769        ch16.byte2 = (ch) & 0xff;
770    }
771    else
772    {
773        ch16.byte1 = 0;
774        ch16.byte2 = caca_utf32_to_ascii(ch);
775    }
776
777    XDrawString16(dpy, px, gc, x + (ch16.byte1 ? 0 : (fw - w) / 2), yoff, &ch16, 1);
778}
779
780/*
781 * Driver initialisation
782 */
783
784int x11_install(caca_display_t *dp)
785{
786#if defined(HAVE_GETENV)
787    if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
788        return -1;
789#endif
790
791    dp->drv.id = CACA_DRIVER_X11;
792    dp->drv.driver = "x11";
793
794    dp->drv.init_graphics = x11_init_graphics;
795    dp->drv.end_graphics = x11_end_graphics;
796    dp->drv.set_display_title = x11_set_display_title;
797    dp->drv.get_display_width = x11_get_display_width;
798    dp->drv.get_display_height = x11_get_display_height;
799    dp->drv.display = x11_display;
800    dp->drv.handle_resize = x11_handle_resize;
801    dp->drv.get_event = x11_get_event;
802    dp->drv.set_mouse = x11_set_mouse;
803    dp->drv.set_cursor = x11_set_cursor;
804
805    return 0;
806}
807
808#endif /* USE_X11 */
809
Note: See TracBrowser for help on using the repository browser.