source: libcaca/trunk/caca/driver_ncurses.c @ 631

Last change on this file since 631 was 629, checked in by Sam Hocevar, 14 years ago
  • Use UTF-8 enable ncurses (ncursesw) if available. Doesn't seem to work.
  • Property svn:keywords set to Id
File size: 14.7 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_ncurses.c
13 *  \version \$Id: driver_ncurses.c 629 2006-03-16 12:49:20Z sam $
14 *  \author Sam Hocevar <sam@zoy.org>
15 *  \brief Ncurses driver
16 *
17 *  This file contains the libcaca Ncurses input and output driver
18 */
19
20#include "config.h"
21
22#if defined(USE_NCURSES)
23
24#if defined(HAVE_NCURSESW_NCURSES_H)
25#   include <ncursesw/ncurses.h>
26#elif defined(HAVE_NCURSES_NCURSES_H)
27#   include <ncurses/ncurses.h>
28#elif defined(HAVE_NCURSES_H)
29#   include <ncurses.h>
30#else
31#   include <curses.h>
32#endif
33
34#include <stdlib.h>
35#include <string.h>
36
37#if defined(HAVE_SIGNAL_H)
38#   include <signal.h>
39#endif
40#if defined(HAVE_SYS_IOCTL_H)
41#   include <sys/ioctl.h>
42#endif
43
44#include "caca.h"
45#include "caca_internals.h"
46#include "cucul.h"
47#include "cucul_internals.h"
48
49/*
50 * Local functions
51 */
52
53#if defined(HAVE_SIGNAL)
54static RETSIGTYPE sigwinch_handler(int);
55static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */
56#endif
57#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
58static void ncurses_check_terminal(void);
59#endif
60static void ncurses_write_utf32(uint32_t);
61
62struct driver_private
63{
64    int attr[16*16];
65    mmask_t oldmask;
66};
67
68static int ncurses_init_graphics(caca_t *kk)
69{
70    static int curses_colors[] =
71    {
72        /* Standard curses colours */
73        COLOR_BLACK,
74        COLOR_BLUE,
75        COLOR_GREEN,
76        COLOR_CYAN,
77        COLOR_RED,
78        COLOR_MAGENTA,
79        COLOR_YELLOW,
80        COLOR_WHITE,
81        /* Extra values for xterm-16color */
82        COLOR_BLACK + 8,
83        COLOR_BLUE + 8,
84        COLOR_GREEN + 8,
85        COLOR_CYAN + 8,
86        COLOR_RED + 8,
87        COLOR_MAGENTA + 8,
88        COLOR_YELLOW + 8,
89        COLOR_WHITE + 8
90    };
91
92    mmask_t newmask;
93    int fg, bg, max;
94
95    kk->drv.p = malloc(sizeof(struct driver_private));
96
97#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
98    ncurses_check_terminal();
99#endif
100
101#if defined(HAVE_SIGNAL)
102    sigwinch_kk = kk;
103    signal(SIGWINCH, sigwinch_handler);
104#endif
105
106    initscr();
107    keypad(stdscr, TRUE);
108    nonl();
109    raw();
110    noecho();
111    nodelay(stdscr, TRUE);
112    curs_set(0);
113
114    /* Activate mouse */
115    newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
116    mousemask(newmask, &kk->drv.p->oldmask);
117    mouseinterval(-1); /* No click emulation */
118
119    /* Set the escape delay to a ridiculously low value */
120    ESCDELAY = 10;
121
122    /* Activate colour */
123    start_color();
124
125    /* If COLORS == 16, it means the terminal supports full bright colours
126     * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
127     * we can build 16*16 colour pairs.
128     * If COLORS == 8, it means the terminal does not know about bright
129     * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
130     * and \e[5m). We can only build 8*8 colour pairs. */
131    max = COLORS >= 16 ? 16 : 8;
132
133    for(bg = 0; bg < max; bg++)
134        for(fg = 0; fg < max; fg++)
135        {
136            /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
137             * is light gray on black. Some terminals don't like this
138             * colour pair to be redefined. */
139            int col = ((max + 7 - fg) % max) + max * bg;
140            init_pair(col, curses_colors[fg], curses_colors[bg]);
141            kk->drv.p->attr[fg + 16 * bg] = COLOR_PAIR(col);
142
143            if(max == 8)
144            {
145                /* Bright fg on simple bg */
146                kk->drv.p->attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
147                /* Simple fg on bright bg */
148                kk->drv.p->attr[fg + 16 * (bg + 8)] = A_BLINK
149                                                    | COLOR_PAIR(col);
150                /* Bright fg on bright bg */
151                kk->drv.p->attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
152                                                        | COLOR_PAIR(col);
153            }
154        }
155
156    _cucul_set_size(kk->qq, COLS, LINES);
157
158    return 0;
159}
160
161static int ncurses_end_graphics(caca_t *kk)
162{
163    mousemask(kk->drv.p->oldmask, NULL);
164    curs_set(1);
165    noraw();
166    endwin();
167
168    free(kk->drv.p);
169
170    return 0;
171}
172
173static int ncurses_set_window_title(caca_t *kk, char const *title)
174{
175    return 0;
176}
177
178static unsigned int ncurses_get_window_width(caca_t *kk)
179{
180    /* Fallback to a 6x10 font */
181    return kk->qq->width * 6;
182}
183
184static unsigned int ncurses_get_window_height(caca_t *kk)
185{
186    /* Fallback to a 6x10 font */
187    return kk->qq->height * 10;
188}
189
190static void ncurses_display(caca_t *kk)
191{
192    int x, y;
193    uint8_t *attr = kk->qq->attr;
194    uint32_t *chars = kk->qq->chars;
195    for(y = 0; y < (int)kk->qq->height; y++)
196    {
197        move(y, 0);
198        for(x = kk->qq->width; x--; )
199        {
200            attrset(kk->drv.p->attr[*attr++]);
201            ncurses_write_utf32(*chars++);
202        }
203    }
204    refresh();
205}
206
207static void ncurses_handle_resize(caca_t *kk)
208{
209    struct winsize size;
210
211    if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
212    {
213        kk->resize.w = size.ws_col;
214        kk->resize.h = size.ws_row;
215#if defined(HAVE_RESIZE_TERM)
216        resize_term(kk->resize.h, kk->resize.w);
217#else
218        resizeterm(*kk->resize.h, *kk->resize.w);
219#endif
220        wrefresh(curscr);
221        return;
222    }
223
224    /* Fallback */
225    kk->resize.w = kk->qq->width;
226    kk->resize.h = kk->qq->height;
227}
228
229static unsigned int ncurses_get_event(caca_t *kk)
230{
231    unsigned int event;
232    int intkey;
233
234    intkey = getch();
235    if(intkey == ERR)
236        return CACA_EVENT_NONE;
237
238    if(intkey < 0x100)
239    {
240        return CACA_EVENT_KEY_PRESS | intkey;
241    }
242
243    if(intkey == KEY_MOUSE)
244    {
245        MEVENT mevent;
246        getmouse(&mevent);
247
248        switch(mevent.bstate)
249        {
250            case BUTTON1_PRESSED:
251                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
252                break;
253            case BUTTON1_RELEASED:
254                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
255                break;
256            case BUTTON1_CLICKED:
257                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
258                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
259                break;
260            case BUTTON1_DOUBLE_CLICKED:
261                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
262                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
263                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
264                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
265                break;
266            case BUTTON1_TRIPLE_CLICKED:
267                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
268                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
269                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
270                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
271                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
272                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
273                break;
274            case BUTTON1_RESERVED_EVENT:
275                break;
276
277            case BUTTON2_PRESSED:
278                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
279                break;
280            case BUTTON2_RELEASED:
281                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
282                break;
283            case BUTTON2_CLICKED:
284                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
285                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
286                break;
287            case BUTTON2_DOUBLE_CLICKED:
288                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
289                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
290                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
291                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
292                break;
293            case BUTTON2_TRIPLE_CLICKED:
294                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
295                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
296                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
297                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
298                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
299                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
300                break;
301            case BUTTON2_RESERVED_EVENT:
302                break;
303
304            case BUTTON3_PRESSED:
305                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
306                break;
307            case BUTTON3_RELEASED:
308                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
309                break;
310            case BUTTON3_CLICKED:
311                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
312                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
313                break;
314            case BUTTON3_DOUBLE_CLICKED:
315                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
316                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
317                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
318                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
319                break;
320            case BUTTON3_TRIPLE_CLICKED:
321                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
322                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
323                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
324                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
325                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
326                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
327                break;
328            case BUTTON3_RESERVED_EVENT:
329                break;
330
331            case BUTTON4_PRESSED:
332                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
333                break;
334            case BUTTON4_RELEASED:
335                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
336                break;
337            case BUTTON4_CLICKED:
338                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
339                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
340                break;
341            case BUTTON4_DOUBLE_CLICKED:
342                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
343                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
344                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
345                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
346                break;
347            case BUTTON4_TRIPLE_CLICKED:
348                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
349                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
350                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
351                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
352                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
353                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
354                break;
355            case BUTTON4_RESERVED_EVENT:
356                break;
357
358            default:
359                break;
360        }
361
362        if(kk->mouse.x == (unsigned int)mevent.x &&
363           kk->mouse.y == (unsigned int)mevent.y)
364            return _pop_event(kk);
365
366        kk->mouse.x = mevent.x;
367        kk->mouse.y = mevent.y;
368
369        return CACA_EVENT_MOUSE_MOTION | (kk->mouse.x << 12) | kk->mouse.y;
370    }
371
372    event = CACA_EVENT_KEY_PRESS;
373
374    switch(intkey)
375    {
376        case KEY_UP: return event | CACA_KEY_UP;
377        case KEY_DOWN: return event | CACA_KEY_DOWN;
378        case KEY_LEFT: return event | CACA_KEY_LEFT;
379        case KEY_RIGHT: return event | CACA_KEY_RIGHT;
380
381        case KEY_IC: return event | CACA_KEY_INSERT;
382        case KEY_DC: return event | CACA_KEY_DELETE;
383        case KEY_HOME: return event | CACA_KEY_HOME;
384        case KEY_END: return event | CACA_KEY_END;
385        case KEY_PPAGE: return event | CACA_KEY_PAGEUP;
386        case KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
387
388        case KEY_F(1): return event | CACA_KEY_F1;
389        case KEY_F(2): return event | CACA_KEY_F2;
390        case KEY_F(3): return event | CACA_KEY_F3;
391        case KEY_F(4): return event | CACA_KEY_F4;
392        case KEY_F(5): return event | CACA_KEY_F5;
393        case KEY_F(6): return event | CACA_KEY_F6;
394        case KEY_F(7): return event | CACA_KEY_F7;
395        case KEY_F(8): return event | CACA_KEY_F8;
396        case KEY_F(9): return event | CACA_KEY_F9;
397        case KEY_F(10): return event | CACA_KEY_F10;
398        case KEY_F(11): return event | CACA_KEY_F11;
399        case KEY_F(12): return event | CACA_KEY_F12;
400    }
401
402    return CACA_EVENT_NONE;
403}
404
405/*
406 * XXX: following functions are local
407 */
408
409#if defined(HAVE_SIGNAL)
410static RETSIGTYPE sigwinch_handler(int sig)
411{
412    sigwinch_kk->resize.resized = 1;
413
414    signal(SIGWINCH, sigwinch_handler);
415}
416#endif
417
418#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
419static void ncurses_check_terminal(void)
420{
421    char *term, *colorterm, *other;
422
423    term = getenv("TERM");
424    colorterm = getenv("COLORTERM");
425
426    if(term && !strcmp(term, "xterm"))
427    {
428        /* If we are using gnome-terminal, it's really a 16 colour terminal */
429        if(colorterm && !strcmp(colorterm, "gnome-terminal"))
430        {
431            SCREEN *screen;
432            screen = newterm("xterm-16color", stdout, stdin);
433            if(screen == NULL)
434                return;
435            endwin();
436            (void)putenv("TERM=xterm-16color");
437            return;
438        }
439
440        /* Ditto if we are using Konsole */
441        other = getenv("KONSOLE_DCOP_SESSION");
442        if(other)
443        {
444            SCREEN *screen;
445            screen = newterm("xterm-16color", stdout, stdin);
446            if(screen == NULL)
447                return;
448            endwin();
449            (void)putenv("TERM=xterm-16color");
450            return;
451        }
452    }
453}
454#endif
455
456static void ncurses_write_utf32(uint32_t c)
457{
458#if defined(HAVE_NCURSESW_NCURSES_H)
459    static const uint8_t mark[7] =
460    {
461        0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
462    };
463
464    char buf[10], *parser;
465    int bytes;
466#endif
467
468    if(c < 0x80)
469    {
470        addch(c);
471        return;
472    }
473
474#if defined(HAVE_NCURSESW_NCURSES_H)
475    if(c < 0x10000)
476    {
477        addch(c); /* FIXME: doesn't work either */
478        return;
479    }
480
481    bytes = (c < 0x800) ? 2 : (c < 0x10000) ? 3 : 4;
482    buf[bytes] = '\0';
483    parser = buf + bytes;
484
485    switch(bytes)
486    {
487        case 4: *--parser = (c | 0x80) & 0xbf; c >>= 6;
488        case 3: *--parser = (c | 0x80) & 0xbf; c >>= 6;
489        case 2: *--parser = (c | 0x80) & 0xbf; c >>= 6;
490    }
491    *--parser = c | mark[bytes];
492
493    addstr(buf);
494#else
495    addch(' ');
496#endif
497}
498
499/*
500 * Driver initialisation
501 */
502
503void ncurses_init_driver(caca_t *kk)
504{
505    kk->drv.driver = CACA_DRIVER_NCURSES;
506
507    kk->drv.init_graphics = ncurses_init_graphics;
508    kk->drv.end_graphics = ncurses_end_graphics;
509    kk->drv.set_window_title = ncurses_set_window_title;
510    kk->drv.get_window_width = ncurses_get_window_width;
511    kk->drv.get_window_height = ncurses_get_window_height;
512    kk->drv.display = ncurses_display;
513    kk->drv.handle_resize = ncurses_handle_resize;
514    kk->drv.get_event = ncurses_get_event;
515}
516
517#endif /* USE_NCURSES */
518
Note: See TracBrowser for help on using the repository browser.