aboutsummaryrefslogtreecommitdiffstats
path: root/graphics.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2003-03-24 16:31:19 +0000
committerH. Peter Anvin <hpa@zytor.com>2003-03-24 16:31:19 +0000
commit1128ad360b5cccb1b82de092505e5ca1c4dbed8d (patch)
tree89c7ebe649b47fa1b901872d646a2183df023c8d /graphics.c
downloadgrv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.gz
grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.xz
grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.zip
Port of "gršvning" to C/SDL, started 2003-03-22
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/graphics.c b/graphics.c
new file mode 100644
index 0000000..155d628
--- /dev/null
+++ b/graphics.c
@@ -0,0 +1,409 @@
+/*
+ * graphics.c
+ *
+ * Graphics function emulation
+ * This pretty much attempts to emulate the QuickBasic-style text screen
+ * in 25x40 or 25x80 mode with no scrolling...
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include "SDL.h"
+#include "grv.h"
+#include "graphics.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define TS_WIDTH 80
+#define TS_HEIGHT 25
+
+#define FONT_XSIZE 8
+#define FONT_YSIZE 14
+
+#define PX_WIDTH (TS_WIDTH*FONT_XSIZE)
+#define PX_HEIGHT (TS_HEIGHT*FONT_YSIZE)
+
+typedef uint8_t font_t;
+extern font_t grv_font[256][FONT_YSIZE];
+
+#define NCOLORS 16
+
+static uint32_t colors[NCOLORS];
+
+static struct rgba { uint8_t a, r, g, b; } rgbcolors[NCOLORS] = {
+ {0x00,0x00,0x00,0x00}, /* black */
+ {0x00,0x00,0x00,0xaa}, /* dark blue */
+ {0x00,0x00,0xaa,0x00}, /* dark green */
+ {0x00,0x00,0xaa,0xaa}, /* dark cyan */
+ {0x00,0xaa,0x00,0x00}, /* dark red */
+ {0x00,0xaa,0x00,0xaa}, /* dark purple */
+ {0x00,0xaa,0x55,0x00}, /* brown */
+ {0x00,0xaa,0xaa,0xaa}, /* light grey */
+ {0x00,0x55,0x55,0x55}, /* dark grey */
+ {0x00,0x55,0x55,0xff}, /* light blue */
+ {0x00,0x55,0xff,0x55}, /* light green */
+ {0x00,0x55,0xff,0xff}, /* light cyan */
+ {0x00,0xff,0x55,0x55}, /* light red */
+ {0x00,0xff,0x55,0xff}, /* light purple */
+ {0x00,0xff,0xff,0x55}, /* yellow */
+ {0x00,0xff,0xff,0xff}, /* white */
+};
+
+struct screen_char {
+ uint8_t sym; /* Symbol code */
+ uint8_t attr; /* Attribute code */
+};
+
+static struct screen_char screendata[TS_HEIGHT][TS_WIDTH];
+
+static struct current_screen {
+ int x;
+ int y;
+ uint8_t attr;
+ uint8_t dwidth; /* Double width characters, must be 0 or 1 */
+} current = {0, 0, 7, 1}; /* Current screen parameters */
+
+static SDL_Surface *rscreen;
+static volatile uint8_t blink_mask = 0;
+
+/*
+ * Set the attribute: fg, bg
+ * For compatiblity's sake the flashing attribute is in fg, not bg
+ */
+void color(int fg, int bg)
+{
+ current.attr = (fg & 15) | ((bg & 7) << 4) | ((fg & 16) << 3);
+}
+
+/*
+ * Set the cursor position (1-based for compatiblity)
+ */
+void locate(int r, int c)
+{
+ current.y = r-1;
+ current.x = (c-1) << current.dwidth;
+}
+
+/*
+ * Get the symbol at a specific screen coordinate
+ */
+int screen(int r, int c)
+{
+ return screendata[r-1][(c-1) << current.dwidth].sym;
+}
+
+/*
+ * Get the attribute at a specific screen coordinate
+ */
+int screen1(int r, int c)
+{
+ return screendata[r-1][(c-1) << current.dwidth].attr;
+}
+
+/*
+ * Prepare screen for modification
+ */
+static void lock_screen(void)
+{
+ SDL_LockSurface(rscreen);
+}
+
+/*
+ * Update the on-screen structure to match the screendata[]
+ * for character (tx,ty), but don't refresh the rectangle just
+ * yet...
+ *
+ * tx,ty is zero-based and must be adjusted for doublewidth mode
+ */
+static void put_screen(int tx, int ty)
+{
+ font_t *fontp;
+ font_t v;
+ uint32_t *pixelp, fgp, bgp;
+ int x, y, z;
+ int attr;
+ int pxwid = current.dwidth + 1;
+ int bmask = blink_mask;
+
+ attr = screendata[ty][tx].attr;
+ fontp = grv_font[screendata[ty][tx].sym];
+
+ bgp = colors[(attr >> 4) & 7];
+ fgp = (bmask & attr) ? bgp : colors[attr & 15];
+
+ pixelp = ((uint32_t *) rscreen->pixels) +
+ ty*(FONT_XSIZE*FONT_YSIZE*TS_WIDTH) +
+ tx*FONT_XSIZE;
+
+ for ( y = 0 ; y < FONT_YSIZE ; y++ ) {
+ v = fontp[y];
+ for ( x = 0 ; x < FONT_XSIZE ; x++ ) {
+ for ( z = 0 ; z < pxwid ; z++ )
+ *pixelp++ = v & ((font_t)1 << (FONT_XSIZE-1)) ? fgp : bgp;
+ v <<= 1;
+ }
+ pixelp += PX_WIDTH-pxwid*FONT_XSIZE;
+ }
+}
+
+/*
+ * This routine switches the blink status, then goes around the screen
+ * and updates all characters which has the blink attribute set.
+ */
+void update_blink(void)
+{
+ int x, y, xs;
+ int gx, gy, gw, gh;
+ SDL_Rect rects[TS_WIDTH*TS_HEIGHT]; /* Absolute maximum needed */
+ SDL_Rect *rect = rects-1;
+ int nrects = 0;
+
+ blink_mask ^= 0x80;
+
+ xs = current.dwidth+1;
+
+ SDL_LockSurface(rscreen);
+
+ gw = FONT_XSIZE << current.dwidth;
+ gh = FONT_YSIZE;
+
+ for ( y = 0, gy = 0 ; y < TS_HEIGHT ; y++, gy += gh ) {
+ for ( x = 0, gx = 0 ; x < TS_WIDTH ; x += xs, gx += gw ) {
+ if ( screendata[y][x].attr & 0x80 ) {
+ put_screen(x,y);
+ if ( !nrects || rect->y != gy || rect->x+rect->w != gx ) {
+ nrects++;
+ rect++;
+ rect->x = gx; rect->y = gy;
+ rect->w = gw; rect->h = gh;
+ } else {
+ rect->w += gw;
+ }
+ }
+ }
+ }
+
+ SDL_UnlockSurface(rscreen);
+
+ if ( nrects )
+ SDL_UpdateRects(rscreen, nrects, rects);
+}
+
+/*
+ * Refresh rectangle and unlock screen
+ * Coordinates are inclusive and must be adjusted for double-pixel mode
+ */
+static void update_screen(int x0, int y0, int x1, int y1)
+{
+ SDL_UnlockSurface(rscreen);
+
+ SDL_UpdateRect(rscreen, x0*FONT_XSIZE, y0*FONT_YSIZE,
+ (x1-x0+1)*FONT_XSIZE, (y1-y0+1)*FONT_YSIZE);
+}
+
+/*
+ * Display a string of characters and advance cursor
+ */
+void print(const char *str)
+{
+ const unsigned char *p = (const unsigned char *)str;
+ unsigned char ch;
+ int x = current.x;
+ int y = current.y;
+ uint8_t attr = current.attr;
+ int x0 = x; int y0 = y;
+ int x1 = x; int y1 = y;
+ int xwid;
+
+ if ( !*str )
+ return;
+
+ xwid = current.dwidth+1;
+
+ lock_screen();
+
+ while ( (ch = *p++) ) {
+ screendata[y][x].sym = ch;
+ screendata[y][x].attr = attr;
+ put_screen(x,y);
+ x0 = min(x0,x); y0 = min(y0,y);
+ x1 = max(x1,x+xwid-1); y1 = max(y1,y);
+ x += xwid;
+ if ( x >= TS_WIDTH ) {
+ x = 0; y++;
+ if ( y >= TS_HEIGHT ) {
+ y = 0;
+ }
+ }
+ }
+ update_screen(x0,y0,x1,y1);
+
+ current.x = x; current.y = y;
+}
+
+/*
+ * Same except with printf-style formatting
+ */
+void gprintf(const char *fmt, ...)
+{
+ unsigned char buffer[4096];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ print(buffer);
+}
+
+/*
+ * locate() and print() as a combined function
+ */
+void lprint(int r, int c, const char *str)
+{
+ locate(r,c);
+ print(str);
+}
+
+/*
+ * locate() and gprintf() as a combined function
+ */
+void lprintf(int r, int c, const char *fmt, ...)
+{
+ unsigned char buffer[4096];
+ va_list ap;
+
+ locate(r,c);
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ print(buffer);
+}
+
+/*
+ * Set 40/80 character mode. Unlike hardware this doesn't change the
+ * text currently on the screen, but funny things can happen; cls() is
+ * highly recommended.
+ */
+void width(int wid)
+{
+ switch(wid) {
+ case 40:
+ current.dwidth = 1;
+ current.x &= ~1;
+ break;
+ case 80:
+ current.dwidth = 0;
+ break;
+ default:
+ abort();
+ }
+}
+
+/*
+ * Clear the screen, set all characters to ' ' and set all pixels to the
+ * current background
+ */
+void cls(void)
+{
+ int i;
+ struct screen_char *scp = &screendata[0][0];
+ struct screen_char empty;
+ uint32_t pixelval;
+ SDL_Rect rect;
+
+ lock_screen();
+
+ empty.sym = ' ';
+ empty.attr = current.attr;
+
+ pixelval = colors[(current.attr >> 4) & 7];
+
+ for ( i = TS_WIDTH*TS_HEIGHT ; i ; i-- )
+ *scp++ = empty;
+
+ rect.x = rect.y = 0;
+ rect.w = PX_WIDTH;
+ rect.h = PX_HEIGHT;
+
+ SDL_FillRect(rscreen, &rect, pixelval);
+
+ update_screen(0, 0, TS_WIDTH-1, TS_HEIGHT-1);
+
+ current.x = current.y = 0;
+}
+
+
+/*
+ * Save the screen as well as "current" in a malloc'd buffer
+ */
+struct saved_screen
+{
+ struct current_screen current;
+ struct screen_char screendata[TS_HEIGHT][TS_WIDTH];
+};
+
+struct saved_screen *save_screen(void)
+{
+ struct saved_screen *save = malloc(sizeof(struct saved_screen));
+
+ if ( !save )
+ return NULL;
+
+ memcpy(&save->current, &current, sizeof(current));
+ memcpy(&save->screendata, &screendata, sizeof(screendata));
+
+ return save;
+}
+
+void restore_screen(struct saved_screen *save)
+{
+ int x, y, xs;
+
+ memcpy(&current, &save->current, sizeof(current));
+ memcpy(&screendata, &save->screendata, sizeof(screendata));
+
+ xs = current.dwidth+1;
+
+ lock_screen();
+
+ for ( y = 0 ; y < TS_HEIGHT ; y++ ) {
+ for ( x = 0 ; x < TS_WIDTH ; x += xs ) {
+ put_screen(x,y);
+ }
+ }
+
+ update_screen(0, 0, TS_WIDTH-1, TS_HEIGHT-1);
+}
+
+/*
+ * Initialize SDL and the data structures
+ */
+int screen_init(void)
+{
+ int i;
+
+ if ( SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO) )
+ return -1;
+
+ if ( !(rscreen = SDL_SetVideoMode(PX_WIDTH, PX_HEIGHT, 32, SDL_SWSURFACE)) ) {
+ SDL_Quit();
+ return -1;
+ }
+
+ atexit(SDL_Quit);
+
+ /* Convert colors to preferred machine representation */
+ for ( i = 0 ; i < NCOLORS ; i++ ) {
+ colors[i] = SDL_MapRGB(rscreen->format,
+ rgbcolors[i].r, rgbcolors[i].g, rgbcolors[i].b);
+ }
+
+ cls();
+
+ SDL_AddTimer(400, post_periodic, (void *)event_blink);
+
+ return 0;
+}