/* * UAE - The Un*x Amiga Emulator * * [n]curses output. * * There are 17 color modes: * -H0/-H1 are black/white output * -H2 through -H16 give you different color allocation strategies. On my * system, -H14 seems to give nice results. * * Copyright 1997 Samuel Devulder, Bernd Schmidt */ /****************************************************************************/ #include "sysconfig.h" #include "sysdeps.h" #include #include /****************************************************************************/ #include "options.h" #include "threaddep/thread.h" #include "uae.h" #include "memory.h" #include "custom.h" #include "newcpu.h" #include "xwin.h" #include "keyboard.h" #include "keybuf.h" #include "disk.h" #include "debug.h" #include "gui.h" #ifdef HAVE_NCURSES_H #include #else #include #endif /****************************************************************************/ #define MAXGRAYCHAR 128 enum { MYCOLOR_BLACK, MYCOLOR_RED, MYCOLOR_GREEN, MYCOLOR_BLUE, MYCOLOR_YELLOW, MYCOLOR_CYAN, MYCOLOR_MAGENTA, MYCOLOR_WHITE }; static int mycolor2curses_map [] = { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_CYAN, COLOR_MAGENTA, COLOR_WHITE }; static int mycolor2pair_map[] = { 1,2,3,4,5,6,7,8 }; static chtype graychar[MAXGRAYCHAR]; static int maxc,max_graychar; static int curses_on; static int *x2graymap; /* Keyboard and mouse */ static int keystate[256]; static int keydelay = 20; static void curses_exit(void); /****************************************************************************/ static RETSIGTYPE sigbrkhandler(int foo) { curses_exit(); activate_debugger(); } void setup_brkhandler(void) { struct sigaction sa; sa.sa_handler = sigbrkhandler; sa.sa_flags = 0; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); } /***************************************************************************/ static void curses_insert_disk(void) { curses_exit(); gui_changesettings(); flush_screen(0,0); } /****************************************************************************/ /* * old: fmt = " .,:=(Io^vM^vb*X^#M^vX*boI(=:. ^b^vobX^#M" doesn't work: "^vXb*oI(=:. "; * good: fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. ^v^b=(IoJpgFPEB"; * * fmt = " .,:=(Io*b^vM^vX^#M^vXb*oI(=:. "; */ static void init_graychar(void) { chtype *p = graychar; chtype attrs; int i,j; char *fmt; attrs = termattrs(); if ((currprefs.color_mode & 1) == 0 && (attrs & (A_REVERSE | A_BOLD))) fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. ^v^boJpgFPEB"; else if ((currprefs.color_mode & 1) == 0 && (attrs & A_REVERSE)) fmt = " .':;=(IoJpgFPEB#^vgpJoI(=;:'. "; else /* One could find a better pattern.. */ fmt = " .`'^^\",:;i!1Il+=tfjxznuvyZYXHUOQ0MWB"; attrs = A_NORMAL | COLOR_PAIR (0); while(*fmt) { if(*fmt == '^') { ++fmt; switch(*fmt) { case 's': case 'S': attrs ^= A_STANDOUT; break; case 'v': case 'V': attrs ^= A_REVERSE; break; case 'b': case 'B': attrs ^= A_BOLD; break; case 'd': case 'D': attrs ^= A_DIM; break; case 'u': case 'U': attrs ^= A_UNDERLINE; break; case 'p': case 'P': attrs = A_NORMAL; break; case '#': if(ACS_CKBOARD == ':') *p++ = (attrs | '#'); else *p++ = (attrs | ACS_CKBOARD); break; default: *p++ = (attrs | *fmt); break; } ++fmt; } else *p++ = (attrs | *fmt++); if(p >= graychar + MAXGRAYCHAR) break; } max_graychar = (p - graychar) - 1; for (i = 0; i <= maxc; i++) x2graymap[i] = i * max_graychar / maxc; #if 0 for(j=0;j> 8; g = (i >> 4) & 15; b = i & 15; xcolors[i] = (77 * r + 151 * g + 28 * b)/16; if(xcolors[i] > maxc) maxc = xcolors[i]; m = r; if (g > m) m = g; if (b > m) m = b; if (m == 0) { xcolors[i] |= MYCOLOR_WHITE << 8; /* to get gray instead of black in dark areas */ continue; } if ((currprefs.color_mode & ~1) != 0) { r1 = r*15 / m; g1 = g*15 / m; b1 = b*15 / m; comp = 8; for (;;) { if (b1 < comp) { if (r1 < comp) ctype = MYCOLOR_GREEN; else if (g1 < comp) ctype = MYCOLOR_RED; else ctype = MYCOLOR_YELLOW; } else { if (r1 < comp) { if (g1 < comp) ctype = MYCOLOR_BLUE; else ctype = MYCOLOR_CYAN; } else if (g1 < comp) ctype = MYCOLOR_MAGENTA; else { comp += 4; if (comp == 12 && (currprefs.color_mode & 2) != 0) continue; ctype = MYCOLOR_WHITE; } } break; } if (currprefs.color_mode & 8) { if (ctype == MYCOLOR_BLUE && xcolors[i] > /*27*/50) ctype = r1 > (g1+2) ? MYCOLOR_MAGENTA : MYCOLOR_CYAN; if (ctype == MYCOLOR_RED && xcolors[i] > /*75*/ 90) ctype = b1 > (g1+6) ? MYCOLOR_MAGENTA : MYCOLOR_YELLOW; } xcolors[i] |= ctype << 8; } } if (currprefs.color_mode & 4) { int j; for (j = MYCOLOR_RED; j < MYCOLOR_WHITE; j++) { int best = 0, maxv = 0; int multi, divi; for (i = 0; i < 4096; i++) if ((xcolors[i] & 255) > maxv && (xcolors[i] >> 8) == j) { best = i; maxv = (xcolors[best] & 255); } /* Now maxv is the highest intensity a color of type J is supposed to have. * In reality, it will most likely only have intensity maxv*multi/divi. * We try to correct this. */ maxv = maxv * 256 / maxc; divi = 256; switch (j) { case MYCOLOR_RED: multi = 77; break; case MYCOLOR_GREEN: multi = 151; break; case MYCOLOR_BLUE: multi = 28; break; case MYCOLOR_YELLOW: multi = 228; break; case MYCOLOR_CYAN: multi = 179; break; case MYCOLOR_MAGENTA: multi = 105; break; default: abort(); } #if 1 /* This makes the correction less extreme */ if (! (currprefs.color_mode & 8)) multi = (multi + maxv) / 2; #endif for (i = 0; i < 4096; i++) { int v = xcolors[i]; if ((v >> 8) != j) continue; v &= 255; /* I don't think either of these is completely correct, but * the first one produces rather good results. */ #if 1 v = v * divi / multi; if (v > maxc) v = maxc; #else v = v * 256 / maxv); if (v > maxc) /*maxc = v*/abort(); #endif xcolors[i] = v | (j << 8); } } } x2graymap = (int *)malloc(sizeof(int) * (maxc+1)); } static void curses_init(void) { initscr (); start_color (); if (! has_colors () || COLOR_PAIRS < 20 /* whatever */) currprefs.color_mode &= 1; else { init_pair (1, COLOR_BLACK, COLOR_BLACK); init_pair (2, COLOR_RED, COLOR_BLACK); init_pair (3, COLOR_GREEN, COLOR_BLACK); init_pair (4, COLOR_BLUE, COLOR_BLACK); init_pair (5, COLOR_YELLOW, COLOR_BLACK); init_pair (6, COLOR_CYAN, COLOR_BLACK); init_pair (7, COLOR_MAGENTA, COLOR_BLACK); init_pair (8, COLOR_WHITE, COLOR_BLACK); } printf ("curses_init: %d pairs available\n", COLOR_PAIRS); cbreak(); noecho(); nonl (); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); nodelay(stdscr, TRUE); leaveok(stdscr, TRUE); attron (A_NORMAL | COLOR_PAIR (0)); bkgd(' '|COLOR_PAIR(0)); #ifdef NCURSES_MOUSE_VERSION mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON3_PRESSED | BUTTON3_RELEASED | REPORT_MOUSE_POSITION, NULL); #endif init_graychar(); curses_on = 1; } static void curses_exit(void) { #ifdef NCURSES_MOUSE_VERSION mousemask(0, NULL); #endif nocbreak(); echo(); nl(); intrflush(stdscr, TRUE); keypad(stdscr, FALSE); nodelay(stdscr, FALSE); leaveok(stdscr, FALSE); endwin(); curses_on = 0; } /****************************************************************************/ static int getgraycol(int x, int y) { uae_u8 *bufpt; int xs, xl, ys, yl, c, cm; xl = x_map[x+1] - (xs = x_map[x]); yl = y_map[y+1] - (ys = y_map[y]); bufpt = ((uae_u8 *)gfxvidinfo.bufmem) + ys*currprefs.gfx_width + xs; cm = c = 0; for(y = 0; y < yl; y++, bufpt += currprefs.gfx_width) for(x = 0; x < xl; x++) { c += bufpt[x]; ++cm; } if (cm) c /= cm; if (! currprefs.curses_reverse_video) c = maxc - c; return graychar[x2graymap[c]]; } static int getcol(int x, int y) { uae_u16 *bufpt; int xs, xl, ys, yl, c, cm; int bestcol = MYCOLOR_BLACK, bestccnt = 0; unsigned char colcnt [8]; memset (colcnt, 0 , sizeof colcnt); xl = x_map[x+1] - (xs = x_map[x]); yl = y_map[y+1] - (ys = y_map[y]); bufpt = ((uae_u16 *)gfxvidinfo.bufmem) + ys*currprefs.gfx_width + xs; cm = c = 0; for(y = 0; y < yl; y++, bufpt += currprefs.gfx_width) for(x = 0; x < xl; x++) { int v = bufpt[x]; int cnt; c += v & 0xFF; cnt = ++colcnt[v >> 8]; if (cnt > bestccnt) { bestccnt = cnt; bestcol = v >> 8; } ++cm; } if (cm) c /= cm; if (! currprefs.curses_reverse_video) c = maxc - c; return (graychar[x2graymap[c]] & ~A_COLOR) | COLOR_PAIR (mycolor2pair_map[bestcol]); } static void flush_line_txt(int y) { int x; move (y,0); if (currprefs.color_mode < 2) for (x = 0; x < COLS; ++x) { int c; c = getgraycol(x,y); addch(c); } else for (x = 0; x < COLS; ++x) { int c; c = getcol(x,y); addch(c); } } __inline__ void flush_line(int y) { if(y < 0 || y >= currprefs.gfx_height) { /* printf("flush_line out of window: %d\n", y); */ return; } if(!curses_on) return; flush_line_txt(y_rev_map[y]); } void flush_block (int ystart, int ystop) { int y; if(!curses_on) return; ystart = y_rev_map[ystart]; ystop = y_rev_map[ystop]; for(y = ystart; y <= ystop; ++y) flush_line_txt(y); } void flush_screen (int ystart, int ystop) { if(!debugging && !curses_on) { curses_init(); flush_block(0, currprefs.gfx_height - 1); } refresh(); } /****************************************************************************/ struct bstring *video_mode_menu = NULL; void vidmode_menu_selected(int a) { } int graphics_setup(void) { return 1; } int graphics_init(void) { int i; if (currprefs.color_mode > 16) write_log ("Bad color mode selected. Using default.\n"), currprefs.color_mode = 0; init_colors(); curses_init(); write_log ("Using %s.\n",longname()); if (debugging) curses_exit (); /* we have a 320x256x8 pseudo screen */ currprefs.gfx_width = 320; currprefs.gfx_height = 256; currprefs.gfx_lores = 1; gfxvidinfo.width = currprefs.gfx_width; gfxvidinfo.height = currprefs.gfx_height; gfxvidinfo.maxblocklines = 1000; gfxvidinfo.pixbytes = currprefs.color_mode < 2 ? 1 : 2; gfxvidinfo.rowbytes = gfxvidinfo.pixbytes * currprefs.gfx_width; gfxvidinfo.bufmem = (char *)calloc(gfxvidinfo.rowbytes, currprefs.gfx_height+1); gfxvidinfo.linemem = 0; gfxvidinfo.emergmem = 0; gfxvidinfo.can_double = 0; switch (gfxvidinfo.pixbytes) { case 1: for (i = 0; i < 4096; i++) xcolors[i] = xcolors[i] * 0x01010101; gfxvidinfo.can_double = 1; break; case 2: for (i = 0; i < 4096; i++) xcolors[i] = xcolors[i] * 0x00010001; gfxvidinfo.can_double = 1; break; } if(!gfxvidinfo.bufmem) { write_log ("Not enough memory.\n"); return 0; } for (i = 0; i < sizeof x_map / sizeof *x_map; i++) x_map[i] = (i * currprefs.gfx_width) / COLS; for (i = 0; i < sizeof y_map / sizeof *y_map; i++) y_map[i] = (i * currprefs.gfx_height) / LINES; for (i = 0; i < sizeof y_map / sizeof *y_map - 1; i++) { int l1 = y_map[i]; int l2 = y_map[i+1]; int j; if (l2 >= sizeof y_rev_map / sizeof *y_rev_map) break; for (j = l1; j < l2; j++) y_rev_map[j] = i; } buttonstate[0] = buttonstate[1] = buttonstate[2] = 0; for(i=0; i<256; i++) keystate[i] = 0; lastmx = lastmy = 0; newmousecounters = 0; return 1; } /****************************************************************************/ void graphics_leave(void) { curses_exit(); } /****************************************************************************/ static int keycode2amiga(int ch) { switch(ch) { case KEY_A1: return AK_NP7; case KEY_UP: return AK_NP8; case KEY_A3: return AK_NP9; case KEY_LEFT: return AK_NP4; case KEY_B2: return AK_NP5; case KEY_RIGHT: return AK_NP6; case KEY_C1: return AK_NP1; case KEY_DOWN: return AK_NP2; case KEY_C3: return AK_NP3; case KEY_ENTER: return AK_ENT; case 13: return AK_RET; case ' ': return AK_SPC; case 27: return AK_ESC; default: return -1; } } /***************************************************************************/ void handle_events(void) { int ch; int kc; /* Hack to simulate key release */ for(kc = 0; kc < 256; ++kc) { if(keystate[kc]) if(!--keystate[kc]) record_key((kc << 1) | 1); } if(buttonstate[0]) --buttonstate[0]; if(buttonstate[1]) --buttonstate[1]; if(buttonstate[2]) --buttonstate[2]; newmousecounters = 0; if(!curses_on) return; while((ch = getch())!=ERR) { if(ch == 12) {clearok(stdscr,TRUE);refresh();} #ifdef NCURSES_MOUSE_VERSION if(ch == KEY_MOUSE) { MEVENT ev; if(getmouse(&ev) == OK) { lastmx = (ev.x*currprefs.gfx_width)/COLS; lastmy = (ev.y*currprefs.gfx_height)/LINES; if(ev.bstate & BUTTON1_PRESSED) buttonstate[0] = keydelay; if(ev.bstate & BUTTON1_RELEASED) buttonstate[0] = 0; if(ev.bstate & BUTTON2_PRESSED) buttonstate[1] = keydelay; if(ev.bstate & BUTTON2_RELEASED) buttonstate[1] = 0; if(ev.bstate & BUTTON3_PRESSED) buttonstate[2] = keydelay; if(ev.bstate & BUTTON3_RELEASED) buttonstate[2] = 0; } } #endif if (ch == 6) ++lastmx; /* ^F */ if (ch == 2) --lastmx; /* ^B */ if (ch == 14) ++lastmy; /* ^N */ if (ch == 16) --lastmy; /* ^P */ if (ch == 11) {buttonstate[0] = keydelay;ch = 0;} /* ^K */ if (ch == 25) {buttonstate[2] = keydelay;ch = 0;} /* ^Y */ if (ch == 15) uae_reset (); /* ^O */ if (ch == 17) uae_quit (); /* ^Q */ if (ch == KEY_F(1)) { curses_insert_disk(); ch = 0; } if(isupper(ch)) { keystate[AK_LSH] = keystate[AK_RSH] = keydelay; record_key(AK_LSH << 1); record_key(AK_RSH << 1); kc = keycode2amiga(tolower(ch)); keystate[kc] = keydelay; record_key(kc << 1); } else if((kc = keycode2amiga(ch)) >= 0) { keystate[kc] = keydelay; record_key(kc << 1); } } gui_handle_events(); } /***************************************************************************/ void target_specific_usage(void) { printf("----------------------------------------------------------------------------\n"); printf("[n]curses specific usage:\n"); printf(" -x : Display reverse video.\n"); printf("By default uae will assume a black on white display. If yours\n"); printf("is light on dark, use -x. In case of graphics garbage, ^L will\n"); printf("redisplay the screen. ^K simulate left mouse button, ^Y RMB.\n"); printf("If you are using a xterm UAE can use the mouse. Else use ^F ^B\n"); printf("^P ^N to emulate mouse mouvements.\n"); printf("----------------------------------------------------------------------------\n"); } /***************************************************************************/ int check_prefs_changed_gfx (void) { return 0; } int debuggable(void) { return 1; } int needmousehack(void) { return 1; } void LED(int on) { } void write_log (const char *buf, ...) { } int lockscr (void) { return 1; } void unlockscr (void) { } void target_save_options (FILE *f, struct uae_prefs *p) { fprintf (f, "curses.reverse_video=%s\n", p->curses_reverse_video ? "true" : "false"); } int target_parse_option (struct uae_prefs *p, char *option, char *value) { return (cfgfile_yesno (option, value, "reverse_video", &p->curses_reverse_video)); }