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

Last change on this file since 549 was 549, checked in by Sam Hocevar, 14 years ago
  • Cleaned up useless header includes.
  • Property svn:keywords set to Id
File size: 12.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_ncurses.c
13 *  \version \$Id: driver_ncurses.c 549 2006-03-08 09:51:53Z 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#if defined(HAVE_SIGNAL_H)
31#   include <signal.h>
32#endif
33#if defined(HAVE_SYS_IOCTL_H)
34#   include <sys/ioctl.h>
35#endif
36
37#include "caca.h"
38#include "caca_internals.h"
39#include "cucul.h"
40#include "cucul_internals.h"
41
42/*
43 * Local functions
44 */
45
46#if defined(HAVE_SIGNAL)
47static RETSIGTYPE sigwinch_handler(int);
48static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */
49#endif
50
51static int ncurses_init_graphics(caca_t *kk)
52{
53    static int curses_colors[] =
54    {
55        /* Standard curses colours */
56        COLOR_BLACK,
57        COLOR_BLUE,
58        COLOR_GREEN,
59        COLOR_CYAN,
60        COLOR_RED,
61        COLOR_MAGENTA,
62        COLOR_YELLOW,
63        COLOR_WHITE,
64        /* Extra values for xterm-16color */
65        COLOR_BLACK + 8,
66        COLOR_BLUE + 8,
67        COLOR_GREEN + 8,
68        COLOR_CYAN + 8,
69        COLOR_RED + 8,
70        COLOR_MAGENTA + 8,
71        COLOR_YELLOW + 8,
72        COLOR_WHITE + 8
73    };
74
75    mmask_t newmask;
76    int fg, bg, max;
77
78#if defined(HAVE_SIGNAL)
79    sigwinch_kk = kk;
80    signal(SIGWINCH, sigwinch_handler);
81#endif
82
83    initscr();
84    keypad(stdscr, TRUE);
85    nonl();
86    raw();
87    noecho();
88    nodelay(stdscr, TRUE);
89    curs_set(0);
90
91    /* Activate mouse */
92    newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
93    mousemask(newmask, &kk->ncurses.oldmask);
94    mouseinterval(-1); /* No click emulation */
95
96    /* Set the escape delay to a ridiculously low value */
97    ESCDELAY = 10;
98
99    /* Activate colour */
100    start_color();
101
102    /* If COLORS == 16, it means the terminal supports full bright colours
103     * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
104     * we can build 16*16 colour pairs.
105     * If COLORS == 8, it means the terminal does not know about bright
106     * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
107     * and \e[5m). We can only build 8*8 colour pairs. */
108    max = COLORS >= 16 ? 16 : 8;
109
110    for(bg = 0; bg < max; bg++)
111        for(fg = 0; fg < max; fg++)
112        {
113            /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
114             * is light gray on black. Some terminals don't like this
115             * colour pair to be redefined. */
116            int col = ((max + 7 - fg) % max) + max * bg;
117            init_pair(col, curses_colors[fg], curses_colors[bg]);
118            kk->ncurses.attr[fg + 16 * bg] = COLOR_PAIR(col);
119
120            if(max == 8)
121            {
122                /* Bright fg on simple bg */
123                kk->ncurses.attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
124                /* Simple fg on bright bg */
125                kk->ncurses.attr[fg + 16 * (bg + 8)] = A_BLINK
126                                                    | COLOR_PAIR(col);
127                /* Bright fg on bright bg */
128                kk->ncurses.attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
129                                                        | COLOR_PAIR(col);
130            }
131        }
132
133    cucul_set_size(kk->qq, COLS, LINES);
134
135    return 0;
136}
137
138static int ncurses_end_graphics(caca_t *kk)
139{
140    mousemask(kk->ncurses.oldmask, NULL);
141    curs_set(1);
142    noraw();
143    endwin();
144
145    return 0;
146}
147
148static int ncurses_set_window_title(caca_t *kk, char const *title)
149{
150    return 0;
151}
152
153static unsigned int ncurses_get_window_width(caca_t *kk)
154{
155    /* Fallback to a 6x10 font */
156    return kk->qq->width * 6;
157}
158
159static unsigned int ncurses_get_window_height(caca_t *kk)
160{
161    /* Fallback to a 6x10 font */
162    return kk->qq->height * 10;
163}
164
165static void ncurses_display(caca_t *kk)
166{
167    int x, y;
168    uint8_t *attr = kk->qq->attr;
169    uint32_t *chars = kk->qq->chars;
170    for(y = 0; y < (int)kk->qq->height; y++)
171    {
172        move(y, 0);
173        for(x = kk->qq->width; x--; )
174        {
175            attrset(kk->ncurses.attr[*attr++]);
176            addch(*chars++ & 0x7f);
177        }
178    }
179    refresh();
180}
181
182static void ncurses_handle_resize(caca_t *kk, unsigned int *new_width,
183                                              unsigned int *new_height)
184{
185    struct winsize size;
186
187    *new_width = kk->qq->width;
188    *new_height = kk->qq->height;
189
190    if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
191    {
192        *new_width = size.ws_col;
193        *new_height = size.ws_row;
194#if defined(HAVE_RESIZE_TERM)
195        resize_term(*new_height, *new_width);
196#else
197        resizeterm(*new_height, *new_width);
198#endif
199        wrefresh(curscr);
200    }
201}
202
203static unsigned int ncurses_get_event(caca_t *kk)
204{
205    unsigned int event;
206    int intkey;
207
208    if(kk->resize_event)
209    {
210        kk->resize_event = 0;
211        kk->resize = 1;
212        return CACA_EVENT_RESIZE;
213    }
214
215    intkey = getch();
216    if(intkey == ERR)
217        return CACA_EVENT_NONE;
218
219    if(intkey < 0x100)
220    {
221        return CACA_EVENT_KEY_PRESS | intkey;
222    }
223
224    if(intkey == KEY_MOUSE)
225    {
226        MEVENT mevent;
227        getmouse(&mevent);
228
229        switch(mevent.bstate)
230        {
231            case BUTTON1_PRESSED:
232                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
233                break;
234            case BUTTON1_RELEASED:
235                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
236                break;
237            case BUTTON1_CLICKED:
238                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
239                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
240                break;
241            case BUTTON1_DOUBLE_CLICKED:
242                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
243                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
244                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
245                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
246                break;
247            case BUTTON1_TRIPLE_CLICKED:
248                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
249                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
250                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
251                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
252                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
253                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
254                break;
255            case BUTTON1_RESERVED_EVENT:
256                break;
257
258            case BUTTON2_PRESSED:
259                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
260                break;
261            case BUTTON2_RELEASED:
262                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
263                break;
264            case BUTTON2_CLICKED:
265                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
266                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
267                break;
268            case BUTTON2_DOUBLE_CLICKED:
269                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
270                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
271                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
272                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
273                break;
274            case BUTTON2_TRIPLE_CLICKED:
275                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
276                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
277                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
278                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
279                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
280                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
281                break;
282            case BUTTON2_RESERVED_EVENT:
283                break;
284
285            case BUTTON3_PRESSED:
286                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
287                break;
288            case BUTTON3_RELEASED:
289                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
290                break;
291            case BUTTON3_CLICKED:
292                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
293                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
294                break;
295            case BUTTON3_DOUBLE_CLICKED:
296                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
297                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
298                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
299                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
300                break;
301            case BUTTON3_TRIPLE_CLICKED:
302                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
303                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
304                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
305                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
306                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
307                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
308                break;
309            case BUTTON3_RESERVED_EVENT:
310                break;
311
312            case BUTTON4_PRESSED:
313                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
314                break;
315            case BUTTON4_RELEASED:
316                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
317                break;
318            case BUTTON4_CLICKED:
319                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
320                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
321                break;
322            case BUTTON4_DOUBLE_CLICKED:
323                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
324                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
325                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
326                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
327                break;
328            case BUTTON4_TRIPLE_CLICKED:
329                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
330                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
331                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
332                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
333                _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
334                _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
335                break;
336            case BUTTON4_RESERVED_EVENT:
337                break;
338
339            default:
340                break;
341        }
342
343        if(kk->mouse_x == (unsigned int)mevent.x &&
344           kk->mouse_y == (unsigned int)mevent.y)
345            return _pop_event(kk);
346
347        kk->mouse_x = mevent.x;
348        kk->mouse_y = mevent.y;
349
350        return CACA_EVENT_MOUSE_MOTION | (kk->mouse_x << 12) | kk->mouse_y;
351    }
352
353    event = CACA_EVENT_KEY_PRESS;
354
355    switch(intkey)
356    {
357        case KEY_UP: return event | CACA_KEY_UP;
358        case KEY_DOWN: return event | CACA_KEY_DOWN;
359        case KEY_LEFT: return event | CACA_KEY_LEFT;
360        case KEY_RIGHT: return event | CACA_KEY_RIGHT;
361
362        case KEY_IC: return event | CACA_KEY_INSERT;
363        case KEY_DC: return event | CACA_KEY_DELETE;
364        case KEY_HOME: return event | CACA_KEY_HOME;
365        case KEY_END: return event | CACA_KEY_END;
366        case KEY_PPAGE: return event | CACA_KEY_PAGEUP;
367        case KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
368
369        case KEY_F(1): return event | CACA_KEY_F1;
370        case KEY_F(2): return event | CACA_KEY_F2;
371        case KEY_F(3): return event | CACA_KEY_F3;
372        case KEY_F(4): return event | CACA_KEY_F4;
373        case KEY_F(5): return event | CACA_KEY_F5;
374        case KEY_F(6): return event | CACA_KEY_F6;
375        case KEY_F(7): return event | CACA_KEY_F7;
376        case KEY_F(8): return event | CACA_KEY_F8;
377        case KEY_F(9): return event | CACA_KEY_F9;
378        case KEY_F(10): return event | CACA_KEY_F10;
379        case KEY_F(11): return event | CACA_KEY_F11;
380        case KEY_F(12): return event | CACA_KEY_F12;
381    }
382
383    return CACA_EVENT_NONE;
384}
385
386/*
387 * XXX: following functions are local
388 */
389
390#if defined(HAVE_SIGNAL)
391static RETSIGTYPE sigwinch_handler(int sig)
392{
393    sigwinch_kk->resize_event = 1;
394
395    signal(SIGWINCH, sigwinch_handler);;
396}
397#endif
398
399/*
400 * Driver initialisation
401 */
402
403void ncurses_init_driver(caca_t *kk)
404{
405    kk->driver.driver = CACA_DRIVER_NCURSES;
406
407    kk->driver.init_graphics = ncurses_init_graphics;
408    kk->driver.end_graphics = ncurses_end_graphics;
409    kk->driver.set_window_title = ncurses_set_window_title;
410    kk->driver.get_window_width = ncurses_get_window_width;
411    kk->driver.get_window_height = ncurses_get_window_height;
412    kk->driver.display = ncurses_display;
413    kk->driver.handle_resize = ncurses_handle_resize;
414    kk->driver.get_event = ncurses_get_event;
415}
416
417#endif /* USE_NCURSES */
418
Note: See TracBrowser for help on using the repository browser.