[BACK]Return to term.c CVS log [TXT][DIR] Up to [contributed] / brogue-ce / src / platform

Annotation of brogue-ce/src/platform/term.c, Revision 1.1.1.1

1.1       rubenllo    1: #include <ncurses.h>
                      2: #include "term.h"
                      3: #include <math.h>
                      4: #include <stdlib.h>
                      5: #include <string.h>
                      6:
                      7:
                      8: // As a rule, everything in term.c is the result of gradual evolutionary
                      9: // change.  It's messy.
                     10:
                     11: #define COLORING(fg,bg) (((fg) & 0x0f) | (((bg) & 0x07) << 4))
                     12: #define COLOR_FG(color,fg) (((fg) & 0x0f) + ((color) & 0x70))
                     13: #define COLOR_BG(color,bg) (((color) & 0x0f) + (((bg) & 0x07) << 4))
                     14: #define COLOR_INDEX(color) (1 + ((color)&0x07) + (((color) >> 1) & 0x38))
                     15: #define COLOR_ATTR(color) (COLOR_PAIR(COLOR_INDEX(color)) | (((color)&0x08) ? A_BOLD : 0))
                     16:
                     17:
                     18: static struct { int curses, color; } videomode = { 0, 0 };
                     19:
                     20: static struct { int width, height; } minsize = { 80, 24 };
                     21:
                     22: static void init_coersion();
                     23:
                     24:
                     25: // 256 color mode stuff
                     26: static void initialize_prs();
                     27:
                     28: typedef struct {
                     29:     int r, g, b, idx;
                     30: } intcolor;
                     31:
                     32: struct {
                     33:     intcolor fore, back;
                     34:     int count, next;
                     35: } prs[256];
                     36:
                     37:
                     38: typedef struct {
                     39:     int ch, pair, shuffle;
                     40:     intcolor fore, back;
                     41: } pairmode_cell;
                     42:
                     43: pairmode_cell *cell_buffer;
                     44:
                     45: enum {
                     46:     coerce_16,
                     47:     coerce_256
                     48: } colormode;
                     49:
                     50: int is_xterm;
                     51:
                     52:
                     53: //
                     54:
                     55: static void preparecolor ( ) {
                     56:     // sixteen color mode colors (we use these in 256-color mode, too)
                     57:     static int pairParts[8] = {
                     58:         COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
                     59:         COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
                     60:     };
                     61:
                     62:     int fg, bg;
                     63:     for (bg=0; bg<8; bg++) {
                     64:         for (fg=0; fg<8; fg++) {
                     65:             init_pair(
                     66:                 COLOR_INDEX(COLORING(fg, bg)),
                     67:                 pairParts[fg], pairParts[bg]
                     68:             );
                     69:         }
                     70:     }
                     71:
                     72:     if (COLORS >= 256) {
                     73:         colormode = coerce_256;
                     74:     }
                     75: }
                     76:
                     77: static void term_title(const char *title) {
                     78:     if (is_xterm) {
                     79:         printf ("\033]2;%s\007", title); // ESC ]0; title BEL
                     80:     }
                     81: }
                     82:
                     83: static void term_title_pop() {
                     84:     if (is_xterm) {
                     85:         term_title("Terminal");
                     86:         printf ("\033[22;2t");
                     87:     }
                     88: }
                     89: static void term_title_push() {
                     90:     if (is_xterm) {
                     91:         printf ("\033[23;2t");
                     92:     }
                     93: }
                     94:
                     95: static void term_set_size(int h, int w) {
                     96:     // works in gnome-terminal, but not xterm; causes trouble for maximized windows
                     97:     if (is_xterm) {
                     98:         // first, try resizing the height, in case only that is supported
                     99:         printf ("\033[%dt", (h > 24 ? h : 24));
                    100:
                    101:         // then try resizing both, in case we can
                    102:         printf ("\033[8;%d;%dt", h, w);
                    103:
                    104:         // then refresh so ncurses knows about it
                    105:         refresh( );
                    106:     }
                    107: }
                    108:
                    109: static void term_show_scrollbar(int show) {
                    110:     // works in xterm, but not gnome-terminal
                    111:     if (is_xterm) {
                    112:         if (show) {
                    113:             printf ("\033[?30h");
                    114:         } else {
                    115:             printf ("\033[?30l");
                    116:         }
                    117:     }
                    118: }
                    119:
                    120: static int curses_init( ) {
                    121:     if (videomode.curses) return 0;
                    122:
                    123:     // isterm?
                    124:     initscr( );
                    125:     if (!has_colors( )) {
                    126:         endwin( );
                    127:         fprintf (stderr, "Your terminal has no color support.\n");
                    128:         return 1;
                    129:     }
                    130:
                    131:     start_color( );
                    132:     clear( );
                    133:     curs_set( 0 );
                    134:     refresh( );
                    135:     leaveok(stdscr, TRUE);
                    136:     preparecolor( );
                    137:     cbreak( );
                    138:     noecho( );
                    139:
                    140:     nodelay(stdscr, TRUE);
                    141:     meta(stdscr, TRUE);
                    142:     keypad(stdscr, TRUE);
                    143:
                    144:     mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED | REPORT_MOUSE_POSITION | BUTTON_SHIFT | BUTTON_CTRL, NULL);
                    145:     mouseinterval(0); //do no click processing, thank you
                    146:
                    147:     videomode.curses = 1;
                    148:
                    149:     getmaxyx(stdscr, Term.height, Term.width);
                    150:
                    151:     return 1;
                    152: }
                    153:
                    154:
                    155: static int term_start() {
                    156:     char *term = getenv("TERM");
                    157:     is_xterm = (strncmp(term, "xterm", 5) == 0) || (strncmp(term, "gnome", 5) == 0) || (strncmp(term, "st", 2) == 0);
                    158:
                    159:     term_title_push();
                    160:     term_show_scrollbar(0);
                    161:
                    162:     int ok = curses_init();
                    163:     init_coersion();
                    164:
                    165:     return ok;
                    166: }
                    167:
                    168: static void term_end() {
                    169:     term_title_pop();
                    170:     clear();
                    171:     refresh();
                    172:     endwin();
                    173: }
                    174:
                    175: typedef struct CIE {
                    176:     float X, Y, Z;
                    177:     float x, y, z;
                    178: } CIE;
                    179:
                    180: typedef struct Lab {
                    181:     float L, a, b;
                    182: } Lab;
                    183:
                    184: #define DARK 0.0
                    185: #define DIM 0.1
                    186: #define MID 0.3
                    187: #define HALFBRIGHT 0.5
                    188: #define BRIGHT 0.9
                    189:
                    190: fcolor palette[16] = {
                    191:     {DARK, DARK, DARK},
                    192:     {MID, DARK, DARK},
                    193:     {DARK, MID, DARK},
                    194:     {MID, .8 * MID, DIM},
                    195:     {DARK, DARK, MID},
                    196:     {MID + DIM, DARK, MID},
                    197:     {DARK, MID, MID},
                    198:     {HALFBRIGHT, HALFBRIGHT, HALFBRIGHT},
                    199:
                    200:     {MID, MID, MID},
                    201:     {BRIGHT, DARK, DARK},
                    202:     {DARK, BRIGHT, DARK},
                    203:     {BRIGHT, BRIGHT, DARK},
                    204:     {HALFBRIGHT, MID, BRIGHT},
                    205:     {BRIGHT, HALFBRIGHT, BRIGHT},
                    206:     {DARK, BRIGHT, BRIGHT},
                    207:     {BRIGHT, BRIGHT, BRIGHT}
                    208: };
                    209:
                    210: CIE ciePalette[16];
                    211: Lab labPalette[16];
                    212: CIE adamsPalette[16];
                    213:
                    214: static CIE white;
                    215:
                    216: static CIE toCIE(fcolor c) {
                    217:     double a = 0.055;
                    218:
                    219:     // http://en.wikipedia.org/wiki/SRGB_color_space#The_reverse_transformation
                    220:
                    221:     c.r = c.r <= 0.04045 ? c.r / 12.92 : pow((c.r + a) / (1 + a), 2.4);
                    222:     c.g = c.g <= 0.04045 ? c.g / 12.92 : pow((c.g + a) / (1 + a), 2.4);
                    223:     c.b = c.b <= 0.04045 ? c.b / 12.92 : pow((c.b + a) / (1 + a), 2.4);
                    224:
                    225:     CIE cie;
                    226:     cie.X = 0.4124 * c.r + 0.3576 * c.g + 0.1805 * c.b;
                    227:     cie.Y = 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
                    228:     cie.Z = 0.0193 * c.r + 0.1192 * c.g + 0.9505 * c.b;
                    229:
                    230:     float sum = cie.X + cie.Y + cie.Z;
                    231:     if (sum == 0.0) sum = 1.0;
                    232:     cie.x = cie.X / sum;
                    233:     cie.y = cie.Y / sum;
                    234:     cie.z = 1.0 - cie.x - cie.y;
                    235:
                    236:     return cie;
                    237: }
                    238:
                    239: static float Labf(float t) {
                    240:     return t > ((6.0/29.0) * (6.0/29.0) * (6.0/29.0)) ? pow(t, 1.0/3.0) : ((1.0/3.0) * (29.0 / 6.0) * (29.0 / 6.0)) * t + (4.0 / 29.0);
                    241: }
                    242:
                    243: static Lab toLab(CIE *c) {
                    244:     CIE n = (CIE) {Labf(c->X / white.X), Labf(c->Y / white.Y), Labf(c->Z / white.Z)};
                    245:     Lab l;
                    246:
                    247:     // http://en.wikipedia.org/wiki/L*a*b*#RGB_and_CMYK_conversions
                    248:     l.L = 116.0 * n.Y - 16;
                    249:     l.a = 500.0 * (n.X - n.Y);
                    250:     l.b = 200.0 * (n.Y - n.Z);
                    251:
                    252:     return l;
                    253: }
                    254:
                    255: static float munsellSloanGodlove(float t) {
                    256:     return sqrt(1.4742 * t - 0.004743 * t * t);
                    257: }
                    258:
                    259: static CIE adams(CIE *v) {
                    260:     CIE c;
                    261:     c.Y = munsellSloanGodlove(v->Y);
                    262:     c.X = munsellSloanGodlove((white.Y / white.X) * v->X) - c.Y;
                    263:     c.Z = munsellSloanGodlove((white.Z / white.X) * v->Z) - c.Y;
                    264:
                    265:     return c;
                    266: }
                    267:
                    268: #define SQUARE(x) ((x) * (x))
                    269:
                    270: static float CIE76(Lab *L1, Lab *L2) {
                    271:     // http://en.wikipedia.org/wiki/Color_difference#CIE76
                    272:     float lbias = 1.0;
                    273:     return sqrt(lbias * SQUARE(L2->L - L1->L) + SQUARE(L2->a - L1->a) + SQUARE(L2->b - L1->b));
                    274: }
                    275:
                    276: static void init_coersion() {
                    277:     fcolor sRGB_white = (fcolor) {1, 1, 1};
                    278:     white = toCIE(sRGB_white);
                    279:
                    280:     int i;
                    281:     for (i = 0; i < 16; i++) {
                    282:         ciePalette[i] = toCIE(palette[i]);
                    283:         labPalette[i] = toLab(&ciePalette[i]);
                    284:         adamsPalette[i] = adams(&ciePalette[i]);
                    285:     }
                    286:
                    287:     if (colormode == coerce_256) {
                    288:         initialize_prs();
                    289:     }
                    290:
                    291:     cell_buffer = 0;
                    292: }
                    293:
                    294: static int best (fcolor *fg, fcolor *bg) {
                    295:     // analyze fg & bg for their contrast
                    296:     CIE cieFg = toCIE(*fg);
                    297:     CIE cieBg = toCIE(*bg);
                    298:     Lab labFg = toLab(&cieFg);
                    299:     Lab labBg = toLab(&cieBg);
                    300:     // CIE adamsFg = adams(&cieFg);
                    301:     // CIE adamsBg = adams(&cieBg);
                    302:
                    303:     float JND = 2.3; // just-noticeable-difference
                    304:     int areTheSame = CIE76(&labFg, &labBg) <= 2.0 * JND; // a little extra fudge
                    305:
                    306:     float big = 100000000;
                    307:     int fg1 = 0, fg2 = 0, bg1 = 0, bg2 = 0;
                    308:     float fg1_score = big, fg2_score = big;
                    309:     float bg1_score = big, bg2_score = big;
                    310:
                    311:     int i;
                    312:
                    313:     for (i = 0; i < 8; i++) {
                    314:         float s = CIE76(labPalette + i, &labBg);
                    315:
                    316:         if (s < bg2_score) {
                    317:             if (s < bg1_score) {
                    318:                 bg2 = bg1; bg1 = i;
                    319:                 bg2_score = bg1_score; bg1_score = s;
                    320:             } else {
                    321:                 bg2 = i; bg2_score = s;
                    322:             }
                    323:         }
                    324:     }
                    325:
                    326:     if (areTheSame) {
                    327:         return COLORING(bg1, bg1);
                    328:     }
                    329:
                    330:     for (i = 0; i < 16; i++) {
                    331:         float s = CIE76(labPalette + i, &labFg);
                    332:
                    333:         if (s < fg2_score) {
                    334:             if (s < fg1_score) {
                    335:                 fg2 = fg1; fg1 = i;
                    336:                 fg2_score = fg1_score; fg1_score = s;
                    337:             } else {
                    338:                 fg2 = i; fg2_score = s;
                    339:             }
                    340:         }
                    341:     }
                    342:
                    343:     if (fg1 != bg1) {
                    344:         return COLORING (fg1, bg1);
                    345:     } else {
                    346:         if (fg1_score + bg2_score < fg2_score + bg1_score) {
                    347:             return COLORING(fg1, bg2);
                    348:         } else {
                    349:             return COLORING(fg2, bg1);
                    350:         }
                    351:     }
                    352: }
                    353:
                    354:
                    355:
                    356:
                    357: static void initialize_prs() {
                    358:     int i;
                    359:     for (i = 16; i < 255; i++) {
                    360:         prs[i].next = i + 1;
                    361:     }
                    362:     prs[0].next = 16;
                    363:     prs[1].next = 0;
                    364:     prs[255].next = 0;
                    365: }
                    366:
                    367: static void coerce_colorcube (fcolor *f, intcolor *c) {
                    368:     // 0-15 are the standard ANSI colors
                    369:     // 16-231 are a 6x6x6 RGB color cube given by ((36 * r) + (6 * g) + b + 16) with r,g,b in [0..5]
                    370:     // 232-255 are a greyscale ramp without black and white.
                    371:
                    372:     float sat = 0.2, bright = 0.6, contrast = 6.3;
                    373:
                    374:     float rf = bright + f->r * contrast,
                    375:         gf = bright + f->g * contrast,
                    376:         bf = bright + f->b * contrast;
                    377:
                    378:     if (rf < gf && rf < bf) rf -= sat * ((gf < bf ? bf : gf) - rf);
                    379:     else if (gf < bf && gf < rf) gf -= sat * ((rf < bf ? bf : rf) - gf);
                    380:     else if (bf < gf && bf < rf) bf -= sat * ((gf < rf ? rf : gf) - bf);
                    381:
                    382:     int r = rf, g = gf, b = bf;
                    383:     r = r < 0 ? 0 : r > 5 ? 5 : r;
                    384:     g = g < 0 ? 0 : g > 5 ? 5 : g;
                    385:     b = b < 0 ? 0 : b > 5 ? 5 : b;
                    386:
                    387:     c->r = r;
                    388:     c->g = g;
                    389:     c->b = b;
                    390:     c->idx = ((36 * r) + (6 * g) + b + 16);
                    391: }
                    392:
                    393: static int intcolor_distance (intcolor *a, intcolor *b) {
                    394:     return
                    395:         (a->r - b->r) * (a->r - b->r)
                    396:         + (a->g - b->g) * (a->g - b->g)
                    397:         + (a->b - b->b) * (a->b - b->b);
                    398: }
                    399:
                    400: static int coerce_prs (intcolor *fg, intcolor *bg) {
                    401:     // search for an exact match in the list
                    402:     int pair;
                    403:     pair = prs[1].next;
                    404:     while (pair) {
                    405:         if (prs[pair].fore.idx == fg->idx && prs[pair].back.idx == bg->idx) {
                    406:             // perfect.
                    407:             prs[pair].count++;
                    408:             return pair;
                    409:         }
                    410:         pair = prs[pair].next;
                    411:     }
                    412:
                    413:     // no exact match? try to insert it as a new one
                    414:     pair = prs[0].next;
                    415:     if (pair) {
                    416:         // there's room!
                    417:
                    418:         // remove
                    419:         prs[0].next = prs[pair].next;
                    420:
                    421:         // insert at the front
                    422:         prs[pair].next = prs[1].next;
                    423:         prs[1].next = pair;
                    424:
                    425:         // initialize it
                    426:         prs[pair].fore = *fg;
                    427:         prs[pair].back = *bg;
                    428:         prs[pair].count = 1;
                    429:
                    430:         init_pair(pair, fg->idx, bg->idx);
                    431:
                    432:         return pair;
                    433:     }
                    434:
                    435:     // search for an approximate match in the list
                    436:     int bestpair = 0, bestscore = 2 * 3 * 6 * 6; // naive distance metric for now
                    437:     pair = prs[1].next;
                    438:     while (pair) {
                    439:         int delta = intcolor_distance(&prs[pair].fore, fg) + intcolor_distance(&prs[pair].back, bg);
                    440:         if (delta < bestscore) {
                    441:             bestscore = delta;
                    442:             bestpair = pair;
                    443:             if (delta == 1) break; // as good as it gets without being exact!
                    444:         }
                    445:         pair = prs[pair].next;
                    446:     }
                    447:
                    448:     prs[bestpair].count++;
                    449:     return bestpair;
                    450: }
                    451:
                    452: static void buffer_plot(int ch, int x, int y, fcolor *fg, fcolor *bg) {
                    453:     // int pair = 256 + x + y * minsize.width;
                    454:     // intcolor cube_fg, cube_bg;
                    455:     // coerce_colorcube(fg, &cube_fg),
                    456:     // coerce_colorcube(bg, &cube_bg);
                    457:
                    458:     // pair = cube_bg.idx;
                    459:     // cube_fg = cube_bg;
                    460:
                    461:
                    462:     // init_pair(pair, cube_fg.idx, cube_bg.idx);
                    463:
                    464:     // return pair;
                    465:
                    466:     intcolor cube_fg, cube_bg;
                    467:
                    468:     coerce_colorcube(fg, &cube_fg);
                    469:     coerce_colorcube(bg, &cube_bg);
                    470:     if (cube_fg.idx == cube_bg.idx) {
                    471:         // verify that the colors are really the same; otherwise, we'd better force the output apart
                    472:         int naive_distance =
                    473:             (fg->r - bg->r) * (fg->r - bg->r)
                    474:             + (fg->g - bg->g) * (fg->g - bg->g)
                    475:             + (fg->b - bg->b) * (fg->b - bg->b);
                    476:         if (naive_distance > 3) {
                    477:             // very arbitrary cutoff, and an arbitrary fix, very lazy
                    478:             if (cube_bg.r > 0) {cube_bg.r -= 1; cube_bg.idx -= 1; }
                    479:             if (cube_bg.g > 0) {cube_bg.g -= 1; cube_bg.idx -= 6; }
                    480:             if (cube_bg.b > 0) {cube_bg.b -= 1; cube_bg.idx -= 36; }
                    481:         }
                    482:     }
                    483:
                    484:     int cell = x + y * minsize.width;
                    485:     cell_buffer[cell].ch = ch;
                    486:     cell_buffer[cell].pair = -1;
                    487:     cell_buffer[cell].fore = cube_fg;
                    488:     cell_buffer[cell].back = cube_bg;
                    489: }
                    490:
                    491: static void buffer_render_256() {
                    492:     // build a new palette
                    493:     initialize_prs();
                    494:
                    495:     int length = minsize.width * minsize.height;
                    496:     int i, idx, x, y;
                    497:
                    498:     for (i = 0; i < length; i++) {
                    499:         cell_buffer[i].shuffle = i;
                    500:     }
                    501:     for (i = length - 1; i >= 0; i--) {
                    502:         // int roll = i == 0 ? 0 : rand() % i;
                    503:         // idx = cell_buffer[roll].shuffle;
                    504:
                    505:         // cell_buffer[roll].shuffle = cell_buffer[i].shuffle;
                    506:
                    507:         idx = i;
                    508:
                    509:         int pair = coerce_prs(&cell_buffer[idx].fore, &cell_buffer[idx].back);
                    510:         cell_buffer[idx].pair = pair;
                    511:     }
                    512:
                    513:     // render it all!
                    514:     i = 0;
                    515:     for (y = 0; y < minsize.height; y++) {
                    516:         move(y, 0);
                    517:         for (x = 0; x < minsize.width; x++) {
                    518:             color_set(cell_buffer[i].pair, NULL);
                    519:             addch(cell_buffer[i].ch);
                    520:             i++;
                    521:         }
                    522:     }
                    523: }
                    524:
                    525: static void term_mvaddch(int x, int y, int ch, fcolor *fg, fcolor *bg) {
                    526:     if (x < 0 || y < 0 || x >= minsize.width || y >= minsize.height) return;
                    527:
                    528:     if (colormode == coerce_16) {
                    529:         int c = best(fg, bg);
                    530:         attrset(COLOR_ATTR(c));
                    531:         mvaddch(y, x, ch);
                    532:     } else {
                    533:         buffer_plot(ch, x, y, fg, bg);
                    534:     }
                    535: }
                    536:
                    537: static void term_refresh() {
                    538:     // to set up a 256-color terminal, see:
                    539:     // http://push.cx/2008/256-color-xterms-in-ubuntu
                    540:     if (0 && can_change_color()) {
                    541:         int i;
                    542:         for (i = 0; i < 16; i++) {
                    543:             short r = palette[i].r * 1000;
                    544:             short g = palette[i].g * 1000;
                    545:             short b = palette[i].b * 1000;
                    546:             if (r < 0) r = 0;
                    547:             if (g < 0) g = 0;
                    548:             if (b < 0) b = 0;
                    549:             init_color(i + 1, r, g, b);
                    550:         }
                    551:     }
                    552:     if (0) {
                    553:         int i;
                    554:         short r, g, b;
                    555:         for (i = 0; i < 8; i++) {
                    556:             color_content(i, &r, &g, &b);
                    557:             palette[i].r = r * .001;
                    558:             palette[i].g = g * .001;
                    559:             palette[i].b = b * .001;
                    560:         }
                    561:     }
                    562:
                    563:
                    564:     if (colormode == coerce_256) {
                    565:         buffer_render_256();
                    566:     }
                    567:
                    568:     refresh();
                    569: }
                    570:
                    571: static void ensure_size( );
                    572:
                    573: static int term_getkey( ) {
                    574:     Term.mouse.justPressed = 0;
                    575:     Term.mouse.justReleased = 0;
                    576:     Term.mouse.justMoved = 0;
                    577:
                    578:     while (1) {
                    579:         int got = getch();
                    580:         if (got == KEY_RESIZE) {
                    581:             ensure_size( );
                    582:         } else if (got == KEY_MOUSE) {
                    583:             MEVENT mevent;
                    584:             getmouse (&mevent);
                    585:             Term.mouse.x = mevent.x;
                    586:             Term.mouse.y = mevent.y;
                    587:             Term.mouse.shift = (mevent.bstate & BUTTON_SHIFT) != 0;
                    588:             Term.mouse.control = (mevent.bstate & BUTTON_CTRL) != 0;
                    589:             if (mevent.bstate & BUTTON1_PRESSED) {
                    590:                 Term.mouse.justPressed = 1;
                    591:                 Term.mouse.isPressed = 1;
                    592:             } else if (mevent.bstate & BUTTON1_RELEASED) {
                    593:                 if (Term.mouse.isPressed) {
                    594:                     Term.mouse.justReleased = 1;
                    595:                     Term.mouse.isPressed = 0;
                    596:                 }
                    597:             } else {
                    598:                 Term.mouse.justMoved = 1;
                    599:             }
                    600:             return TERM_MOUSE;
                    601:         } else {
                    602:             if (got == KEY_ENTER) got = 13; // KEY_ENTER -> ^M for systems with odd values for KEY_ENTER
                    603:             if (got == ERR) return TERM_NONE;
                    604:             else return got;
                    605:         }
                    606:     }
                    607: }
                    608:
                    609: static int term_has_key() {
                    610:     int ch = getch();
                    611:     if (ch != ERR) {
                    612:         ungetch(ch);
                    613:         return 1;
                    614:     } else {
                    615:         return 0;
                    616:     }
                    617: }
                    618:
                    619: static void ensure_size( ) {
                    620:     int w = minsize.width, h = minsize.height;
                    621:
                    622:     getmaxyx(stdscr, Term.height, Term.width);
                    623:     if (Term.height < h || Term.width < w) {
                    624:         getmaxyx(stdscr, Term.height, Term.width);
                    625:         nodelay(stdscr, FALSE);
                    626:         while (Term.height < h || Term.width < w) {
                    627:             erase();
                    628:             attrset(COLOR_ATTR(7));
                    629:
                    630:             mvprintw(1,0,"Brogue needs a terminal window that is at least [%d x %d]", w, h);
                    631:
                    632:             attrset(COLOR_ATTR(15));
                    633:             mvprintw(2,0,"If your terminal can be resized, resize it now.\n");
                    634:
                    635:             attrset(COLOR_ATTR(7));
                    636:             mvprintw(3,0,"Press ctrl-c at any time to quit.\n");
                    637:
                    638:             printw("Width:  %d/%d\n", Term.width, w);
                    639:             printw("Height: %d/%d\n", Term.height, h);
                    640:
                    641:             mvprintw(10, 0, "Colors (pairs): %d (%d)\n", COLORS, COLOR_PAIRS);
                    642:
                    643:             getch();
                    644:             getmaxyx(stdscr, Term.height, Term.width);
                    645:         }
                    646:         nodelay(stdscr, TRUE);
                    647:         erase();
                    648:         refresh();
                    649:     }
                    650: }
                    651:
                    652: static void term_resize(int w, int h) {
                    653:     minsize.width = w;
                    654:     minsize.height = h;
                    655:
                    656:     // try to set the terminal size if the terminal will let us:
                    657:     term_set_size(h, w);
                    658:     // (this works in gnome-terminal, but causes trouble for curses on maximized windows.)
                    659:
                    660:     // now make sure it worked, and ask the user to resize the terminal if it didn't
                    661:     ensure_size();
                    662:
                    663:
                    664:     // make a new cell buffer
                    665:
                    666:     if (cell_buffer) free(cell_buffer);
                    667:     cell_buffer = malloc(sizeof(pairmode_cell) * w * h);
                    668:     // add error checking
                    669:     int i;
                    670:
                    671:     for (i = 0; i < w * h; i++) {
                    672:         // I guess we could just zero it all, hmm
                    673:         cell_buffer[i].ch = 0;
                    674:         cell_buffer[i].pair = 0;
                    675:         cell_buffer[i].fore.idx = 0;
                    676:         cell_buffer[i].back.idx = 0;
                    677:     }
                    678: }
                    679:
                    680: static void term_wait(int ms) {
                    681:     napms(ms);
                    682: }
                    683:
                    684:
                    685: struct {
                    686:     char *name;
                    687:     int ch;
                    688: } curses_keys[] = {
                    689:     {"NONE", TERM_NONE},
                    690:
                    691:     {"TAB", '\t'},
                    692:     {"ENTER", '\n'},
                    693:     {"RETURN", '\n'},
                    694:     {"SPACE", ' '},
                    695:
                    696:     {"ESC", 27},
                    697:     {"ESCAPE", 27},
                    698:
                    699:     {"BREAK", KEY_BREAK},
                    700:     {"SRESET", KEY_SRESET},
                    701:     {"RESET", KEY_RESET},
                    702:     {"DOWN", KEY_DOWN},
                    703:     {"UP", KEY_UP   },
                    704:     {"LEFT", KEY_LEFT},
                    705:     {"RIGHT", KEY_RIGHT},
                    706:     {"HOME", KEY_HOME},
                    707:     {"BACKSPACE", KEY_BACKSPACE},
                    708:     {"F1", KEY_F(1)},
                    709:     {"F2", KEY_F(2)},
                    710:     {"F3", KEY_F(3)},
                    711:     {"F4", KEY_F(4)},
                    712:     {"F5", KEY_F(5)},
                    713:     {"F6", KEY_F(6)},
                    714:     {"F7", KEY_F(7)},
                    715:     {"F8", KEY_F(8)},
                    716:     {"F9", KEY_F(9)},
                    717:     {"F10", KEY_F(10)},
                    718:     {"F11", KEY_F(11)},
                    719:     {"F12", KEY_F(12)},
                    720:     {"DL", KEY_DL},
                    721:     {"IL", KEY_IL},
                    722:     {"DC", KEY_DC},
                    723:     {"DEL", KEY_DC},
                    724:     {"DELETE", KEY_DC},
                    725:     {"IC", KEY_IC},
                    726:     {"EIC", KEY_EIC},
                    727:     {"CLEAR", KEY_CLEAR},
                    728:     {"EOS", KEY_EOS},
                    729:     {"EOL", KEY_EOL},
                    730:     {"SF", KEY_SF},
                    731:     {"SR", KEY_SR},
                    732:
                    733:     {"PGUP", KEY_NPAGE},
                    734:     {"PGDN", KEY_PPAGE},
                    735:     {"PAGEDOWN", KEY_NPAGE},
                    736:     {"PAGEUP", KEY_PPAGE},
                    737:     {"NPAGE", KEY_NPAGE},
                    738:     {"PPAGE", KEY_PPAGE},
                    739:
                    740:     {"STAB", KEY_STAB},
                    741:     {"CTAB", KEY_CTAB},
                    742:     {"CATAB", KEY_CATAB},
                    743:
                    744:     {"PRINT", KEY_PRINT},
                    745:     {"LL", KEY_LL},
                    746:     {"A1", KEY_A1},
                    747:     {"A3", KEY_A3},
                    748:     {"B2", KEY_B2},
                    749:     {"C1", KEY_C1},
                    750:     {"C3", KEY_C3},
                    751:     {"BTAB", KEY_BTAB},
                    752:     {"BEG", KEY_BEG },
                    753:     {"CANCEL", KEY_CANCEL},
                    754:     {"CLOSE", KEY_CLOSE},
                    755:     {"COMMAND", KEY_COMMAND},
                    756:     {"COPY", KEY_COPY},
                    757:     {"CREATE", KEY_CREATE},
                    758:     {"END", KEY_END },
                    759:     {"EXIT", KEY_EXIT},
                    760:     {"FIND", KEY_FIND},
                    761:     {"HELP", KEY_HELP},
                    762:     {"MARK", KEY_MARK},
                    763:     {"MESSAGE", KEY_MESSAGE},
                    764:     {"MOVE", KEY_MOVE},
                    765:     {"NEXT", KEY_NEXT},
                    766:     {"OPEN", KEY_OPEN},
                    767:     {"OPTIONS", KEY_OPTIONS},
                    768:     {"PREVIOUS", KEY_PREVIOUS},
                    769:     {"REDO", KEY_REDO},
                    770:     {"REFERENCE", KEY_REFERENCE},
                    771:     {"REFRESH", KEY_REFRESH},
                    772:     {"REPLACE", KEY_REPLACE},
                    773:     {"RESTART", KEY_RESTART},
                    774:     {"RESUME", KEY_RESUME},
                    775:     {"SAVE", KEY_SAVE},
                    776:     {"SBEG", KEY_SBEG},
                    777:     {"SCANCEL", KEY_SCANCEL},
                    778:     {"SCOMMAND", KEY_SCOMMAND},
                    779:     {"SCOPY", KEY_SCOPY},
                    780:     {"SCREATE", KEY_SCREATE},
                    781:     {"SDC", KEY_SDC },
                    782:     {"SDL", KEY_SDL },
                    783:     {"SELECT", KEY_SELECT},
                    784:     {"SEND", KEY_SEND},
                    785:     {"SEOL", KEY_SEOL},
                    786:     {"SEXIT", KEY_SEXIT},
                    787:     {"SFIND", KEY_SFIND},
                    788:     {"SHELP", KEY_SHELP},
                    789:     {"SHOME", KEY_SHOME},
                    790:     {"SIC", KEY_SIC },
                    791:     {"SLEFT", KEY_SLEFT},
                    792:     {"SMESSAGE", KEY_SMESSAGE},
                    793:     {"SMOVE", KEY_SMOVE},
                    794:     {"SNEXT", KEY_SNEXT},
                    795:     {"SOPTIONS", KEY_SOPTIONS},
                    796:     {"SPREVIOUS", KEY_SPREVIOUS},
                    797:     {"SPRINT", KEY_SPRINT},
                    798:     {"SREDO", KEY_SREDO},
                    799:     {"SREPLACE", KEY_SREPLACE},
                    800:     {"SRIGHT", KEY_SRIGHT},
                    801:     {"SRSUME", KEY_SRSUME},
                    802:     {"SSAVE", KEY_SSAVE},
                    803:     {"SSUSPEND", KEY_SSUSPEND},
                    804:     {"SUNDO", KEY_SUNDO},
                    805:     {"SUSPEND", KEY_SUSPEND},
                    806:     {"UNDO", KEY_UNDO},
                    807:     {"MOUSE", KEY_MOUSE},
                    808:     {"RESIZE", KEY_RESIZE},
                    809:     {NULL, 0},
                    810: };
                    811:
                    812: int term_keycodeByName(const char *name) {
                    813:     int i = 0;
                    814:     while (curses_keys[i].name != NULL) {
                    815:         if (strcmp(name, curses_keys[i].name) == 0) {
                    816:             return curses_keys[i].ch;
                    817:         }
                    818:         i++;
                    819:     }
                    820:
                    821:     return name[0];
                    822: }
                    823:
                    824:
                    825: struct term_t Term = {
                    826:     term_start,
                    827:     term_end,
                    828:     term_mvaddch,
                    829:     term_refresh,
                    830:     term_getkey,
                    831:     term_wait,
                    832:     term_has_key,
                    833:     term_title,
                    834:     term_resize,
                    835:     term_keycodeByName,
                    836:     {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_BACKSPACE, KEY_DC, KEY_F(12)}
                    837: };

CVSweb