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

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