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

Last change on this file since 548 was 548, checked in by Sam Hocevar, 14 years ago
  • Split event.c into the appropriate driver_*.c files.
  • Property svn:keywords set to Id
File size: 13.6 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file driver_x11.c
13 *  \version \$Id: driver_x11.c 548 2006-03-08 09:28:41Z sam $
14 *  \author Sam Hocevar <sam@zoy.org>
15 *  \brief X11 driver
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 <string.h>
33#include <stdlib.h>
34#if defined(HAVE_UNISTD_H)
35#   include <unistd.h>
36#endif
37#include <stdarg.h>
38
39#include "caca.h"
40#include "caca_internals.h"
41#include "cucul.h"
42#include "cucul_internals.h"
43
44/*
45 * Local functions
46 */
47static int x11_error_handler(Display *, XErrorEvent *);
48
49static int x11_init_graphics(caca_t *kk)
50{
51    static int const x11_palette[] =
52    {
53        /* Standard curses colours */
54        0x0,    0x0,    0x0,
55        0x0,    0x0,    0x8000,
56        0x0,    0x8000, 0x0,
57        0x0,    0x8000, 0x8000,
58        0x8000, 0x0,    0x0,
59        0x8000, 0x0,    0x8000,
60        0x8000, 0x8000, 0x0,
61        0x8000, 0x8000, 0x8000,
62        /* Extra values for xterm-16color */
63        0x4000, 0x4000, 0x4000,
64        0x4000, 0x4000, 0xffff,
65        0x4000, 0xffff, 0x4000,
66        0x4000, 0xffff, 0xffff,
67        0xffff, 0x4000, 0x4000,
68        0xffff, 0x4000, 0xffff,
69        0xffff, 0xffff, 0x4000,
70        0xffff, 0xffff, 0xffff,
71    };
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    unsigned int width = 0, height = 0;
79    int i;
80
81    geometry = getenv("CACA_GEOMETRY");
82    if(geometry && *(geometry))
83        sscanf(geometry, "%ux%u", &width, &height);
84
85    if(width && height)
86        cucul_set_size(kk->qq, width, height);
87
88    kk->x11.dpy = XOpenDisplay(NULL);
89    if(kk->x11.dpy == NULL)
90        return -1;
91
92    fonts[0] = getenv("CACA_FONT");
93    if(fonts[0] && *fonts[0])
94        parser = fonts;
95    else
96        parser = fonts + 1;
97
98    /* Ignore font errors */
99    old_error_handler = XSetErrorHandler(x11_error_handler);
100
101    /* Parse our font list */
102    for( ; ; parser++)
103    {
104        if(!*parser)
105        {
106            XSetErrorHandler(old_error_handler);
107            XCloseDisplay(kk->x11.dpy);
108            return -1;
109        }
110
111        kk->x11.font = XLoadFont(kk->x11.dpy, *parser);
112        if(!kk->x11.font)
113            continue;
114
115        kk->x11.font_struct = XQueryFont(kk->x11.dpy, kk->x11.font);
116        if(!kk->x11.font_struct)
117        {
118            XUnloadFont(kk->x11.dpy, kk->x11.font);
119            continue;
120        }
121
122        break;
123    }
124
125    /* Reset the default X11 error handler */
126    XSetErrorHandler(old_error_handler);
127
128    kk->x11.font_width = kk->x11.font_struct->max_bounds.width;
129    kk->x11.font_height = kk->x11.font_struct->max_bounds.ascent
130                         + kk->x11.font_struct->max_bounds.descent;
131    kk->x11.font_offset = kk->x11.font_struct->max_bounds.descent;
132
133    colormap = DefaultColormap(kk->x11.dpy, DefaultScreen(kk->x11.dpy));
134    for(i = 0; i < 16; i++)
135    {
136        XColor color;
137        color.red = x11_palette[i * 3];
138        color.green = x11_palette[i * 3 + 1];
139        color.blue = x11_palette[i * 3 + 2];
140        XAllocColor(kk->x11.dpy, colormap, &color);
141        kk->x11.colors[i] = color.pixel;
142    }
143
144    x11_winattr.backing_store = Always;
145    x11_winattr.background_pixel = kk->x11.colors[0];
146    x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
147
148    kk->x11.window =
149        XCreateWindow(kk->x11.dpy, DefaultRootWindow(kk->x11.dpy), 0, 0,
150                      kk->qq->width * kk->x11.font_width,
151                      kk->qq->height * kk->x11.font_height,
152                      0, 0, InputOutput, 0,
153                      CWBackingStore | CWBackPixel | CWEventMask,
154                      &x11_winattr);
155
156    XStoreName(kk->x11.dpy, kk->x11.window, "caca for X");
157
158    XSelectInput(kk->x11.dpy, kk->x11.window, StructureNotifyMask);
159    XMapWindow(kk->x11.dpy, kk->x11.window);
160
161    kk->x11.gc = XCreateGC(kk->x11.dpy, kk->x11.window, 0, NULL);
162    XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[15]);
163    XSetFont(kk->x11.dpy, kk->x11.gc, kk->x11.font);
164
165    for(;;)
166    {
167        XEvent xevent;
168        XNextEvent(kk->x11.dpy, &xevent);
169        if (xevent.type == MapNotify)
170            break;
171    }
172
173#if defined(HAVE_X11_XKBLIB_H)
174    /* Disable autorepeat */
175    XkbSetDetectableAutoRepeat(kk->x11.dpy, True, &kk->x11.autorepeat);
176    if(!kk->x11.autorepeat)
177        XAutoRepeatOff(kk->x11.dpy);
178#endif
179
180    kk->x11.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
181          | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
182          | ExposureMask;
183
184    XSelectInput(kk->x11.dpy, kk->x11.window, kk->x11.event_mask);
185
186    XSync(kk->x11.dpy, False);
187
188    kk->x11.pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window,
189                                   kk->qq->width * kk->x11.font_width,
190                                   kk->qq->height * kk->x11.font_height,
191                                   DefaultDepth(kk->x11.dpy,
192                                            DefaultScreen(kk->x11.dpy)));
193
194    kk->x11.new_width = kk->x11.new_height = 0;
195
196    return 0;
197}
198
199static int x11_end_graphics(caca_t *kk)
200{
201    XSync(kk->x11.dpy, False);
202#if defined(HAVE_X11_XKBLIB_H)
203    if(!kk->x11.autorepeat)
204        XAutoRepeatOn(kk->x11.dpy);
205#endif
206    XFreePixmap(kk->x11.dpy, kk->x11.pixmap);
207    XFreeFont(kk->x11.dpy, kk->x11.font_struct);
208    XFreeGC(kk->x11.dpy, kk->x11.gc);
209    XUnmapWindow(kk->x11.dpy, kk->x11.window);
210    XDestroyWindow(kk->x11.dpy, kk->x11.window);
211    XCloseDisplay(kk->x11.dpy);
212
213    return 0;
214}
215
216static int x11_set_window_title(caca_t *kk, char const *title)
217{
218    XStoreName(kk->x11.dpy, kk->x11.window, title);
219    return 0;
220}
221
222static unsigned int x11_get_window_width(caca_t *kk)
223{
224    return kk->qq->width * kk->x11.font_width;
225}
226
227static unsigned int x11_get_window_height(caca_t *kk)
228{
229    return kk->qq->height * kk->x11.font_height;
230}
231
232static void x11_display(caca_t *kk)
233{
234    unsigned int x, y, len;
235
236    /* First draw the background colours. Splitting the process in two
237     * loops like this is actually slightly faster. */
238    for(y = 0; y < kk->qq->height; y++)
239    {
240        for(x = 0; x < kk->qq->width; x += len)
241        {
242            uint8_t *attr = kk->qq->attr + x + y * kk->qq->width;
243
244            len = 1;
245            while(x + len < kk->qq->width
246                   && (attr[len] >> 4) == (attr[0] >> 4))
247                len++;
248
249            XSetForeground(kk->x11.dpy, kk->x11.gc,
250                           kk->x11.colors[attr[0] >> 4]);
251            XFillRectangle(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc,
252                           x * kk->x11.font_width, y * kk->x11.font_height,
253                           len * kk->x11.font_width, kk->x11.font_height);
254        }
255    }
256
257    /* Then print the foreground characters */
258    for(y = 0; y < kk->qq->height; y++)
259    {
260        for(x = 0; x < kk->qq->width; x += len)
261        {
262            char buffer[BUFSIZ]; /* FIXME: use a smaller buffer */
263            uint32_t *chars = kk->qq->chars + x + y * kk->qq->width;
264            uint8_t *attr = kk->qq->attr + x + y * kk->qq->width;
265
266            len = 1;
267
268            /* Skip spaces */
269            if(chars[0] == ' ')
270                continue;
271
272            buffer[0] = chars[0] & 0x7f;
273
274            while(x + len < kk->qq->width
275                   && (attr[len] & 0xf) == (attr[0] & 0xf))
276            {
277                buffer[len] = chars[len] & 0x7f;
278                len++;
279            }
280
281            XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[attr[0] & 0xf]);
282            XDrawString(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc,
283                        x * kk->x11.font_width,
284                        (y + 1) * kk->x11.font_height - kk->x11.font_offset,
285                        buffer, len);
286        }
287    }
288
289    XCopyArea(kk->x11.dpy, kk->x11.pixmap, kk->x11.window, kk->x11.gc, 0, 0,
290              kk->qq->width * kk->x11.font_width,
291              kk->qq->height * kk->x11.font_height,
292              0, 0);
293    XFlush(kk->x11.dpy);
294}
295
296static void x11_handle_resize(caca_t *kk, unsigned int *new_width,
297                                          unsigned int *new_height)
298{
299    Pixmap new_pixmap;
300
301    *new_width = kk->x11.new_width;
302    *new_height = kk->x11.new_height;
303
304    new_pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window,
305                               kk->qq->width * kk->x11.font_width,
306                               kk->qq->height * kk->x11.font_height,
307                               DefaultDepth(kk->x11.dpy,
308                                            DefaultScreen(kk->x11.dpy)));
309    XCopyArea(kk->x11.dpy, kk->x11.pixmap, new_pixmap, kk->x11.gc, 0, 0,
310              kk->qq->width * kk->x11.font_width,
311              kk->qq->height * kk->x11.font_height, 0, 0);
312    XFreePixmap(kk->x11.dpy, kk->x11.pixmap);
313    kk->x11.pixmap = new_pixmap;
314}
315
316static unsigned int x11_get_event(caca_t *kk)
317{
318    unsigned int event = 0;
319    XEvent xevent;
320    char key;
321
322    while(XCheckWindowEvent(kk->x11.dpy, kk->x11.window,
323                            kk->x11.event_mask, &xevent) == True)
324    {
325        KeySym keysym;
326
327        /* Expose event */
328        if(xevent.type == Expose)
329        {
330            XCopyArea(kk->x11.dpy, kk->x11.pixmap,
331                      kk->x11.window, kk->x11.gc, 0, 0,
332                      kk->qq->width * kk->x11.font_width,
333                      kk->qq->height * kk->x11.font_height, 0, 0);
334            continue;
335        }
336
337        /* Resize event */
338        if(xevent.type == ConfigureNotify)
339        {
340            unsigned int w, h;
341
342            w = (xevent.xconfigure.width + kk->x11.font_width / 3)
343                  / kk->x11.font_width;
344            h = (xevent.xconfigure.height + kk->x11.font_height / 3)
345                  / kk->x11.font_height;
346
347            if(!w || !h || (w == kk->qq->width && h == kk->qq->height))
348                continue;
349
350            kk->x11.new_width = w;
351            kk->x11.new_height = h;
352
353            /* If we are already resizing, ignore the new signal */
354            if(kk->resize)
355                continue;
356
357            kk->resize = 1;
358
359            return CACA_EVENT_RESIZE;
360        }
361
362        /* Check for mouse motion events */
363        if(xevent.type == MotionNotify)
364        {
365            unsigned int newx = xevent.xmotion.x / kk->x11.font_width;
366            unsigned int newy = xevent.xmotion.y / kk->x11.font_height;
367
368            if(newx >= kk->qq->width)
369                newx = kk->qq->width - 1;
370            if(newy >= kk->qq->height)
371                newy = kk->qq->height - 1;
372
373            if(kk->mouse_x == newx && kk->mouse_y == newy)
374                continue;
375
376            kk->mouse_x = newx;
377            kk->mouse_y = newy;
378
379            return CACA_EVENT_MOUSE_MOTION | (kk->mouse_x << 12) | kk->mouse_y;
380        }
381
382        /* Check for mouse press and release events */
383        if(xevent.type == ButtonPress)
384            return CACA_EVENT_MOUSE_PRESS
385                    | ((XButtonEvent *)&xevent)->button;
386
387        if(xevent.type == ButtonRelease)
388            return CACA_EVENT_MOUSE_RELEASE
389                    | ((XButtonEvent *)&xevent)->button;
390
391        /* Check for key press and release events */
392        if(xevent.type == KeyPress)
393            event |= CACA_EVENT_KEY_PRESS;
394        else if(xevent.type == KeyRelease)
395            event |= CACA_EVENT_KEY_RELEASE;
396        else
397            continue;
398
399        if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
400            return event | key;
401
402        keysym = XKeycodeToKeysym(kk->x11.dpy, xevent.xkey.keycode, 0);
403        switch(keysym)
404        {
405        case XK_F1:    return event | CACA_KEY_F1;
406        case XK_F2:    return event | CACA_KEY_F2;
407        case XK_F3:    return event | CACA_KEY_F3;
408        case XK_F4:    return event | CACA_KEY_F4;
409        case XK_F5:    return event | CACA_KEY_F5;
410        case XK_F6:    return event | CACA_KEY_F6;
411        case XK_F7:    return event | CACA_KEY_F7;
412        case XK_F8:    return event | CACA_KEY_F8;
413        case XK_F9:    return event | CACA_KEY_F9;
414        case XK_F10:   return event | CACA_KEY_F10;
415        case XK_F11:   return event | CACA_KEY_F11;
416        case XK_F12:   return event | CACA_KEY_F12;
417        case XK_F13:   return event | CACA_KEY_F13;
418        case XK_F14:   return event | CACA_KEY_F14;
419        case XK_F15:   return event | CACA_KEY_F15;
420        case XK_Left:  return event | CACA_KEY_LEFT;
421        case XK_Right: return event | CACA_KEY_RIGHT;
422        case XK_Up:    return event | CACA_KEY_UP;
423        case XK_Down:  return event | CACA_KEY_DOWN;
424        default:       return CACA_EVENT_NONE;
425        }
426    }
427
428    return CACA_EVENT_NONE;
429}
430
431/*
432 * XXX: following functions are local
433 */
434
435static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
436{
437    /* Ignore the error */
438    return 0;
439}
440
441/*
442 * Driver initialisation
443 */
444
445void x11_init_driver(caca_t *kk)
446{
447    kk->driver.driver = CACA_DRIVER_X11;
448
449    kk->driver.init_graphics = x11_init_graphics;
450    kk->driver.end_graphics = x11_end_graphics;
451    kk->driver.set_window_title = x11_set_window_title;
452    kk->driver.get_window_width = x11_get_window_width;
453    kk->driver.get_window_height = x11_get_window_height;
454    kk->driver.display = x11_display;
455    kk->driver.handle_resize = x11_handle_resize;
456    kk->driver.get_event = x11_get_event;
457}
458
459#endif /* USE_X11 */
460
Note: See TracBrowser for help on using the repository browser.