"Fossies" - the Fresh Open Source Software Archive

Member "libcaca-0.99.beta20/caca/driver/ncurses.c" (19 Oct 2021, 22270 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  *  libcaca     Colour ASCII-Art library
    3  *  Copyright © 2002—2021 Sam Hocevar <sam@hocevar.net>
    4  *              2007 Ben Wiley Sittler <bsittler@gmail.com>
    5  *              All Rights Reserved
    6  *
    7  *  This library is free software. It comes without any warranty, to
    8  *  the extent permitted by applicable law. You can redistribute it
    9  *  and/or modify it under the terms of the Do What the Fuck You Want
   10  *  to Public License, Version 2, as published by Sam Hocevar. See
   11  *  http://www.wtfpl.net/ for more details.
   12  */
   13 
   14 /*
   15  *  This file contains the libcaca Ncurses input and output driver
   16  */
   17 
   18 #include "config.h"
   19 
   20 #if defined USE_NCURSES
   21 
   22 #if defined HAVE_NCURSESW_NCURSES_H
   23 #   include <ncursesw/ncurses.h>
   24 #elif defined HAVE_NCURSES_NCURSES_H
   25 #   include <ncurses/ncurses.h>
   26 #elif defined HAVE_NCURSES_H
   27 #   include <ncurses.h>
   28 #else
   29 #   include <curses.h>
   30 #endif
   31 
   32 #include <stdlib.h>
   33 #include <string.h>
   34 
   35 #if defined HAVE_UNISTD_H
   36 #   include <unistd.h>
   37 #endif
   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 #if defined HAVE_LOCALE_H
   45 #   include <locale.h>
   46 #endif
   47 #if defined HAVE_TERMIOS_H
   48 #   include <termios.h>
   49 #endif
   50 
   51 #include "caca.h"
   52 #include "caca_internals.h"
   53 
   54 /*
   55  * Emulation for missing ACS_* in older curses
   56  */
   57 
   58 #ifndef ACS_BLOCK
   59 #define ACS_BLOCK '#'
   60 #endif
   61 
   62 #ifndef ACS_BOARD
   63 #define ACS_BOARD '#'
   64 #endif
   65 
   66 #ifndef ACS_BTEE
   67 #define ACS_BTEE '+'
   68 #endif
   69 
   70 #ifndef ACS_BULLET
   71 #define ACS_BULLET '.'
   72 #endif
   73 
   74 #ifndef ACS_CKBOARD
   75 #define ACS_CKBOARD ':'
   76 #endif
   77 
   78 #ifndef ACS_DARROW
   79 #define ACS_DARROW 'v'
   80 #endif
   81 
   82 #ifndef ACS_DEGREE
   83 #define ACS_DEGREE '\''
   84 #endif
   85 
   86 #ifndef ACS_DIAMOND
   87 #define ACS_DIAMOND '+'
   88 #endif
   89 
   90 #ifndef ACS_GEQUAL
   91 #define ACS_GEQUAL '>'
   92 #endif
   93 
   94 #ifndef ACS_HLINE
   95 #define ACS_HLINE '-'
   96 #endif
   97 
   98 #ifndef ACS_LANTERN
   99 #define ACS_LANTERN '#'
  100 #endif
  101 
  102 #ifndef ACS_LARROW
  103 #define ACS_LARROW '<'
  104 #endif
  105 
  106 #ifndef ACS_LEQUAL
  107 #define ACS_LEQUAL '<'
  108 #endif
  109 
  110 #ifndef ACS_LLCORNER
  111 #define ACS_LLCORNER '+'
  112 #endif
  113 
  114 #ifndef ACS_LRCORNER
  115 #define ACS_LRCORNER '+'
  116 #endif
  117 
  118 #ifndef ACS_LTEE
  119 #define ACS_LTEE '+'
  120 #endif
  121 
  122 #ifndef ACS_NEQUAL
  123 #define ACS_NEQUAL '!'
  124 #endif
  125 
  126 #ifndef ACS_PI
  127 #define ACS_PI '*'
  128 #endif
  129 
  130 #ifndef ACS_STERLING
  131 #define ACS_STERLING 'f'
  132 #endif
  133 
  134 #ifndef ACS_PLMINUS
  135 #define ACS_PLMINUS '#'
  136 #endif
  137 
  138 #ifndef ACS_PLUS
  139 #define ACS_PLUS '+'
  140 #endif
  141 
  142 #ifndef ACS_RARROW
  143 #define ACS_RARROW '>'
  144 #endif
  145 
  146 #ifndef ACS_RTEE
  147 #define ACS_RTEE '+'
  148 #endif
  149 
  150 #ifndef ACS_S1
  151 #define ACS_S1 '-'
  152 #endif
  153 
  154 #ifndef ACS_S3
  155 #define ACS_S3 '-'
  156 #endif
  157 
  158 #ifndef ACS_S7
  159 #define ACS_S7 '-'
  160 #endif
  161 
  162 #ifndef ACS_S9
  163 #define ACS_S9 '-'
  164 #endif
  165 
  166 #ifndef ACS_TTEE
  167 #define ACS_TTEE '+'
  168 #endif
  169 
  170 #ifndef ACS_UARROW
  171 #define ACS_UARROW '^'
  172 #endif
  173 
  174 #ifndef ACS_ULCORNER
  175 #define ACS_ULCORNER '+'
  176 #endif
  177 
  178 #ifndef ACS_URCORNER
  179 #define ACS_URCORNER '+'
  180 #endif
  181 
  182 #ifndef ACS_VLINE
  183 #define ACS_VLINE '|'
  184 #endif
  185 
  186 /*
  187  * Local functions
  188  */
  189 
  190 #if defined HAVE_SIGNAL
  191 static void sigwinch_handler(int);
  192 static caca_display_t *sigwinch_d; /* FIXME: we ought to get rid of this */
  193 #endif
  194 #if defined HAVE_GETENV && defined HAVE_PUTENV
  195 static void ncurses_install_terminal(caca_display_t *);
  196 static void ncurses_uninstall_terminal(caca_display_t *);
  197 #endif
  198 static void ncurses_write_utf32(uint32_t);
  199 
  200 struct driver_private
  201 {
  202     int attr[16*16];
  203     mmask_t oldmask;
  204     char *term;
  205 };
  206 
  207 static int ncurses_init_graphics(caca_display_t *dp)
  208 {
  209     static int curses_colors[] =
  210     {
  211         /* Standard curses colours */
  212         COLOR_BLACK,
  213         COLOR_BLUE,
  214         COLOR_GREEN,
  215         COLOR_CYAN,
  216         COLOR_RED,
  217         COLOR_MAGENTA,
  218         COLOR_YELLOW,
  219         COLOR_WHITE,
  220         /* Extra values for xterm-16color */
  221         COLOR_BLACK + 8,
  222         COLOR_BLUE + 8,
  223         COLOR_GREEN + 8,
  224         COLOR_CYAN + 8,
  225         COLOR_RED + 8,
  226         COLOR_MAGENTA + 8,
  227         COLOR_YELLOW + 8,
  228         COLOR_WHITE + 8
  229     };
  230 
  231 #if defined HAVE_LOCALE_H
  232     char const *old_locale;
  233 #endif
  234     mmask_t newmask;
  235     int fg, bg, max;
  236 
  237     dp->drv.p = malloc(sizeof(struct driver_private));
  238 
  239 #if defined HAVE_GETENV && defined HAVE_PUTENV
  240     ncurses_install_terminal(dp);
  241 #endif
  242 
  243 #if defined HAVE_SIGNAL
  244     sigwinch_d = dp;
  245     signal(SIGWINCH, sigwinch_handler);
  246 #endif
  247 
  248     _caca_set_term_title("caca for ncurses");
  249 
  250 #if defined HAVE_LOCALE_H
  251     old_locale = setlocale(LC_CTYPE, "");
  252 #endif
  253     initscr();
  254 #if defined HAVE_LOCALE_H
  255     setlocale(LC_CTYPE, old_locale);
  256 #endif
  257 
  258     keypad(stdscr, TRUE);
  259     nonl();
  260     raw();
  261     noecho();
  262     nodelay(stdscr, TRUE);
  263     curs_set(0);
  264 
  265     /* Activate mouse */
  266     newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
  267     mousemask(newmask, &dp->drv.p->oldmask);
  268     mouseinterval(-1); /* No click emulation */
  269 
  270     /* Set the escape delay to a ridiculously low value */
  271 #if defined set_escdelay
  272     set_escdelay(10);
  273 #else
  274     ESCDELAY = 10;
  275 #endif
  276 
  277     /* Activate colour */
  278     start_color();
  279 
  280     /* If COLORS == 16, it means the terminal supports full bright colours
  281      * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
  282      * we can build 16*16 colour pairs.
  283      * If COLORS == 8, it means the terminal does not know about bright
  284      * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
  285      * and \e[5m). We can only build 8*8 colour pairs. */
  286     max = COLORS >= 16 ? 16 : 8;
  287 
  288     for(bg = 0; bg < max; bg++)
  289         for(fg = 0; fg < max; fg++)
  290         {
  291             /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
  292              * is light gray on black. Some terminals don't like this
  293              * colour pair to be redefined. */
  294             int col = ((max + 7 - fg) % max) + max * bg;
  295             init_pair(col, curses_colors[fg], curses_colors[bg]);
  296             dp->drv.p->attr[fg + 16 * bg] = COLOR_PAIR(col);
  297 
  298             if(max == 8)
  299             {
  300                 /* Bright fg on simple bg */
  301                 dp->drv.p->attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
  302                 /* Simple fg on bright bg */
  303                 dp->drv.p->attr[fg + 16 * (bg + 8)] = A_BLINK
  304                                                     | COLOR_PAIR(col);
  305                 /* Bright fg on bright bg */
  306                 dp->drv.p->attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
  307                                                         | COLOR_PAIR(col);
  308             }
  309         }
  310 
  311     caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height);
  312     dp->resize.allow = 1;
  313     caca_set_canvas_size(dp->cv, COLS, LINES);
  314     dp->resize.allow = 0;
  315 
  316     return 0;
  317 }
  318 
  319 static int ncurses_end_graphics(caca_display_t *dp)
  320 {
  321     _caca_set_term_title("");
  322     mousemask(dp->drv.p->oldmask, NULL);
  323     curs_set(1);
  324     noraw();
  325     endwin();
  326 
  327 #if defined HAVE_GETENV && defined HAVE_PUTENV
  328     ncurses_uninstall_terminal(dp);
  329 #endif
  330 
  331     free(dp->drv.p);
  332 
  333     return 0;
  334 }
  335 
  336 static int ncurses_set_display_title(caca_display_t *dp, char const *title)
  337 {
  338     _caca_set_term_title(title);
  339 
  340     return 0;
  341 }
  342 
  343 static int ncurses_get_display_width(caca_display_t const *dp)
  344 {
  345     /* Fallback to a 6x10 font */
  346     return caca_get_canvas_width(dp->cv) * 6;
  347 }
  348 
  349 static int ncurses_get_display_height(caca_display_t const *dp)
  350 {
  351     /* Fallback to a 6x10 font */
  352     return caca_get_canvas_height(dp->cv) * 10;
  353 }
  354 
  355 static void ncurses_display(caca_display_t *dp)
  356 {
  357     int x, y, i;
  358 
  359     for(i = 0; i < caca_get_dirty_rect_count(dp->cv); i++)
  360     {
  361         uint32_t const *cvchars, *cvattrs;
  362         int dx, dy, dw, dh;
  363 
  364         caca_get_dirty_rect(dp->cv, i, &dx, &dy, &dw, &dh);
  365 
  366         cvchars = caca_get_canvas_chars(dp->cv) + dx + dy * dp->cv->width;
  367         cvattrs = caca_get_canvas_attrs(dp->cv) + dx + dy * dp->cv->width;
  368 
  369         for(y = dy; y < dy + dh; y++)
  370         {
  371             move(y, dx);
  372             for(x = dx; x < dx + dw; x++)
  373             {
  374                 uint32_t attr = *cvattrs++;
  375 
  376                 (void)attrset(dp->drv.p->attr[caca_attr_to_ansi(attr)]);
  377                 if(attr & CACA_BOLD)
  378                     attron(A_BOLD);
  379                 if(attr & CACA_BLINK)
  380                     attron(A_BLINK);
  381                 if(attr & CACA_UNDERLINE)
  382                     attron(A_UNDERLINE);
  383 
  384                 ncurses_write_utf32(*cvchars++);
  385             }
  386 
  387             cvchars += dp->cv->width - dw;
  388             cvattrs += dp->cv->width - dw;
  389         }
  390     }
  391 
  392     x = caca_wherex(dp->cv);
  393     y = caca_wherey(dp->cv);
  394     move(y, x);
  395 
  396     refresh();
  397 }
  398 
  399 static void ncurses_handle_resize(caca_display_t *dp)
  400 {
  401     struct winsize size;
  402 
  403 #if defined HAVE_SYS_IOCTL_H
  404     if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
  405     {
  406         dp->resize.w = size.ws_col;
  407         dp->resize.h = size.ws_row;
  408 #if defined HAVE_RESIZE_TERM
  409         resize_term(dp->resize.h, dp->resize.w);
  410 #else
  411         resizeterm(dp->resize.h, dp->resize.w);
  412 #endif
  413         wrefresh(curscr);
  414         return;
  415     }
  416 #endif
  417 
  418     /* Fallback */
  419     dp->resize.w = caca_get_canvas_width(dp->cv);
  420     dp->resize.h = caca_get_canvas_height(dp->cv);
  421 }
  422 
  423 static int ncurses_get_event(caca_display_t *dp, caca_privevent_t *ev)
  424 {
  425     int intkey;
  426 
  427     intkey = getch();
  428     if(intkey == ERR)
  429     {
  430         ev->type = CACA_EVENT_NONE;
  431         return 0;
  432     }
  433 
  434     if(intkey < 0x7f)
  435     {
  436         ev->type = CACA_EVENT_KEY_PRESS;
  437         ev->data.key.ch = intkey;
  438         ev->data.key.utf32 = intkey;
  439         ev->data.key.utf8[0] = intkey;
  440         ev->data.key.utf8[1] = '\0';
  441         return 1;
  442     }
  443 
  444     /* If the key was UTF-8, parse the whole sequence */
  445     if(intkey >= 0x80 && intkey < 0x100)
  446     {
  447         int keys[7]; /* Necessary for ungetch(); */
  448         char utf8[7];
  449         uint32_t utf32;
  450         size_t i, bytes = 0;
  451 
  452         keys[0] = intkey;
  453         utf8[0] = intkey;
  454 
  455         for(i = 1; i < 6; i++)
  456         {
  457             keys[i] = getch();
  458             utf8[i] = (unsigned char)keys[i];
  459         }
  460 
  461         utf8[i] = '\0';
  462         utf32 = caca_utf8_to_utf32(utf8, &bytes);
  463 
  464         while(i > bytes)
  465             ungetch(keys[--i]);
  466 
  467         if(bytes)
  468         {
  469             ev->type = CACA_EVENT_KEY_PRESS;
  470             ev->data.key.ch = 0;
  471             ev->data.key.utf32 = utf32;
  472             strcpy(ev->data.key.utf8, utf8);
  473             return 1;
  474         }
  475     }
  476 
  477     if(intkey == KEY_MOUSE)
  478     {
  479         MEVENT mevent;
  480         getmouse(&mevent);
  481 
  482         switch(mevent.bstate)
  483         {
  484 #define PRESS(x) ev->data.mouse.button = x; \
  485                  ev->type = CACA_EVENT_MOUSE_PRESS; _push_event(dp, ev)
  486 #define RELEASE(x) ev->data.mouse.button = x; \
  487                    ev->type = CACA_EVENT_MOUSE_RELEASE; _push_event(dp, ev)
  488 #define CLICK(x) PRESS(x); RELEASE(x)
  489             case BUTTON1_PRESSED: PRESS(1); break;
  490             case BUTTON1_RELEASED: RELEASE(1); break;
  491             case BUTTON1_CLICKED: CLICK(1); break;
  492             case BUTTON1_DOUBLE_CLICKED: CLICK(1); CLICK(1); break;
  493             case BUTTON1_TRIPLE_CLICKED: CLICK(1); CLICK(1); CLICK(1); break;
  494 #if defined BUTTON1_RESERVED_EVENT
  495             case BUTTON1_RESERVED_EVENT: break;
  496 #endif
  497 
  498             case BUTTON2_PRESSED: PRESS(2); break;
  499             case BUTTON2_RELEASED: RELEASE(2); break;
  500             case BUTTON2_CLICKED: CLICK(2); break;
  501             case BUTTON2_DOUBLE_CLICKED: CLICK(2); CLICK(2); break;
  502             case BUTTON2_TRIPLE_CLICKED: CLICK(2); CLICK(2); CLICK(2); break;
  503 #if defined BUTTON2_RESERVED_EVENT
  504             case BUTTON2_RESERVED_EVENT: break;
  505 #endif
  506 
  507             case BUTTON3_PRESSED: PRESS(3); break;
  508             case BUTTON3_RELEASED: RELEASE(3); break;
  509             case BUTTON3_CLICKED: CLICK(3); break;
  510             case BUTTON3_DOUBLE_CLICKED: CLICK(3); CLICK(3); break;
  511             case BUTTON3_TRIPLE_CLICKED: CLICK(3); CLICK(3); CLICK(3); break;
  512 #if defined BUTTON3_RESERVED_EVENT
  513             case BUTTON3_RESERVED_EVENT: break;
  514 #endif
  515 
  516             case BUTTON4_PRESSED: PRESS(4); break;
  517             case BUTTON4_RELEASED: RELEASE(4); break;
  518             case BUTTON4_CLICKED: CLICK(4); break;
  519             case BUTTON4_DOUBLE_CLICKED: CLICK(4); CLICK(4); break;
  520             case BUTTON4_TRIPLE_CLICKED: CLICK(4); CLICK(4); CLICK(4); break;
  521 #if defined BUTTON4_RESERVED_EVENT
  522             case BUTTON4_RESERVED_EVENT: break;
  523 #endif
  524 
  525             default:
  526                 break;
  527 #undef PRESS
  528 #undef RELEASE
  529 #undef CLICK
  530         }
  531 
  532         if(dp->mouse.x == mevent.x && dp->mouse.y == mevent.y)
  533             return _pop_event(dp, ev);
  534 
  535         dp->mouse.x = mevent.x;
  536         dp->mouse.y = mevent.y;
  537 
  538         ev->type = CACA_EVENT_MOUSE_MOTION;
  539         ev->data.mouse.x = dp->mouse.x;
  540         ev->data.mouse.y = dp->mouse.y;
  541         return 1;
  542     }
  543 
  544     switch(intkey)
  545     {
  546         case KEY_UP: ev->data.key.ch = CACA_KEY_UP; break;
  547         case KEY_DOWN: ev->data.key.ch = CACA_KEY_DOWN; break;
  548         case KEY_LEFT: ev->data.key.ch = CACA_KEY_LEFT; break;
  549         case KEY_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
  550 
  551         case KEY_IC: ev->data.key.ch = CACA_KEY_INSERT; break;
  552         case KEY_DC: ev->data.key.ch = CACA_KEY_DELETE; break;
  553         case 0x7f:
  554         case KEY_BACKSPACE: ev->data.key.ch = CACA_KEY_BACKSPACE; break;
  555         case KEY_HOME: ev->data.key.ch = CACA_KEY_HOME; break;
  556         case KEY_END: ev->data.key.ch = CACA_KEY_END; break;
  557         case KEY_PPAGE: ev->data.key.ch = CACA_KEY_PAGEUP; break;
  558         case KEY_NPAGE: ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
  559 
  560         case KEY_F(1): ev->data.key.ch = CACA_KEY_F1; break;
  561         case KEY_F(2): ev->data.key.ch = CACA_KEY_F2; break;
  562         case KEY_F(3): ev->data.key.ch = CACA_KEY_F3; break;
  563         case KEY_F(4): ev->data.key.ch = CACA_KEY_F4; break;
  564         case KEY_F(5): ev->data.key.ch = CACA_KEY_F5; break;
  565         case KEY_F(6): ev->data.key.ch = CACA_KEY_F6; break;
  566         case KEY_F(7): ev->data.key.ch = CACA_KEY_F7; break;
  567         case KEY_F(8): ev->data.key.ch = CACA_KEY_F8; break;
  568         case KEY_F(9): ev->data.key.ch = CACA_KEY_F9; break;
  569         case KEY_F(10): ev->data.key.ch = CACA_KEY_F10; break;
  570         case KEY_F(11): ev->data.key.ch = CACA_KEY_F11; break;
  571         case KEY_F(12): ev->data.key.ch = CACA_KEY_F12; break;
  572 
  573         default:
  574             /* Unknown key */
  575             ev->type = CACA_EVENT_NONE; return 0;
  576     }
  577 
  578     ev->type = CACA_EVENT_KEY_PRESS;
  579     ev->data.key.utf32 = 0;
  580     ev->data.key.utf8[0] = '\0';
  581     return 1;
  582 }
  583 
  584 static void ncurses_set_cursor(caca_display_t *dp, int flags)
  585 {
  586     if (!flags)
  587         curs_set(0);
  588     else if (curs_set(2) == ERR)
  589         curs_set(1);
  590 }
  591 
  592 /*
  593  * XXX: following functions are local
  594  */
  595 
  596 #if defined HAVE_SIGNAL
  597 static void sigwinch_handler(int sig)
  598 {
  599     sigwinch_d->resize.resized = 1;
  600 
  601     signal(SIGWINCH, sigwinch_handler);
  602 }
  603 #endif
  604 
  605 #if defined HAVE_GETENV && defined HAVE_PUTENV
  606 static void ncurses_install_terminal(caca_display_t *dp)
  607 {
  608     char *term, *colorterm;
  609 
  610     dp->drv.p->term = NULL;
  611 
  612     term = getenv("TERM");
  613     colorterm = getenv("COLORTERM");
  614 
  615     if(!term || strcmp(term, "xterm"))
  616         return;
  617 
  618     /* If we are using gnome-terminal, it's really a 16 colour terminal.
  619      * Ditto if we are using xfce4-terminal, or Konsole. */
  620     if((colorterm && (!strcmp(colorterm, "gnome-terminal")
  621                        || !strcmp(colorterm, "Terminal")))
  622          || getenv("KONSOLE_DCOP_SESSION"))
  623     {
  624         SCREEN *screen;
  625         screen = newterm("xterm-16color", stdout, stdin);
  626         if(screen == NULL)
  627             return;
  628         endwin();
  629         (void)putenv("TERM=xterm-16color");
  630         dp->drv.p->term = strdup(term);
  631         return;
  632     }
  633 }
  634 
  635 static void ncurses_uninstall_terminal(caca_display_t *dp)
  636 {
  637     /* Needs to be persistent because we use putenv() */
  638     static char termenv[1024];
  639 
  640     if(!dp->drv.p->term)
  641         return;
  642 
  643     snprintf(termenv, 1023, "TERM=%s", dp->drv.p->term);
  644     free(dp->drv.p->term);
  645     (void)putenv(termenv);
  646 }
  647 #endif
  648 
  649 static void ncurses_write_utf32(uint32_t ch)
  650 {
  651 #if defined HAVE_NCURSESW_NCURSES_H
  652     char buf[10];
  653     int bytes;
  654 #endif
  655 
  656     if(ch == CACA_MAGIC_FULLWIDTH)
  657         return;
  658 
  659 #if defined HAVE_NCURSESW_NCURSES_H
  660     bytes = caca_utf32_to_utf8(buf, ch);
  661     buf[bytes] = '\0';
  662     addstr(buf);
  663 #else
  664     if(ch < 0x80)
  665     {
  666         addch(ch);
  667     }
  668     else
  669     {
  670         chtype cch;
  671         chtype cch2;
  672 
  673         cch = '?';
  674         cch2 = ' ';
  675         if ((ch > 0x0000ff00) && (ch < 0x0000ff5f))
  676         {
  677             cch = ch - 0x0000ff00 + ' ';
  678         }
  679         switch (ch)
  680         {
  681         case 0x000000a0: /* <nbsp> */
  682         case 0x00003000: /*   */
  683             cch = ' ';
  684             break;
  685         case 0x000000a3: /* £ */
  686             cch = ACS_STERLING;
  687             break;
  688         case 0x000000b0: /* ° */
  689             cch = ACS_DEGREE;
  690             break;
  691         case 0x000000b1: /* ± */
  692             cch = ACS_PLMINUS;
  693             break;
  694         case 0x000000b7: /* · */
  695         case 0x00002219: /* ∙ */
  696         case 0x000030fb: /* ・ */
  697             cch = ACS_BULLET;
  698             break;
  699         case 0x000003c0: /* π */
  700             cch = ACS_PI;
  701             break;
  702         case 0x00002018: /* ‘ */
  703         case 0x00002019: /* ’ */
  704             cch = '\'';
  705             break;
  706         case 0x0000201c: /* “ */
  707         case 0x0000201d: /* ” */
  708             cch = '"';
  709             break;
  710         case 0x00002190: /* ← */
  711             cch = ACS_LARROW;
  712             break;
  713         case 0x00002191: /* ↑ */
  714             cch = ACS_UARROW;
  715             break;
  716         case 0x00002192: /* → */
  717             cch = ACS_RARROW;
  718             break;
  719         case 0x00002193: /* ↓ */
  720             cch = ACS_DARROW;
  721             break;
  722         case 0x00002260: /* ≠ */
  723             cch = ACS_NEQUAL;
  724             break;
  725         case 0x00002261: /* ≡ */
  726             cch = '=';
  727             break;
  728         case 0x00002264: /* ≤ */
  729             cch = ACS_LEQUAL;
  730             break;
  731         case 0x00002265: /* ≥ */
  732             cch = ACS_GEQUAL;
  733             break;
  734         case 0x000023ba: /* ⎺ */
  735             cch = ACS_S1;
  736             cch2 = cch;
  737             break;
  738         case 0x000023bb: /* ⎻ */
  739             cch = ACS_S3;
  740             cch2 = cch;
  741             break;
  742         case 0x000023bc: /* ⎼ */
  743             cch = ACS_S7;
  744             cch2 = cch;
  745             break;
  746         case 0x000023bd: /* ⎽ */
  747             cch = ACS_S9;
  748             cch2 = cch;
  749             break;
  750         case 0x00002500: /* ─ */
  751         case 0x00002550: /* ═ */
  752             cch = ACS_HLINE;
  753             cch2 = cch;
  754             break;
  755         case 0x00002502: /* │ */
  756         case 0x00002551: /* ║ */
  757             cch = ACS_VLINE;
  758             break;
  759         case 0x0000250c: /* ┌ */
  760         case 0x00002552: /* ╒ */
  761         case 0x00002553: /* ╓ */
  762         case 0x00002554: /* ╔ */
  763             cch = ACS_ULCORNER;
  764             cch2 = ACS_HLINE;
  765             break;
  766         case 0x00002510: /* ┐ */
  767         case 0x00002555: /* ╕ */
  768         case 0x00002556: /* ╖ */
  769         case 0x00002557: /* ╗ */
  770             cch = ACS_URCORNER;
  771             break;
  772         case 0x00002514: /* └ */
  773         case 0x00002558: /* ╘ */
  774         case 0x00002559: /* ╙ */
  775         case 0x0000255a: /* ╚ */
  776             cch = ACS_LLCORNER;
  777             cch2 = ACS_HLINE;
  778             break;
  779         case 0x00002518: /* ┘ */
  780         case 0x0000255b: /* ╛ */
  781         case 0x0000255c: /* ╜ */
  782         case 0x0000255d: /* ╝ */
  783             cch = ACS_LRCORNER;
  784             break;
  785         case 0x0000251c: /* ├ */
  786         case 0x0000255e: /* ╞ */
  787         case 0x0000255f: /* ╟ */
  788         case 0x00002560: /* ╠ */
  789             cch = ACS_LTEE;
  790             cch2 = ACS_HLINE;
  791             break;
  792         case 0x00002524: /* ┤ */
  793         case 0x00002561: /* ╡ */
  794         case 0x00002562: /* ╢ */
  795         case 0x00002563: /* ╣ */
  796             cch = ACS_RTEE;
  797             break;
  798         case 0x0000252c: /* ┬ */
  799         case 0x00002564: /* ╤ */
  800         case 0x00002565: /* ╥ */
  801         case 0x00002566: /* ╦ */
  802             cch = ACS_TTEE;
  803             cch2 = ACS_HLINE;
  804             break;
  805         case 0x00002534: /* ┴ */
  806         case 0x00002567: /* ╧ */
  807         case 0x00002568: /* ╨ */
  808         case 0x00002569: /* ╩ */
  809             cch = ACS_BTEE;
  810             cch2 = ACS_HLINE;
  811             break;
  812         case 0x0000253c: /* ┼ */
  813         case 0x0000256a: /* ╪ */
  814         case 0x0000256b: /* ╫ */
  815         case 0x0000256c: /* ╬ */
  816             cch = ACS_PLUS;
  817             cch2 = ACS_HLINE;
  818             break;
  819         case 0x00002591: /* ░ */
  820             cch = ACS_BOARD;
  821             cch2 = cch;
  822             break;
  823         case 0x00002592: /* ▒ */
  824         case 0x00002593: /* ▓ */
  825             cch = ACS_CKBOARD;
  826             cch2 = cch;
  827             break;
  828         case 0x00002580: /* ▀ */
  829         case 0x00002584: /* ▄ */
  830         case 0x00002588: /* █ */
  831         case 0x0000258c: /* ▌ */
  832         case 0x00002590: /* ▐ */
  833         case 0x000025a0: /* ■ */
  834         case 0x000025ac: /* ▬ */
  835         case 0x000025ae: /* ▮ */
  836             cch = ACS_BLOCK;
  837             cch2 = cch;
  838             break;
  839         case 0x000025c6: /* ◆ */
  840         case 0x00002666: /* ♦ */
  841             cch = ACS_DIAMOND;
  842             break;
  843         case 0x00002022: /* • */
  844         case 0x000025cb: /* ○ */
  845         case 0x000025cf: /* ● */
  846         case 0x00002603: /* ☃ */
  847         case 0x0000263c: /* ☼ */
  848             cch = ACS_LANTERN;
  849             break;
  850         case 0x0000301c: /* 〜 */
  851             cch = '~';
  852             break;
  853         }
  854         addch(cch);
  855         if(caca_utf32_is_fullwidth(ch))
  856         {
  857             addch(cch2);
  858         }
  859     }
  860 #endif
  861 }
  862 
  863 /*
  864  * Driver initialisation
  865  */
  866 
  867 int ncurses_install(caca_display_t *dp)
  868 {
  869     dp->drv.id = CACA_DRIVER_NCURSES;
  870     dp->drv.driver = "ncurses";
  871 
  872     dp->drv.init_graphics = ncurses_init_graphics;
  873     dp->drv.end_graphics = ncurses_end_graphics;
  874     dp->drv.set_display_title = ncurses_set_display_title;
  875     dp->drv.get_display_width = ncurses_get_display_width;
  876     dp->drv.get_display_height = ncurses_get_display_height;
  877     dp->drv.display = ncurses_display;
  878     dp->drv.handle_resize = ncurses_handle_resize;
  879     dp->drv.get_event = ncurses_get_event;
  880     dp->drv.set_mouse = NULL;
  881     dp->drv.set_cursor = ncurses_set_cursor;
  882 
  883     return 0;
  884 }
  885 
  886 #endif /* USE_NCURSES */
  887