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