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

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