1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * [n]curses output.
5   *
6   * There are 17 color modes:
7   *  -H0/-H1 are black/white output
8   *  -H2 through -H16 give you different color allocation strategies. On my
9   *    system, -H14 seems to give nice results.
10   *
11   * Copyright 1997 Samuel Devulder, Bernd Schmidt
12   */
13 
14 /****************************************************************************/
15 
16 #include "sysconfig.h"
17 #include "sysdeps.h"
18 
19 #include <ctype.h>
20 #include <signal.h>
21 
22 /****************************************************************************/
23 
24 #include "options.h"
25 #include "threaddep/thread.h"
26 #include "uae.h"
27 #include "memory.h"
28 #include "custom.h"
29 #include "newcpu.h"
30 #include "xwin.h"
31 #include "keyboard.h"
32 #include "keybuf.h"
33 #include "disk.h"
34 #include "debug.h"
35 #include "gui.h"
36 
37 #ifdef HAVE_NCURSES_H
38 #include <ncurses.h>
39 #else
40 #include <curses.h>
41 #endif
42 
43 /****************************************************************************/
44 
45 #define MAXGRAYCHAR 128
46 
47 enum {
48     MYCOLOR_BLACK, MYCOLOR_RED, MYCOLOR_GREEN, MYCOLOR_BLUE,
49     MYCOLOR_YELLOW, MYCOLOR_CYAN, MYCOLOR_MAGENTA, MYCOLOR_WHITE
50 };
51 
52 static int mycolor2curses_map [] = {
53     COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE,
54     COLOR_YELLOW, COLOR_CYAN, COLOR_MAGENTA, COLOR_WHITE
55 };
56 
57 static int mycolor2pair_map[] = { 1,2,3,4,5,6,7,8 };
58 
59 static chtype graychar[MAXGRAYCHAR];
60 static int maxc,max_graychar;
61 static int curses_on;
62 
63 static int *x2graymap;
64 
65 /* Keyboard and mouse */
66 
67 static int keystate[256];
68 static int keydelay = 20;
69 
70 static void curses_exit(void);
71 
72 /****************************************************************************/
73 
sigbrkhandler(int foo)74 static RETSIGTYPE sigbrkhandler(int foo)
75 {
76     curses_exit();
77     activate_debugger();
78 }
79 
setup_brkhandler(void)80 void setup_brkhandler(void)
81 {
82     struct sigaction sa;
83     sa.sa_handler = sigbrkhandler;
84     sa.sa_flags = 0;
85     sa.sa_flags = SA_RESTART;
86     sigemptyset(&sa.sa_mask);
87     sigaction(SIGINT, &sa, NULL);
88 }
89 
90 /***************************************************************************/
91 
curses_insert_disk(void)92 static void curses_insert_disk(void)
93 {
94     curses_exit();
95     gui_changesettings();
96     flush_screen(0,0);
97 }
98 
99 /****************************************************************************/
100 
101 /*
102  * old:	fmt = " .,:=(Io^vM^vb*X^#M^vX*boI(=:. ^b^vobX^#M" doesn't work: "^vXb*oI(=:. ";
103  * good:	fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. ^v^b=(IoJpgFPEB";
104  *
105  * 	fmt = " .,:=(Io*b^vM^vX^#M^vXb*oI(=:. ";
106  */
107 
init_graychar(void)108 static void init_graychar(void)
109 {
110     chtype *p = graychar;
111     chtype attrs;
112     int i,j;
113     char *fmt;
114 
115     attrs = termattrs();
116     if ((currprefs.color_mode & 1) == 0 && (attrs & (A_REVERSE | A_BOLD)))
117 	fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. ^v^boJpgFPEB";
118     else if ((currprefs.color_mode & 1) == 0 && (attrs & A_REVERSE))
119 	fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. ";
120     else
121 	/* One could find a better pattern.. */
122 	fmt = " .`'^^\",:;i!1Il+=tfjxznuvyZYXHUOQ0MWB";
123     attrs = A_NORMAL | COLOR_PAIR (0);
124     while(*fmt) {
125 	if(*fmt == '^') {
126 	    ++fmt;
127 	    switch(*fmt) {
128 		case 's': case 'S': attrs ^= A_STANDOUT; break;
129 		case 'v': case 'V': attrs ^= A_REVERSE; break;
130 		case 'b': case 'B': attrs ^= A_BOLD; break;
131 		case 'd': case 'D': attrs ^= A_DIM; break;
132 		case 'u': case 'U': attrs ^= A_UNDERLINE; break;
133 		case 'p': case 'P': attrs  = A_NORMAL; break;
134 		case '#': if(ACS_CKBOARD == ':')
135 			       *p++ = (attrs | '#');
136 			  else *p++ = (attrs | ACS_CKBOARD); break;
137 		default:  *p++ = (attrs | *fmt); break;
138 	    }
139 	    ++fmt;
140 	} else *p++ = (attrs | *fmt++);
141 	if(p >= graychar + MAXGRAYCHAR) break;
142     }
143     max_graychar = (p - graychar) - 1;
144 
145     for (i = 0; i <= maxc; i++)
146 	x2graymap[i] = i * max_graychar / maxc;
147 #if 0
148     for(j=0;j<LINES;++j) {
149 	move(j,0);
150 	for(i=0;i<COLS;++i) addch(graychar[i % (max_graychar+1)]);
151     }
152     refresh();
153     sleep(3);
154 #endif
155 }
156 
157 static int x_map[900], y_map[700], y_rev_map [700];
158 
159 
160 /****************************************************************************/
161 
init_colors(void)162 static void init_colors(void)
163 {
164     int i;
165 
166     maxc = 0;
167 
168     for(i = 0; i < 4096; ++i) {
169 	int r,g,b,r1,g1,b1;
170 	int m, comp;
171 	int ctype;
172 
173 	r =  i >> 8;
174 	g = (i >> 4) & 15;
175 	b =  i & 15;
176 
177 	xcolors[i] = (77 * r + 151 * g + 28 * b)/16;
178 	if(xcolors[i] > maxc)
179 	    maxc = xcolors[i];
180 	m = r;
181 	if (g > m)
182 	    m = g;
183 	if (b > m)
184 	    m = b;
185 	if (m == 0) {
186 	    xcolors[i] |= MYCOLOR_WHITE << 8; /* to get gray instead of black in dark areas */
187 	    continue;
188 	}
189 
190 	if ((currprefs.color_mode & ~1) != 0) {
191 	    r1 = r*15 / m;
192 	    g1 = g*15 / m;
193 	    b1 = b*15 / m;
194 
195 	    comp = 8;
196 	    for (;;) {
197 		if (b1 < comp) {
198 		    if (r1 < comp)
199 			ctype = MYCOLOR_GREEN;
200 		    else if (g1 < comp)
201 			ctype = MYCOLOR_RED;
202 		    else
203 			ctype = MYCOLOR_YELLOW;
204 		} else {
205 		    if (r1 < comp) {
206 			if (g1 < comp)
207 			    ctype = MYCOLOR_BLUE;
208 			else
209 			    ctype = MYCOLOR_CYAN;
210 		    } else if (g1 < comp)
211 			    ctype = MYCOLOR_MAGENTA;
212 		    else {
213 			comp += 4;
214 			if (comp == 12 && (currprefs.color_mode & 2) != 0)
215 			    continue;
216 			ctype = MYCOLOR_WHITE;
217 		    }
218 		}
219 		break;
220 	    }
221 	    if (currprefs.color_mode & 8) {
222 		if (ctype == MYCOLOR_BLUE && xcolors[i] > /*27*/50)
223 		    ctype = r1 > (g1+2) ? MYCOLOR_MAGENTA : MYCOLOR_CYAN;
224 		if (ctype == MYCOLOR_RED && xcolors[i] > /*75*/ 90)
225 		    ctype = b1 > (g1+6) ? MYCOLOR_MAGENTA : MYCOLOR_YELLOW;
226 	    }
227 	    xcolors[i] |= ctype << 8;
228 	}
229     }
230     if (currprefs.color_mode & 4) {
231 	int j;
232 	for (j = MYCOLOR_RED; j < MYCOLOR_WHITE; j++) {
233 	    int best = 0, maxv = 0;
234 	    int multi, divi;
235 
236 	    for (i = 0; i < 4096; i++)
237 		if ((xcolors[i] & 255) > maxv && (xcolors[i] >> 8) == j) {
238 		    best = i;
239 		    maxv = (xcolors[best] & 255);
240 		}
241 	    /* Now maxv is the highest intensity a color of type J is supposed to have.
242 	     * In  reality, it will most likely only have intensity maxv*multi/divi.
243 	     * We try to correct this. */
244 	    maxv = maxv * 256 / maxc;
245 
246 	    divi = 256;
247 	    switch (j) {
248 	     case MYCOLOR_RED:     multi = 77; break;
249 	     case MYCOLOR_GREEN:   multi = 151; break;
250 	     case MYCOLOR_BLUE:    multi = 28; break;
251 	     case MYCOLOR_YELLOW:  multi = 228; break;
252 	     case MYCOLOR_CYAN:    multi = 179; break;
253 	     case MYCOLOR_MAGENTA: multi = 105; break;
254 	     default: abort();
255 	    }
256 #if 1 /* This makes the correction less extreme */
257 	    if (! (currprefs.color_mode & 8))
258 		multi = (multi + maxv) / 2;
259 #endif
260 	    for (i = 0; i < 4096; i++) {
261 		int v = xcolors[i];
262 		if ((v >> 8) != j)
263 		    continue;
264 		v &= 255;
265 		/* I don't think either of these is completely correct, but
266 		 * the first one produces rather good results. */
267 #if 1
268 		v = v * divi / multi;
269 		if (v > maxc)
270 		    v = maxc;
271 #else
272 		v = v * 256 / maxv);
273 		if (v > maxc)
274 		    /*maxc = v*/abort();
275 #endif
276 		xcolors[i] = v | (j << 8);
277 	    }
278 	}
279     }
280     x2graymap = (int *)malloc(sizeof(int) * (maxc+1));
281 }
282 
curses_init(void)283 static void curses_init(void)
284 {
285     initscr ();
286 
287     start_color ();
288     if (! has_colors () || COLOR_PAIRS < 20 /* whatever */)
289 	currprefs.color_mode &= 1;
290     else {
291 	init_pair (1, COLOR_BLACK, COLOR_BLACK);
292 	init_pair (2, COLOR_RED, COLOR_BLACK);
293 	init_pair (3, COLOR_GREEN, COLOR_BLACK);
294 	init_pair (4, COLOR_BLUE, COLOR_BLACK);
295 	init_pair (5, COLOR_YELLOW, COLOR_BLACK);
296 	init_pair (6, COLOR_CYAN, COLOR_BLACK);
297 	init_pair (7, COLOR_MAGENTA, COLOR_BLACK);
298 	init_pair (8, COLOR_WHITE, COLOR_BLACK);
299     }
300     printf ("curses_init: %d pairs available\n", COLOR_PAIRS);
301 
302     cbreak(); noecho();
303     nonl (); intrflush(stdscr, FALSE); keypad(stdscr, TRUE);
304     nodelay(stdscr, TRUE);
305     leaveok(stdscr, TRUE);
306 
307     attron (A_NORMAL | COLOR_PAIR (0));
308     bkgd(' '|COLOR_PAIR(0));
309 
310 #ifdef NCURSES_MOUSE_VERSION
311     mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED |
312 	      BUTTON2_PRESSED | BUTTON2_RELEASED |
313 	      BUTTON3_PRESSED | BUTTON3_RELEASED |
314 	      REPORT_MOUSE_POSITION, NULL);
315 #endif
316 
317     init_graychar();
318     curses_on = 1;
319 }
320 
curses_exit(void)321 static void curses_exit(void)
322 {
323 #ifdef NCURSES_MOUSE_VERSION
324     mousemask(0, NULL);
325 #endif
326 
327     nocbreak(); echo(); nl(); intrflush(stdscr, TRUE);
328     keypad(stdscr, FALSE); nodelay(stdscr, FALSE); leaveok(stdscr, FALSE);
329     endwin();
330     curses_on = 0;
331 }
332 
333 /****************************************************************************/
334 
getgraycol(int x,int y)335 static int getgraycol(int x, int y)
336 {
337     uae_u8 *bufpt;
338     int xs, xl, ys, yl, c, cm;
339 
340     xl = x_map[x+1] - (xs = x_map[x]);
341     yl = y_map[y+1] - (ys = y_map[y]);
342 
343     bufpt = ((uae_u8 *)gfxvidinfo.bufmem) + ys*currprefs.gfx_width + xs;
344 
345     cm = c = 0;
346     for(y = 0; y < yl; y++, bufpt += currprefs.gfx_width)
347 	for(x = 0; x < xl; x++) {
348 	    c += bufpt[x];
349 	    ++cm;
350 	}
351     if (cm)
352 	c /= cm;
353     if (! currprefs.curses_reverse_video)
354 	c = maxc - c;
355     return graychar[x2graymap[c]];
356 }
357 
getcol(int x,int y)358 static int getcol(int x, int y)
359 {
360     uae_u16 *bufpt;
361     int xs, xl, ys, yl, c, cm;
362     int bestcol = MYCOLOR_BLACK, bestccnt = 0;
363     unsigned char colcnt [8];
364 
365     memset (colcnt, 0 , sizeof colcnt);
366 
367     xl = x_map[x+1] - (xs = x_map[x]);
368     yl = y_map[y+1] - (ys = y_map[y]);
369 
370     bufpt = ((uae_u16 *)gfxvidinfo.bufmem) + ys*currprefs.gfx_width + xs;
371 
372     cm = c = 0;
373     for(y = 0; y < yl; y++, bufpt += currprefs.gfx_width)
374 	for(x = 0; x < xl; x++) {
375 	    int v = bufpt[x];
376 	    int cnt;
377 
378 	    c += v & 0xFF;
379 	    cnt = ++colcnt[v >> 8];
380 	    if (cnt > bestccnt) {
381 		bestccnt = cnt;
382 		bestcol = v >> 8;
383 	    }
384 	    ++cm;
385 	}
386     if (cm)
387 	c /= cm;
388     if (! currprefs.curses_reverse_video)
389 	c = maxc - c;
390     return (graychar[x2graymap[c]] & ~A_COLOR) | COLOR_PAIR (mycolor2pair_map[bestcol]);
391 }
392 
flush_line_txt(int y)393 static void flush_line_txt(int y)
394 {
395     int x;
396     move (y,0);
397     if (currprefs.color_mode < 2)
398 	for (x = 0; x < COLS; ++x) {
399 	    int c;
400 
401 	    c = getgraycol(x,y);
402 	    addch(c);
403 	}
404     else
405 	for (x = 0; x < COLS; ++x) {
406 	    int c;
407 
408 	    c = getcol(x,y);
409 	    addch(c);
410 	}
411 }
412 
flush_line(int y)413 __inline__ void flush_line(int y)
414 {
415     if(y < 0 || y >= currprefs.gfx_height) {
416 /*       printf("flush_line out of window: %d\n", y); */
417        return;
418     }
419     if(!curses_on)
420 	return;
421     flush_line_txt(y_rev_map[y]);
422 }
423 
flush_block(int ystart,int ystop)424 void flush_block (int ystart, int ystop)
425 {
426     int y;
427     if(!curses_on)
428 	return;
429     ystart = y_rev_map[ystart];
430     ystop  = y_rev_map[ystop];
431     for(y = ystart; y <= ystop; ++y)
432 	flush_line_txt(y);
433 }
434 
flush_screen(int ystart,int ystop)435 void flush_screen (int ystart, int ystop)
436 {
437     if(!debugging && !curses_on) {
438 	curses_init();
439 	flush_block(0, currprefs.gfx_height - 1);
440     }
441     refresh();
442 }
443 
444 /****************************************************************************/
445 
446 struct bstring *video_mode_menu = NULL;
447 
vidmode_menu_selected(int a)448 void vidmode_menu_selected(int a)
449 {
450 }
451 
graphics_setup(void)452 int graphics_setup(void)
453 {
454     return 1;
455 }
456 
graphics_init(void)457 int graphics_init(void)
458 {
459     int i;
460 
461     if (currprefs.color_mode > 16)
462 	write_log ("Bad color mode selected. Using default.\n"), currprefs.color_mode = 0;
463 
464     init_colors();
465 
466     curses_init();
467     write_log ("Using %s.\n",longname());
468 
469     if (debugging)
470 	curses_exit ();
471 
472     /* we have a 320x256x8 pseudo screen */
473 
474     currprefs.gfx_width = 320;
475     currprefs.gfx_height = 256;
476     currprefs.gfx_lores = 1;
477 
478     gfxvidinfo.width = currprefs.gfx_width;
479     gfxvidinfo.height = currprefs.gfx_height;
480     gfxvidinfo.maxblocklines = 1000;
481     gfxvidinfo.pixbytes = currprefs.color_mode < 2 ? 1 : 2;
482     gfxvidinfo.rowbytes = gfxvidinfo.pixbytes * currprefs.gfx_width;
483     gfxvidinfo.bufmem = (char *)calloc(gfxvidinfo.rowbytes, currprefs.gfx_height+1);
484     gfxvidinfo.linemem = 0;
485     gfxvidinfo.emergmem = 0;
486     gfxvidinfo.can_double = 0;
487     switch (gfxvidinfo.pixbytes) {
488      case 1:
489 	for (i = 0; i < 4096; i++)
490 	    xcolors[i] = xcolors[i] * 0x01010101;
491 	gfxvidinfo.can_double = 1;
492 	break;
493      case 2:
494 	for (i = 0; i < 4096; i++)
495 	    xcolors[i] = xcolors[i] * 0x00010001;
496 	gfxvidinfo.can_double = 1;
497 	break;
498     }
499     if(!gfxvidinfo.bufmem) {
500 	write_log ("Not enough memory.\n");
501 	return 0;
502     }
503 
504     for (i = 0; i < sizeof x_map / sizeof *x_map; i++)
505 	x_map[i] = (i * currprefs.gfx_width) / COLS;
506     for (i = 0; i < sizeof y_map / sizeof *y_map; i++)
507 	y_map[i] = (i * currprefs.gfx_height) / LINES;
508     for (i = 0; i < sizeof y_map / sizeof *y_map - 1; i++) {
509 	int l1 = y_map[i];
510 	int l2 = y_map[i+1];
511 	int j;
512 	if (l2 >= sizeof y_rev_map / sizeof *y_rev_map)
513 	    break;
514 	for (j = l1; j < l2; j++)
515 	    y_rev_map[j] = i;
516     }
517 
518     buttonstate[0] = buttonstate[1] = buttonstate[2] = 0;
519     for(i=0; i<256; i++)
520 	keystate[i] = 0;
521 
522     lastmx = lastmy = 0;
523     newmousecounters = 0;
524 
525     return 1;
526 }
527 
528 /****************************************************************************/
529 
graphics_leave(void)530 void graphics_leave(void)
531 {
532     curses_exit();
533 }
534 
535 /****************************************************************************/
536 
keycode2amiga(int ch)537 static int keycode2amiga(int ch)
538 {
539     switch(ch) {
540 	case KEY_A1:    return AK_NP7;
541 	case KEY_UP:    return AK_NP8;
542 	case KEY_A3:    return AK_NP9;
543 	case KEY_LEFT:  return AK_NP4;
544 	case KEY_B2:    return AK_NP5;
545 	case KEY_RIGHT: return AK_NP6;
546 	case KEY_C1:    return AK_NP1;
547 	case KEY_DOWN:  return AK_NP2;
548 	case KEY_C3:    return AK_NP3;
549 	case KEY_ENTER: return AK_ENT;
550 	case 13:        return AK_RET;
551 	case ' ':       return AK_SPC;
552 	case 27:        return AK_ESC;
553 	default: return -1;
554     }
555 }
556 
557 /***************************************************************************/
558 
handle_events(void)559 void handle_events(void)
560 {
561     int ch;
562     int kc;
563 
564     /* Hack to simulate key release */
565     for(kc = 0; kc < 256; ++kc) {
566 	if(keystate[kc]) if(!--keystate[kc]) record_key((kc << 1) | 1);
567     }
568     if(buttonstate[0]) --buttonstate[0];
569     if(buttonstate[1]) --buttonstate[1];
570     if(buttonstate[2]) --buttonstate[2];
571 
572     newmousecounters = 0;
573     if(!curses_on) return;
574 
575     while((ch = getch())!=ERR) {
576 	if(ch == 12) {clearok(stdscr,TRUE);refresh();}
577 #ifdef NCURSES_MOUSE_VERSION
578 	if(ch == KEY_MOUSE) {
579 	    MEVENT ev;
580 	    if(getmouse(&ev) == OK) {
581 		lastmx = (ev.x*currprefs.gfx_width)/COLS;
582 		lastmy = (ev.y*currprefs.gfx_height)/LINES;
583 		if(ev.bstate & BUTTON1_PRESSED)  buttonstate[0] = keydelay;
584 		if(ev.bstate & BUTTON1_RELEASED) buttonstate[0] = 0;
585 		if(ev.bstate & BUTTON2_PRESSED)  buttonstate[1] = keydelay;
586 		if(ev.bstate & BUTTON2_RELEASED) buttonstate[1] = 0;
587 		if(ev.bstate & BUTTON3_PRESSED)  buttonstate[2] = keydelay;
588 		if(ev.bstate & BUTTON3_RELEASED) buttonstate[2] = 0;
589 	    }
590 	}
591 #endif
592 	if (ch == 6)  ++lastmx; /* ^F */
593 	if (ch == 2)  --lastmx; /* ^B */
594 	if (ch == 14) ++lastmy; /* ^N */
595 	if (ch == 16) --lastmy; /* ^P */
596 	if (ch == 11) {buttonstate[0] = keydelay;ch = 0;} /* ^K */
597 	if (ch == 25) {buttonstate[2] = keydelay;ch = 0;} /* ^Y */
598 	if (ch == 15) uae_reset (); /* ^O */
599 	if (ch == 17) uae_quit (); /* ^Q */
600 	if (ch == KEY_F(1)) {
601 	  curses_insert_disk();
602 	  ch = 0;
603 	}
604 
605 	if(isupper(ch)) {
606 	    keystate[AK_LSH] =
607 	    keystate[AK_RSH] = keydelay;
608 	    record_key(AK_LSH << 1);
609 	    record_key(AK_RSH << 1);
610 	    kc = keycode2amiga(tolower(ch));
611 	    keystate[kc] = keydelay;
612 	    record_key(kc << 1);
613 	} else if((kc = keycode2amiga(ch)) >= 0) {
614 	    keystate[kc] = keydelay;
615 	    record_key(kc << 1);
616 	}
617     }
618     gui_handle_events();
619 }
620 
621 /***************************************************************************/
622 
target_specific_usage(void)623 void target_specific_usage(void)
624 {
625     printf("----------------------------------------------------------------------------\n");
626     printf("[n]curses specific usage:\n");
627     printf("  -x : Display reverse video.\n");
628     printf("By default uae will assume a black on white display. If yours\n");
629     printf("is light on dark, use -x. In case of graphics garbage, ^L will\n");
630     printf("redisplay the screen. ^K simulate left mouse button, ^Y RMB.\n");
631     printf("If you are using a xterm UAE can use the mouse. Else use ^F ^B\n");
632     printf("^P ^N to emulate mouse mouvements.\n");
633     printf("----------------------------------------------------------------------------\n");
634 }
635 
636 /***************************************************************************/
637 
check_prefs_changed_gfx(void)638 int check_prefs_changed_gfx (void)
639 {
640     return 0;
641 }
642 
debuggable(void)643 int debuggable(void)
644 {
645     return 1;
646 }
647 
needmousehack(void)648 int needmousehack(void)
649 {
650     return 1;
651 }
652 
LED(int on)653 void LED(int on)
654 {
655 }
656 
write_log(const char * buf,...)657 void write_log (const char *buf, ...)
658 {
659 
660 }
661 
lockscr(void)662 int lockscr (void)
663 {
664     return 1;
665 }
666 
unlockscr(void)667 void unlockscr (void)
668 {
669 }
670 
target_save_options(FILE * f,struct uae_prefs * p)671 void target_save_options (FILE *f, struct uae_prefs *p)
672 {
673     fprintf (f, "curses.reverse_video=%s\n", p->curses_reverse_video ? "true" : "false");
674 }
675 
target_parse_option(struct uae_prefs * p,char * option,char * value)676 int target_parse_option (struct uae_prefs *p, char *option, char *value)
677 {
678     return (cfgfile_yesno (option, value, "reverse_video", &p->curses_reverse_video));
679 }
680