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

Last change on this file since 4140 was 4140, checked in by Sam Hocevar, 10 years ago

Allow the X11 driver to display Unicode characters if they are in the
current font (addresses #21).

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