/* Module : KMVID.C Author : Kim Moser Date : 4/5/89 System : IBM PC / Borland Turbo-C 2.0 Descrip: My video routines */ #include #include #include #include #include #include "kmvid.h" static unsigned char far *CRT_MODE = (unsigned char far *) 0x00400049L; /* BIOS video mode number */ int vidmode(void) { return (int) *CRT_MODE; } static unsigned int far *CRT_COL = (unsigned int far *) 0x0040004AL; /* Pointer to number of columns */ int vidcol(void) { return (int) *CRT_COL; } static unsigned char far *CRT_ROW = (unsigned char far *) 0x00000484L; int vidrow(void) { return (int) (*CRT_ROW +1); } typedef struct scrnchar far *vid_addr; /* Must be FAR because video memory is in a different data segment */ #define KBDFLAGS 0x000000417L /* Address of keyboard status flags */ int shift(void) /* Returns 1 if either [or both] SHIFT key(s) are down, else 0 */ { return ( ( (*((char far *) KBDFLAGS) & 0x01)) || ( (*((char far *) KBDFLAGS) & 0x02)) ); } /************************************************************************** Screen I/O identifiers **************************************************************************/ /* Types for screen I/O: */ typedef unsigned char scrnbyte; typedef struct scrnchar { scrnbyte ch, at; /* Character and its attribute [on screen] */ }; static struct { unsigned long int size; /* How many bytes in buffer */ vid_addr buf; /* Holds pushed screens */ } SCRNBUF; /* Screen I/O vars */ static scrnbyte fgd = WHITE; /* Default foreground int */ static scrnbyte bgd = BLACK; /* Default background int */ static scrnbyte att = WHITE; /* Default attribute byte */ /* @@@ NOT USED static scrncoord curx=1, cury=1; /* Cursor coordinates for write() procedures */ */ #define COLOR 0xB8000000L #define MONO 0xB0000000L static vid_addr SCREEN = (vid_addr) COLOR; /* Default */ /************************************* PROCEDURES *************************************/ char *inttostr( x, w, s ) /* Assumes 1 <= w <= 9 */ long int x; unsigned short int w; char s[]; { char format[] = "%1li"; if (w > 9) s[0] = '\0'; else { format[1] = '0' + w; sprintf( s, format, x ); } return s; } /* NOTE: IF YOU DON'T WANT TO USE [OR FOR SOME REASON DON'T HAVE ACCESS TO] STDIO, YOU CAN USE THE FOLLOWING PROCEDURE: /* Sets 's' to character string representation of 'x' [assumes 's' is long enough to hold up to 7 chars ["-32767\0"]. Includes minus sign ('-') if negative, but omits plus sign ('+') if positive. Returns pointer to beginning of converted string. */ unsigned int i = 0; /* Index into 's' */ unsigned short int d; /* Each digit [takes values 0..9] */ unsigned int place; /* Counts down from 10000 to 1 */ int firstnonzero = 0; /* Set to 1 when first non-zero char found */ if (x<0) { x = -x; /* Make positive */ s[i++] = '-'; } /* Assume 'x' can be no larger than 32767 */ for (place=10000; place>=1; place /= 10) { if (x>=place) { /* Should digit be '1'..'9'? */ d = x / place; /* Result will be 1..9 */ x -= (d*place); /* Remainder */ s[i++] = '0' + d; /* ASCII '0'..'9' */ firstnonzero = 1; } else { /* Zero in this place, but add it only if a non-zero was previously printed: */ if ( (firstnonzero) || (place==1) ) s[i++] = '0'; else s[i++] = ' '; } } s[i] = '\0'; /* Terminate string properly */ return &(s[0]); */ void set_color( f, b ) /* Creates attribute byte 'att' from 'fgd' and 'bgd' colors */ int f, b; { /* Are we setting colors that have already been set? */ if ( (fgd != f) || (bgd != b) ) { /* Set foreground and background colors: */ fgd = f; bgd = b; /* Make attribute byte: */ att = (bgd << 4) + fgd; } } scrncoord xytoadr( x, y ) scrncoord x, y; /* Returns offset from (0,0), of location (x,y) on screen, according to screen width and height #defined */ { return (scrncoord) ( (y-1)* *CRT_COL + (x-1) ); } void writeat( x, y, s ) /* Write string 's' at screen location 'x','y' (1,1 = top left). Does NOT check whether string extends past last screen location. */ scrncoord x,y; char s[]; { int i; /* Index into 's' */ vid_addr scrn; /* Points to char/attribute */ scrn = (vid_addr) (SCREEN + xytoadr(x,y)); /* @@@ REPLACE IF USED: curx = x; cury = y; */ for ( i=0; s[i] != '\0'; i++ ) { scrn->ch = s[i]; /* Write character on screen */ (scrn++)->at = att; /* Write attribute and point to next char on screen */ /* @@@ REPLACE IF USED: curx++; */ } } void writeatcenter( y, s ) scrncoord y; char s[]; { writeat( (*CRT_COL-strlen(s))/2, y, s ); } void writeatch( x, y, c ) /* Write character 'c' at screen location 'x','y' (1,1 = top left). Note: c must be passed as 'c', NOT "c", because it is a char. */ scrncoord x,y; char c; { char s[2]; s[0] = c; s[1] = '\0'; writeat( x, y, s ); } /* NOT USED void writestr( s ) /* Writes string 's' at (curx,cury) */ char s[]; { writeat( curx, cury, s ); } void writech( c ) /* Writes char 'c' at (curx,cury) */ unsigned char c; { writeatch( curx, cury, c ); } */ void fillarea( x1, y1, x2, y2, f, b, c ) /* Fills area of screen bounded by (x1,y1) and (x2,y2) [offset from (1,1)] with char 'c' using foreground and background colors 'f' and 'b' */ scrncoord x1, y1, x2, y2; int f, b; char c; { scrncoord x, y; /* Local row, col counters */ set_color( f, b ); for ( y=y1; y<=y2; y++ ) { /* Fill all requested rows */ for ( x=x1; x<=x2; x++ ) { /* Fill all requested columns of current row */ writeatch( x, y, c ); } } } void clearscr( f, b ) int f, b; { fillarea( 1, 1, *CRT_COL, maxrow(), f, b, ' ' ); } void pushscrn(void) /* Saves text screen in buffer. Calls are NOT stacked; popscrn() must be called before next call to pushscrn() or previous screen will be lost and its memory will be 'leaked'. */ { vid_addr scrn = (vid_addr) (SCREEN); /* Points to char/attribute */ unsigned long int i; SCRNBUF.size=((((unsigned long int)(*CRT_COL)) * ((unsigned long int)maxrow()))); if ( (SCRNBUF.buf=(vid_addr) farmalloc(SCRNBUF.size*sizeof(struct scrnchar))) == NULL ) { fprintf(stderr, "pushscrn(): Unable to allocate enough memory for buffer.\n" ); exit(1); } /* Xfer buf to screen: */ for (i=0; ich; } void peekcolor( x,y, f,b ) scrncoord x,y; int *f, *b; { vid_addr scrn = (vid_addr) (SCREEN + xytoadr(x,y)); *b = scrn->at >> 4; /* Should become '0000nnnn' */ *f = scrn->at & 0x0F; } /*************************************************************************/ int lotus( x, y, f1, b1, f2, b2, h_v, s ) scrncoord x, y; int f1, b1, f2, b2; int h_v; char s[]; /* At screen location (x,y), draw words from 's' either horizontally or vertically, depending on 'h_v', in colors 'f1', 'b1', letting user pick one with arrow keys; highlighted choice is drawn in 'f2', 'b2'; returns which one picked [0=error, 1=first, 2=second, etc.]. If ESCape hit, returns -1. Words in 's' are separated by tabs ['\t']. Assumes no leading and trailing TABs. */ { struct { scrncoord x,y; /* Screen coords of this menu item */ char choice[80]; } mnu[20]; /* 'mnu' is an array of up to 20 menu items, each < 80 chars */ int i=0,j=0,k=0; /* Indices into s, mnu, and mnu[k].choice, respectively */ scrncoord a=x,b=y; /* Coords of each menu choice */ unsigned int ch; /* Keystroke */ int update = 0; /* Whether screen needs updating after keypress */ /* First menu choice is at x,y: */ mnu[0].x = a; mnu[0].y = b; /* Parse 's' into local array: */ for ( i=0; s[i]!='\0'; i++ ) { if (s[i]=='\t') { /* New word/menu choice */ /* Seal off this string and point to next: */ mnu[k++].choice[j] = '\0'; j=0; /* Start at first char of new string */ /* Point to next char in 's' [assumed not to be '\t']: */ i++; if (h_v) a++; else b++; /* Set coords of next menu choice: */ mnu[k].x = a; mnu[k].y = b; } mnu[k].choice[j++] = s[i]; if (h_v) a++; } mnu[k].choice[j] = '\0'; /* Seal off last string */ /* 'k' now contains number of menu choices */ /* Display menu: */ set_color( f1, b1 ); for (i=0; i<=k; i++) { writeat( mnu[i].x, mnu[i].y, mnu[i].choice ); } /* Write first menu choice in selected colors: */ set_color( f2, b2 ); writeat( mnu[0].x, mnu[0].y, mnu[0].choice ); /* Let user select choice: */ i = 0; do { if ((ch=keyhit())==ESCKEY) return (-1); switch(ch) { case UPKEY: case LEFTKEY: j = i; /* Save old index */ if (--i < 0) i=k; update = 1; break; case DOWNKEY: case RIGHTKEY: j = i; /* Save old index */ if (++i > k) i=0; update = 1; break; default: update = 0; break; } if (update) { set_color( f2, b2 ); writeat( mnu[i].x, mnu[i].y, mnu[i].choice ); set_color( f1, b1 ); writeat( mnu[j].x, mnu[j].y, mnu[j].choice ); } } while (ch != ENTERKEY); return i; } /*************************************************************************/ int keyhit(void) { int ch; if ((ch=getch()) == NULL) /* Assumes that extended key code is next char returned, and that getch() will NOT wait for a key [i.e. key will be available immediately]. */ return (getch()+EXTENDED); else return ch; } /*************************************************************************/ /* NOTE: YOUR MAIN MODULE MUST CONTAIN THIS LINE: */ void initkmvid(void) { SCREEN = ((*CRT_MODE==7) ? (vid_addr) MONO : (vid_addr) COLOR ); }