source: libcaca/trunk/caca/driver/x11.c @ 3510

Last change on this file since 3510 was 3510, checked in by Pascal Terjan, 11 years ago

Don't redraw background for more than the dirty rectangle in x11 driver, we will not redraw foreground there

  • Property svn:keywords set to Id
File size: 26.0 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                2007 Ben Wiley Sittler <bsittler@gmail.com>
5 *                All Rights Reserved
6 *
7 *  $Id: x11.c 3510 2009-05-22 13:06:07Z pterjan $
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, i, len;
297
298    for(i = 0; i < caca_get_dirty_rect_count(dp->cv); i++)
299    {
300        int dx, dy, dw, dh;
301
302        caca_get_dirty_rect(dp->cv, i, &dx, &dy, &dw, &dh);
303
304        /* First draw the background colours. Splitting the process in two
305         * loops like this is actually slightly faster. */
306        for(y = dy; y < dy + dh; y++)
307        {
308            for(x = dx; x < dx + dw; x += len)
309            {
310                uint32_t const *attrs = cvattrs + x + y * width;
311                uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
312
313                len = 1;
314                while(x + len < dx + dw
315                       && caca_attr_to_rgb12_bg(attrs[len]) == bg)
316                    len++;
317
318                XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
319                               dp->drv.p->colors[bg]);
320                XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
321                               dp->drv.p->gc,
322                               x * dp->drv.p->font_width,
323                               y * dp->drv.p->font_height,
324                               len * dp->drv.p->font_width,
325                               dp->drv.p->font_height);
326            }
327        }
328
329        /* Then print the foreground characters */
330        for(y = dy; y < dy + dh; y++)
331        {
332            int yoff = (y + 1) * dp->drv.p->font_height
333                                        - dp->drv.p->font_offset;
334            uint32_t const *chars = cvchars + dx + y * width;
335            uint32_t const *attrs = cvattrs + dx + y * width;
336
337            for(x = dx; x < dx + dw; x++, chars++, attrs++)
338            {
339                XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
340                           dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]);
341
342                x11_put_glyph(dp, x * dp->drv.p->font_width,
343                              y * dp->drv.p->font_height, yoff,
344                              dp->drv.p->font_width, dp->drv.p->font_height,
345                              *attrs, *chars);
346            }
347        }
348    }
349
350    /* Print the cursor if necessary. FIXME: handle dirty rectangles! */
351    if(dp->drv.p->cursor_flags)
352    {
353        XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
354                       dp->drv.p->colors[0xfff]);
355        x = caca_get_cursor_x(dp->cv);
356        y = caca_get_cursor_y(dp->cv);
357        XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
358                       x * dp->drv.p->font_width, y * dp->drv.p->font_height,
359                       dp->drv.p->font_width, dp->drv.p->font_height);
360    }
361
362    XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->window,
363              dp->drv.p->gc, 0, 0,
364              width * dp->drv.p->font_width,
365              height * dp->drv.p->font_height,
366              0, 0);
367    XFlush(dp->drv.p->dpy);
368}
369
370static void x11_handle_resize(caca_display_t *dp)
371{
372    Pixmap new_pixmap;
373
374    new_pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
375                               dp->resize.w * dp->drv.p->font_width,
376                               dp->resize.h * dp->drv.p->font_height,
377                               DefaultDepth(dp->drv.p->dpy,
378                                            DefaultScreen(dp->drv.p->dpy)));
379    XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, new_pixmap,
380              dp->drv.p->gc, 0, 0,
381              dp->resize.w * dp->drv.p->font_width,
382              dp->resize.h * dp->drv.p->font_height, 0, 0);
383    XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
384    dp->drv.p->pixmap = new_pixmap;
385}
386
387static int x11_get_event(caca_display_t *dp, caca_privevent_t *ev)
388{
389    int width = caca_get_canvas_width(dp->cv);
390    int height = caca_get_canvas_height(dp->cv);
391    XEvent xevent;
392    char key;
393
394    while(XCheckWindowEvent(dp->drv.p->dpy, dp->drv.p->window,
395                            dp->drv.p->event_mask, &xevent) == True)
396    {
397        KeySym keysym;
398
399        /* Expose event */
400        if(xevent.type == Expose)
401        {
402            XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap,
403                      dp->drv.p->window, dp->drv.p->gc, 0, 0,
404                      width * dp->drv.p->font_width,
405                      height * dp->drv.p->font_height, 0, 0);
406            continue;
407        }
408
409        /* Resize event */
410        if(xevent.type == ConfigureNotify)
411        {
412            int w, h;
413
414            w = (xevent.xconfigure.width + dp->drv.p->font_width / 3)
415                  / dp->drv.p->font_width;
416            h = (xevent.xconfigure.height + dp->drv.p->font_height / 3)
417                  / dp->drv.p->font_height;
418
419            if(!w || !h || (w == width && h == height))
420                continue;
421
422            dp->resize.w = w;
423            dp->resize.h = h;
424            dp->resize.resized = 1;
425
426            continue;
427        }
428
429        /* Check for mouse motion events */
430        if(xevent.type == MotionNotify)
431        {
432            int newx = xevent.xmotion.x / dp->drv.p->font_width;
433            int newy = xevent.xmotion.y / dp->drv.p->font_height;
434
435            if(newx >= width)
436                newx = width - 1;
437            if(newy >= height)
438                newy = height - 1;
439
440            if(dp->mouse.x == newx && dp->mouse.y == newy)
441                continue;
442
443            dp->mouse.x = newx;
444            dp->mouse.y = newy;
445
446            ev->type = CACA_EVENT_MOUSE_MOTION;
447            ev->data.mouse.x = dp->mouse.x;
448            ev->data.mouse.y = dp->mouse.y;
449            return 1;
450        }
451
452        /* Check for mouse press and release events */
453        if(xevent.type == ButtonPress)
454        {
455            ev->type = CACA_EVENT_MOUSE_PRESS;
456            ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
457            return 1;
458        }
459
460        if(xevent.type == ButtonRelease)
461        {
462            ev->type = CACA_EVENT_MOUSE_RELEASE;
463            ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
464            return 1;
465        }
466
467        /* Check for key press and release events */
468        if(xevent.type == KeyPress)
469            ev->type = CACA_EVENT_KEY_PRESS;
470        else if(xevent.type == KeyRelease)
471            ev->type = CACA_EVENT_KEY_RELEASE;
472        else
473            continue;
474
475        if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
476        {
477            ev->data.key.ch = key;
478            ev->data.key.utf32 = key;
479            ev->data.key.utf8[0] = key;
480            ev->data.key.utf8[1] = '\0';
481            return 1;
482        }
483
484        keysym = XKeycodeToKeysym(dp->drv.p->dpy, xevent.xkey.keycode, 0);
485        switch(keysym)
486        {
487            case XK_F1:    ev->data.key.ch = CACA_KEY_F1;    break;
488            case XK_F2:    ev->data.key.ch = CACA_KEY_F2;    break;
489            case XK_F3:    ev->data.key.ch = CACA_KEY_F3;    break;
490            case XK_F4:    ev->data.key.ch = CACA_KEY_F4;    break;
491            case XK_F5:    ev->data.key.ch = CACA_KEY_F5;    break;
492            case XK_F6:    ev->data.key.ch = CACA_KEY_F6;    break;
493            case XK_F7:    ev->data.key.ch = CACA_KEY_F7;    break;
494            case XK_F8:    ev->data.key.ch = CACA_KEY_F8;    break;
495            case XK_F9:    ev->data.key.ch = CACA_KEY_F9;    break;
496            case XK_F10:   ev->data.key.ch = CACA_KEY_F10;   break;
497            case XK_F11:   ev->data.key.ch = CACA_KEY_F11;   break;
498            case XK_F12:   ev->data.key.ch = CACA_KEY_F12;   break;
499            case XK_F13:   ev->data.key.ch = CACA_KEY_F13;   break;
500            case XK_F14:   ev->data.key.ch = CACA_KEY_F14;   break;
501            case XK_F15:   ev->data.key.ch = CACA_KEY_F15;   break;
502            case XK_Left:  ev->data.key.ch = CACA_KEY_LEFT;  break;
503            case XK_Right: ev->data.key.ch = CACA_KEY_RIGHT; break;
504            case XK_Up:    ev->data.key.ch = CACA_KEY_UP;    break;
505            case XK_Down:  ev->data.key.ch = CACA_KEY_DOWN;  break;
506            case XK_KP_Page_Up:
507            case XK_Page_Up:      ev->data.key.ch = CACA_KEY_PAGEUP;   break;
508            case XK_KP_Page_Down:
509            case XK_Page_Down:    ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
510            case XK_KP_Home:
511            case XK_Home:         ev->data.key.ch = CACA_KEY_HOME;     break;
512            case XK_KP_End:
513            case XK_End:          ev->data.key.ch = CACA_KEY_END;      break;
514
515            default: ev->type = CACA_EVENT_NONE; return 0;
516        }
517
518        ev->data.key.utf32 = 0;
519        ev->data.key.utf8[0] = '\0';
520        return 1;
521    }
522
523    while(XCheckTypedEvent(dp->drv.p->dpy, ClientMessage, &xevent))
524    {
525        if(xevent.xclient.message_type != dp->drv.p->wm_protocols)
526            continue;
527
528        if((Atom)xevent.xclient.data.l[0] == dp->drv.p->wm_delete_window)
529        {
530            ev->type = CACA_EVENT_QUIT;
531            return 1;
532        }
533    }
534
535    ev->type = CACA_EVENT_NONE;
536    return 0;
537}
538
539static void x11_set_mouse(caca_display_t *dp, int flags)
540{
541    Cursor no_ptr;
542    Pixmap bm_no;
543    XColor black, dummy;
544    Colormap colormap;
545    static char const empty[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
546
547    if(flags)
548    {
549        XDefineCursor(dp->drv.p->dpy,dp->drv.p->window, 0);
550        return;
551    }
552
553    colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
554    if(!XAllocNamedColor(dp->drv.p->dpy, colormap, "black", &black, &dummy))
555    {
556        return;
557    }
558    bm_no = XCreateBitmapFromData(dp->drv.p->dpy, dp->drv.p->window,
559                                  empty, 8, 8);
560    no_ptr = XCreatePixmapCursor(dp->drv.p->dpy, bm_no, bm_no,
561                                 &black, &black, 0, 0);
562    XDefineCursor(dp->drv.p->dpy, dp->drv.p->window, no_ptr);
563    XFreeCursor(dp->drv.p->dpy, no_ptr);
564    if(bm_no != None)
565        XFreePixmap(dp->drv.p->dpy, bm_no);
566    XFreeColors(dp->drv.p->dpy, colormap, &black.pixel, 1, 0);
567
568    XSync(dp->drv.p->dpy, False);
569}
570
571static void x11_set_cursor(caca_display_t *dp, int flags)
572{
573    dp->drv.p->cursor_flags = flags;
574}
575
576/*
577 * XXX: following functions are local
578 */
579
580static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
581{
582    /* Ignore the error */
583    return 0;
584}
585
586static void x11_put_glyph(caca_display_t *dp, int x, int y, int yoff,
587                          int w, int h, uint32_t attr, uint32_t ch)
588{
589    static uint8_t const udlr[] =
590    {
591        /* 0x2500 - 0x250f: ─ . │ . . . . . . . . . ┌ . . . */
592        0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
593        0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
594        /* 0x2510 - 0x251f: ┐ . . . └ . . . ┘ . . . ├ . . . */
595        0x14, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
596        0x44, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
597        /* 0x2520 - 0x252f: . . . . ┤ . . . . . . . ┬ . . . */
598        0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
599        0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
600        /* 0x2530 - 0x253f: . . . . ┴ . . . . . . . ┼ . . . */
601        0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
602        0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
603        /* 0x2540 - 0x254f: . . . . . . . . . . . . . . . . */
604        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
605        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
606        /* 0x2550 - 0x255f: ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ */
607        0x0a, 0xa0, 0x12, 0x21, 0x22, 0x18, 0x24, 0x28,
608        0x42, 0x81, 0x82, 0x48, 0x84, 0x88, 0x52, 0xa1,
609        /* 0x2560 - 0x256c: ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ */
610        0xa2, 0x58, 0xa4, 0xa8, 0x1a, 0x25, 0x2a, 0x4a,
611        0x85, 0x8a, 0x5a, 0xa5, 0xaa,
612    };
613
614    Display *dpy = dp->drv.p->dpy;
615    Pixmap px = dp->drv.p->pixmap;
616    GC gc = dp->drv.p->gc;
617    int fw;
618    XChar2b ch16;
619
620    /* Underline */
621    if(attr & CACA_UNDERLINE)
622        XFillRectangle(dpy, px, gc, x, y + h - 1, w, 1);
623
624    /* Skip spaces and magic stuff */
625    if(ch <= 0x00000020)
626        return;
627
628    if(ch == CACA_MAGIC_FULLWIDTH)
629        return;
630
631    fw = w;
632    if(caca_utf32_is_fullwidth(ch))
633        fw *= 2;
634
635    /* We want to be able to print a few special Unicode characters
636     * such as the CP437 gradients and half blocks. For unknown
637     * characters, print what caca_utf32_to_ascii() returns. */
638
639    if(ch >= 0x2500 && ch <= 0x256c && udlr[ch - 0x2500])
640    {
641        uint16_t D = udlr[ch - 0x2500];
642
643        if(D & 0x04)
644            XFillRectangle(dpy, px, gc, x, y + h / 2, fw / 2 + 1, 1);
645
646        if(D & 0x01)
647            XFillRectangle(dpy, px, gc,
648                           x + fw / 2, y + h / 2, (fw + 1) / 2, 1);
649
650        if(D & 0x40)
651            XFillRectangle(dpy, px, gc, x + fw / 2, y, 1, h / 2 + 1);
652
653        if(D & 0x10)
654            XFillRectangle(dpy, px, gc, x + fw / 2, y + h / 2, 1, (h + 1) / 2);
655
656#define STEPIF(a,b) (D&(a)?-1:(D&(b))?1:0)
657
658        if(D & 0x08)
659        {
660            XFillRectangle(dpy, px, gc, x, y - 1 + h / 2,
661                           fw / 2 + 1 + STEPIF(0xc0,0x20), 1);
662            XFillRectangle(dpy, px, gc, x, y + 1 + h / 2,
663                           fw / 2 + 1 + STEPIF(0x30,0x80), 1);
664        }
665
666        if(D & 0x02)
667        {
668            XFillRectangle(dpy, px, gc, x - STEPIF(0xc0,0x20) + fw / 2,
669                           y - 1 + h / 2, (fw + 1) / 2 + STEPIF(0xc0,0x20), 1);
670            XFillRectangle(dpy, px, gc, x - STEPIF(0x30,0x80) + fw / 2,
671                           y + 1 + h / 2, (fw + 1) / 2 + STEPIF(0x30,0x80), 1);
672        }
673
674        if(D & 0x80)
675        {
676            XFillRectangle(dpy, px, gc, x - 1 + fw / 2, y,
677                           1, h / 2 + 1 + STEPIF(0x0c,0x02));
678            XFillRectangle(dpy, px, gc, x + 1 + fw / 2, y,
679                           1, h / 2 + 1 + STEPIF(0x03,0x08));
680        }
681
682        if(D & 0x20)
683        {
684            XFillRectangle(dpy, px, gc, x - 1 + fw / 2,
685                           y - STEPIF(0x0c,0x02) + h / 2,
686                           1, (h + 1) / 2 + STEPIF(0x0c,0x02));
687            XFillRectangle(dpy, px, gc, x + 1 + fw / 2,
688                           y - STEPIF(0x03,0x08) + h / 2,
689                           1, (h + 1) / 2 + STEPIF(0x03,0x08));
690        }
691
692        return;
693    }
694
695    switch(ch)
696    {
697        case 0x000000b7: /* · */
698        case 0x00002219: /* ∙ */
699        case 0x000030fb: /* ・ */
700            XFillRectangle(dpy, px, gc, x + fw / 2 - 1, y + h / 2 - 1, 2, 2);
701            return;
702
703        case 0x00002261: /* ≡ */
704            XFillRectangle(dpy, px, gc, x + 1, y - 2 + h / 2, fw - 1, 1);
705            XFillRectangle(dpy, px, gc, x + 1, y + h / 2, fw - 1, 1);
706            XFillRectangle(dpy, px, gc, x + 1, y + 2 + h / 2, fw - 1, 1);
707            return;
708
709        case 0x00002580: /* ▀ */
710            XFillRectangle(dpy, px, gc, x, y, fw, h / 2);
711            return;
712
713        case 0x00002584: /* ▄ */
714            XFillRectangle(dpy, px, gc, x, y + h - h / 2, fw, h / 2);
715            return;
716
717        case 0x00002588: /* █ */
718        case 0x000025ae: /* ▮ */
719            XFillRectangle(dpy, px, gc, x, y, fw, h);
720            return;
721
722        case 0x0000258c: /* ▌ */
723            XFillRectangle(dpy, px, gc, x, y, fw / 2, h);
724            return;
725
726        case 0x00002590: /* ▐ */
727            XFillRectangle(dpy, px, gc, x + fw - fw / 2, y, fw / 2, h);
728            return;
729
730        case 0x000025a0: /* ■ */
731        case 0x000025ac: /* ▬ */
732            XFillRectangle(dpy, px, gc, x, y + h / 4, fw, h / 2);
733            return;
734
735        case 0x00002593: /* ▓ */
736        case 0x00002592: /* ▒ */
737        case 0x00002591: /* ░ */
738        {
739            /* FIXME: this sucks utterly */
740            int i, j, k = ch - 0x00002591;
741            for(j = h; j--; )
742                for(i = fw; i--; )
743            {
744                if(((i + 2 * (j & 1)) & 3) > k)
745                    continue;
746
747                XDrawPoint(dpy, px, gc, x + i, y + j);
748            }
749            return;
750        }
751
752        case 0x000025cb: /* ○ */
753        case 0x00002022: /* • */
754        case 0x000025cf: /* ● */
755        {
756            int d, xo, yo;
757
758            d = fw >> (~ch & 0x1); /* XXX: hack */
759            if(h < fw)
760                d = h;
761            if(d < 1)
762                d = 1;
763            xo = (fw - d) / 2;
764            yo = (h - d) / 2;
765            if(ch == 0x000025cb)
766                XDrawArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
767            else
768                XFillArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
769            return;
770        }
771    }
772
773    if(ch >= 0x00000020 && ch <= dp->drv.p->max_char)
774    {
775        /* ascii, latin-1 or unicode font (might draw a blank square) */
776        ch16.byte1 = (ch) >> 8;
777        ch16.byte2 = (ch) & 0xff;
778    }
779    else
780    {
781        ch16.byte1 = 0;
782        ch16.byte2 = caca_utf32_to_ascii(ch);
783    }
784
785    XDrawString16(dpy, px, gc, x + (ch16.byte1 ? 0 : (fw - w) / 2), yoff, &ch16, 1);
786}
787
788/*
789 * Driver initialisation
790 */
791
792int x11_install(caca_display_t *dp)
793{
794#if defined(HAVE_GETENV)
795    if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
796        return -1;
797#endif
798
799    dp->drv.id = CACA_DRIVER_X11;
800    dp->drv.driver = "x11";
801
802    dp->drv.init_graphics = x11_init_graphics;
803    dp->drv.end_graphics = x11_end_graphics;
804    dp->drv.set_display_title = x11_set_display_title;
805    dp->drv.get_display_width = x11_get_display_width;
806    dp->drv.get_display_height = x11_get_display_height;
807    dp->drv.display = x11_display;
808    dp->drv.handle_resize = x11_handle_resize;
809    dp->drv.get_event = x11_get_event;
810    dp->drv.set_mouse = x11_set_mouse;
811    dp->drv.set_cursor = x11_set_cursor;
812
813    return 0;
814}
815
816#endif /* USE_X11 */
817
Note: See TracBrowser for help on using the repository browser.