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

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