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

Last change on this file since 548 was 548, checked in by Sam Hocevar, 14 years ago
  • Split event.c into the appropriate driver_*.c files.
  • Property svn:keywords set to Id
File size: 12.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 548 2006-03-08 09:28:41Z 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 <stdio.h> /* BUFSIZ */
31#include <string.h>
32#include <stdlib.h>
33#if defined(HAVE_UNISTD_H)
34#   include <unistd.h>
35#endif
36#include <stdarg.h>
37
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
45#include "caca.h"
46#include "caca_internals.h"
47#include "cucul.h"
48#include "cucul_internals.h"
49
50/*
51 * Local functions
52 */
53
54#if defined(HAVE_SIGNAL)
55static RETSIGTYPE sigwinch_handler(int);
56static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */
57#endif
58
59static int ncurses_init_graphics(caca_t *kk)
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    };
82
83    mmask_t newmask;
84    int fg, bg, max;
85
86#if defined(HAVE_SIGNAL)
87    sigwinch_kk = kk;
88    signal(SIGWINCH, sigwinch_handler);
89#endif
90
91    initscr();
92    keypad(stdscr, TRUE);
93    nonl();
94    raw();
95    noecho();
96    nodelay(stdscr, TRUE);
97    curs_set(0);
98
99    /* Activate mouse */
100    newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
101    mousemask(newmask, &kk->ncurses.oldmask);
102    mouseinterval(-1); /* No click emulation */
103
104    /* Set the escape delay to a ridiculously low value */
105    ESCDELAY = 10;
106
107    /* Activate colour */
108    start_color();
109
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;
117
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);
127
128            if(max == 8)
129            {
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
137                                                        | COLOR_PAIR(col);
138            }
139        }
140
141    cucul_set_size(kk->qq, COLS, LINES);
142
143    return 0;
144}
145
146static int ncurses_end_graphics(caca_t *kk)
147{
148    mousemask(kk->ncurses.oldmask, NULL);
149    curs_set(1);
150    noraw();
151    endwin();
152
153    return 0;
154}
155
156static int ncurses_set_window_title(caca_t *kk, char const *title)
157{
158    return 0;
159}
160
161static unsigned int ncurses_get_window_width(caca_t *kk)
162{
163    /* Fallback to a 6x10 font */
164    return kk->qq->width * 6;
165}
166
167static unsigned int ncurses_get_window_height(caca_t *kk)
168{
169    /* Fallback to a 6x10 font */
170    return kk->qq->height * 10;
171}
172
173static void ncurses_display(caca_t *kk)
174{
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++)
179    {
180        move(y, 0);
181        for(x = kk->qq->width; x--; )
182        {
183            attrset(kk->ncurses.attr[*attr++]);
184            addch(*chars++ & 0x7f);
185        }
186    }
187    refresh();
188}
189
190static void ncurses_handle_resize(caca_t *kk, unsigned int *new_width,
191                                              unsigned int *new_height)
192{
193    struct winsize size;
194
195    *new_width = kk->qq->width;
196    *new_height = kk->qq->height;
197
198    if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
199    {
200        *new_width = size.ws_col;
201        *new_height = size.ws_row;
202#if defined(HAVE_RESIZE_TERM)
203        resize_term(*new_height, *new_width);
204#else
205        resizeterm(*new_height, *new_width);
206#endif
207        wrefresh(curscr);
208    }
209}
210
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
394/*
395 * XXX: following functions are local
396 */
397
398#if defined(HAVE_SIGNAL)
399static RETSIGTYPE sigwinch_handler(int sig)
400{
401    sigwinch_kk->resize_event = 1;
402
403    signal(SIGWINCH, sigwinch_handler);;
404}
405#endif
406
407/*
408 * Driver initialisation
409 */
410
411void ncurses_init_driver(caca_t *kk)
412{
413    kk->driver.driver = CACA_DRIVER_NCURSES;
414
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;
422    kk->driver.get_event = ncurses_get_event;
423}
424
425#endif /* USE_NCURSES */
426
Note: See TracBrowser for help on using the repository browser.