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

Last change on this file since 599 was 582, checked in by Sam Hocevar, 14 years ago
  • Use the internal version of cucul_set_size in output drivers so that they can force a resize. We need to find a way to make those specific cucul - caca interactions unavailable to the clueless user.
  • Property svn:keywords set to Id
File size: 13.9 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 582 2006-03-10 10:01:22Z 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_NCURSES_H)
25#   include <ncurses.h>
26#else
27#   include <curses.h>
28#endif
29
30#include <stdlib.h>
31#include <string.h>
32
33#if defined(HAVE_SIGNAL_H)
34#   include <signal.h>
35#endif
36#if defined(HAVE_SYS_IOCTL_H)
37#   include <sys/ioctl.h>
38#endif
39
40#include "caca.h"
41#include "caca_internals.h"
42#include "cucul.h"
43#include "cucul_internals.h"
44
45/*
46 * Local functions
47 */
48
49#if defined(HAVE_SIGNAL)
50static RETSIGTYPE sigwinch_handler(int);
51static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */
52#endif
53#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
54static void ncurses_check_terminal(void);
55#endif
56
57struct driver_private
58{
59    int attr[16*16];
60    mmask_t oldmask;
61};
62
63static int ncurses_init_graphics(caca_t *kk)
64{
65    static int curses_colors[] =
66    {
67        /* Standard curses colours */
68        COLOR_BLACK,
69        COLOR_BLUE,
70        COLOR_GREEN,
71        COLOR_CYAN,
72        COLOR_RED,
73        COLOR_MAGENTA,
74        COLOR_YELLOW,
75        COLOR_WHITE,
76        /* Extra values for xterm-16color */
77        COLOR_BLACK + 8,
78        COLOR_BLUE + 8,
79        COLOR_GREEN + 8,
80        COLOR_CYAN + 8,
81        COLOR_RED + 8,
82        COLOR_MAGENTA + 8,
83        COLOR_YELLOW + 8,
84        COLOR_WHITE + 8
85    };
86
87    mmask_t newmask;
88    int fg, bg, max;
89
90    kk->drv.p = malloc(sizeof(struct driver_private));
91
92#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
93    ncurses_check_terminal();
94#endif
95
96#if defined(HAVE_SIGNAL)
97    sigwinch_kk = kk;
98    signal(SIGWINCH, sigwinch_handler);
99#endif
100
101    initscr();
102    keypad(stdscr, TRUE);
103    nonl();
104    raw();
105    noecho();
106    nodelay(stdscr, TRUE);
107    curs_set(0);
108
109    /* Activate mouse */
110    newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
111    mousemask(newmask, &kk->drv.p->oldmask);
112    mouseinterval(-1); /* No click emulation */
113
114    /* Set the escape delay to a ridiculously low value */
115    ESCDELAY = 10;
116
117    /* Activate colour */
118    start_color();
119
120    /* If COLORS == 16, it means the terminal supports full bright colours
121     * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
122     * we can build 16*16 colour pairs.
123     * If COLORS == 8, it means the terminal does not know about bright
124     * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
125     * and \e[5m). We can only build 8*8 colour pairs. */
126    max = COLORS >= 16 ? 16 : 8;
127
128    for(bg = 0; bg < max; bg++)
129        for(fg = 0; fg < max; fg++)
130        {
131            /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
132             * is light gray on black. Some terminals don't like this
133             * colour pair to be redefined. */
134            int col = ((max + 7 - fg) % max) + max * bg;
135            init_pair(col, curses_colors[fg], curses_colors[bg]);
136            kk->drv.p->attr[fg + 16 * bg] = COLOR_PAIR(col);
137
138            if(max == 8)
139            {
140                /* Bright fg on simple bg */
141                kk->drv.p->attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
142                /* Simple fg on bright bg */
143                kk->drv.p->attr[fg + 16 * (bg + 8)] = A_BLINK
144                                                    | COLOR_PAIR(col);
145                /* Bright fg on bright bg */
146                kk->drv.p->attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
147                                                        | COLOR_PAIR(col);
148            }
149        }
150
151    _cucul_set_size(kk->qq, COLS, LINES);
152
153    return 0;
154}
155
156static int ncurses_end_graphics(caca_t *kk)
157{
158    mousemask(kk->drv.p->oldmask, NULL);
159    curs_set(1);
160    noraw();
161    endwin();
162
163    free(kk->drv.p);
164
165    return 0;
166}
167
168static int ncurses_set_window_title(caca_t *kk, char const *title)
169{
170    return 0;
171}
172
173static unsigned int ncurses_get_window_width(caca_t *kk)
174{
175    /* Fallback to a 6x10 font */
176    return kk->qq->width * 6;
177}
178
179static unsigned int ncurses_get_window_height(caca_t *kk)
180{
181    /* Fallback to a 6x10 font */
182    return kk->qq->height * 10;
183}
184
185static void ncurses_display(caca_t *kk)
186{
187    int x, y;
188    uint8_t *attr = kk->qq->attr;
189    uint32_t *chars = kk->qq->chars;
190    for(y = 0; y < (int)kk->qq->height; y++)
191    {
192        move(y, 0);
193        for(x = kk->qq->width; x--; )
194        {
195            uint32_t c = *chars++;
196
197            attrset(kk->drv.p->attr[*attr++]);
198            if(c > 0x00000020 && c < 0x00000080)
199                addch((char)c);
200            else
201                addch(' ');
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
456/*
457 * Driver initialisation
458 */
459
460void ncurses_init_driver(caca_t *kk)
461{
462    kk->drv.driver = CACA_DRIVER_NCURSES;
463
464    kk->drv.init_graphics = ncurses_init_graphics;
465    kk->drv.end_graphics = ncurses_end_graphics;
466    kk->drv.set_window_title = ncurses_set_window_title;
467    kk->drv.get_window_width = ncurses_get_window_width;
468    kk->drv.get_window_height = ncurses_get_window_height;
469    kk->drv.display = ncurses_display;
470    kk->drv.handle_resize = ncurses_handle_resize;
471    kk->drv.get_event = ncurses_get_event;
472}
473
474#endif /* USE_NCURSES */
475
Note: See TracBrowser for help on using the repository browser.