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

Last change on this file since 550 was 550, checked in by Sam Hocevar, 14 years ago
  • Finished moving everything driver-specific to the apropriate driver_*.c files. Phew. Now they just need a few comments.
  • 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 550 2006-03-08 10:33:01Z 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            attrset(kk->drv.p->attr[*attr++]);
196            addch(*chars++ & 0x7f);
197        }
198    }
199    refresh();
200}
201
202static void ncurses_handle_resize(caca_t *kk, unsigned int *new_width,
203                                              unsigned int *new_height)
204{
205    struct winsize size;
206
207    *new_width = kk->qq->width;
208    *new_height = kk->qq->height;
209
210    if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
211    {
212        *new_width = size.ws_col;
213        *new_height = size.ws_row;
214#if defined(HAVE_RESIZE_TERM)
215        resize_term(*new_height, *new_width);
216#else
217        resizeterm(*new_height, *new_width);
218#endif
219        wrefresh(curscr);
220    }
221}
222
223static unsigned int ncurses_get_event(caca_t *kk)
224{
225    unsigned int event;
226    int intkey;
227
228    if(kk->resize_event)
229    {
230        kk->resize_event = 0;
231        kk->resize = 1;
232        return CACA_EVENT_RESIZE;
233    }
234
235    intkey = getch();
236    if(intkey == ERR)
237        return CACA_EVENT_NONE;
238
239    if(intkey < 0x100)
240    {
241        return CACA_EVENT_KEY_PRESS | intkey;
242    }
243
244    if(intkey == KEY_MOUSE)
245    {
246        MEVENT mevent;
247        getmouse(&mevent);
248
249        switch(mevent.bstate)
250        {
251            case BUTTON1_PRESSED:
252                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
253                break;
254            case BUTTON1_RELEASED:
255                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
256                break;
257            case BUTTON1_CLICKED:
258                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
259                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
260                break;
261            case BUTTON1_DOUBLE_CLICKED:
262                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
263                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
264                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
265                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
266                break;
267            case BUTTON1_TRIPLE_CLICKED:
268                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
269                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
270                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
271                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
272                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
273                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
274                break;
275            case BUTTON1_RESERVED_EVENT:
276                break;
277
278            case BUTTON2_PRESSED:
279                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
280                break;
281            case BUTTON2_RELEASED:
282                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
283                break;
284            case BUTTON2_CLICKED:
285                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
286                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
287                break;
288            case BUTTON2_DOUBLE_CLICKED:
289                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
290                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
291                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
292                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
293                break;
294            case BUTTON2_TRIPLE_CLICKED:
295                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
296                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
297                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
298                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
299                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
300                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
301                break;
302            case BUTTON2_RESERVED_EVENT:
303                break;
304
305            case BUTTON3_PRESSED:
306                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
307                break;
308            case BUTTON3_RELEASED:
309                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
310                break;
311            case BUTTON3_CLICKED:
312                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
313                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
314                break;
315            case BUTTON3_DOUBLE_CLICKED:
316                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
317                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
318                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
319                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
320                break;
321            case BUTTON3_TRIPLE_CLICKED:
322                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
323                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
324                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
325                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
326                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
327                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
328                break;
329            case BUTTON3_RESERVED_EVENT:
330                break;
331
332            case BUTTON4_PRESSED:
333                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
334                break;
335            case BUTTON4_RELEASED:
336                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
337                break;
338            case BUTTON4_CLICKED:
339                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
340                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
341                break;
342            case BUTTON4_DOUBLE_CLICKED:
343                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
344                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
345                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
346                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
347                break;
348            case BUTTON4_TRIPLE_CLICKED:
349                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
350                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
351                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
352                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
353                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
354                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
355                break;
356            case BUTTON4_RESERVED_EVENT:
357                break;
358
359            default:
360                break;
361        }
362
363        if(kk->mouse_x == (unsigned int)mevent.x &&
364           kk->mouse_y == (unsigned int)mevent.y)
365            return _pop_event(kk);
366
367        kk->mouse_x = mevent.x;
368        kk->mouse_y = mevent.y;
369
370        return CACA_EVENT_MOUSE_MOTION | (kk->mouse_x << 12) | kk->mouse_y;
371    }
372
373    event = CACA_EVENT_KEY_PRESS;
374
375    switch(intkey)
376    {
377        case KEY_UP: return event | CACA_KEY_UP;
378        case KEY_DOWN: return event | CACA_KEY_DOWN;
379        case KEY_LEFT: return event | CACA_KEY_LEFT;
380        case KEY_RIGHT: return event | CACA_KEY_RIGHT;
381
382        case KEY_IC: return event | CACA_KEY_INSERT;
383        case KEY_DC: return event | CACA_KEY_DELETE;
384        case KEY_HOME: return event | CACA_KEY_HOME;
385        case KEY_END: return event | CACA_KEY_END;
386        case KEY_PPAGE: return event | CACA_KEY_PAGEUP;
387        case KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
388
389        case KEY_F(1): return event | CACA_KEY_F1;
390        case KEY_F(2): return event | CACA_KEY_F2;
391        case KEY_F(3): return event | CACA_KEY_F3;
392        case KEY_F(4): return event | CACA_KEY_F4;
393        case KEY_F(5): return event | CACA_KEY_F5;
394        case KEY_F(6): return event | CACA_KEY_F6;
395        case KEY_F(7): return event | CACA_KEY_F7;
396        case KEY_F(8): return event | CACA_KEY_F8;
397        case KEY_F(9): return event | CACA_KEY_F9;
398        case KEY_F(10): return event | CACA_KEY_F10;
399        case KEY_F(11): return event | CACA_KEY_F11;
400        case KEY_F(12): return event | CACA_KEY_F12;
401    }
402
403    return CACA_EVENT_NONE;
404}
405
406/*
407 * XXX: following functions are local
408 */
409
410#if defined(HAVE_SIGNAL)
411static RETSIGTYPE sigwinch_handler(int sig)
412{
413    sigwinch_kk->resize_event = 1;
414
415    signal(SIGWINCH, sigwinch_handler);;
416}
417#endif
418
419#if defined(HAVE_GETENV) && defined(HAVE_PUTENV)
420static void ncurses_check_terminal(void)
421{
422    char *term, *colorterm, *other;
423
424    term = getenv("TERM");
425    colorterm = getenv("COLORTERM");
426
427    if(term && !strcmp(term, "xterm"))
428    {
429        /* If we are using gnome-terminal, it's really a 16 colour terminal */
430        if(colorterm && !strcmp(colorterm, "gnome-terminal"))
431        {
432            SCREEN *screen;
433            screen = newterm("xterm-16color", stdout, stdin);
434            if(screen == NULL)
435                return;
436            endwin();
437            (void)putenv("TERM=xterm-16color");
438            return;
439        }
440
441        /* Ditto if we are using Konsole */
442        other = getenv("KONSOLE_DCOP_SESSION");
443        if(other)
444        {
445            SCREEN *screen;
446            screen = newterm("xterm-16color", stdout, stdin);
447            if(screen == NULL)
448                return;
449            endwin();
450            (void)putenv("TERM=xterm-16color");
451            return;
452        }
453    }
454}
455#endif
456
457/*
458 * Driver initialisation
459 */
460
461void ncurses_init_driver(caca_t *kk)
462{
463    kk->drv.driver = CACA_DRIVER_NCURSES;
464
465    kk->drv.init_graphics = ncurses_init_graphics;
466    kk->drv.end_graphics = ncurses_end_graphics;
467    kk->drv.set_window_title = ncurses_set_window_title;
468    kk->drv.get_window_width = ncurses_get_window_width;
469    kk->drv.get_window_height = ncurses_get_window_height;
470    kk->drv.display = ncurses_display;
471    kk->drv.handle_resize = ncurses_handle_resize;
472    kk->drv.get_event = ncurses_get_event;
473}
474
475#endif /* USE_NCURSES */
476
Note: See TracBrowser for help on using the repository browser.