[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     ! 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