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

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

Call setlocale() in the X11 driver to activate the current locale. If it
is an UTF-8 locale, more glyphs are displayed.

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