/* vid_ggi.c general LibGGI video driver Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 1999 Marcus Sundberg [mackan@stacken.kth.se] This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA $Id$ */ #define _BSD #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include "client.h" #include "cmd.h" #include "console.h" #include "cvar.h" #include "d_local.h" #include "draw.h" #include "host.h" #include "qargs.h" #include "qendian.h" #include "sys.h" extern viddef_t vid; // global video state unsigned short d_8to16table[256]; /* Unused */ int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; byte *VGA_pagebase; cvar_t *m_filter; cvar_t *_windowed_mouse; #define NUM_STDBUTTONS 3 #define NUM_BUTTONS 10 static ggi_visual_t ggivis = NULL; static ggi_mode mode; static const ggi_directbuffer *dbuf1 = NULL, *dbuf2 = NULL; static uint8 *drawptr = NULL; static void *frameptr[2] = { NULL, NULL }; static void *oneline = NULL; static void *palette = NULL; static int curframe = 0; static int realwidth, realheight; static int doublebuffer; static int scale; static int stride, drawstride; static int pixelsize; static int usedbuf, havedbuf; int VID_options_items = 1; static void do_scale8 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 2; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pix1 = src[i++], pix2 = src[i++]; #ifdef GGI_LITTLE_ENDIAN *((uint32 *) (dest + stride)) = *((uint32 *) dest) = (pix1 | (pix1 << 8) | (pix2 << 16) | (pix2 << 24)); #else *((uint32 *) (dest + stride)) = *((uint32 *) dest) = (pix2 | (pix2 << 8) | (pix1 << 16) | (pix1 << 24)); #endif dest += 4; } dest += destinc; src += xsize; } } static void do_scale16 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 4; uint16 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; *((uint32 *) (dest + stride)) = *((uint32 *) dest) = pixel | (pixel << 16); dest += 4; } dest += destinc; src += xsize; } } static void do_scale32 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j, destinc = stride * 2 - xsize * 8; uint32 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; *((uint32 *) (dest + stride)) = *((uint32 *) (dest)) = pixel; dest += 4; *((uint32 *) (dest + stride)) = *((uint32 *) (dest)) = pixel; dest += 4; } dest += destinc; src += xsize; } } static void do_copy8 (int xsize, int ysize, uint8 * dest, uint8 * src) { int i, j; uint8 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; i++) { dest[i] = palptr[src[i]]; } dest += stride; src += xsize; } } static void do_copy16 (int xsize, int ysize, void *destptr, uint8 * src) { int i, j, destinc = (stride / 2 - xsize) / 2; uint16 *palptr = palette; uint32 *dest = destptr; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; /* i is incremented below */ ) { register uint32 pixel = palptr[src[i++]]; #ifdef GGI_LITTLE_ENDIAN *(dest++) = pixel | (palptr[src[i++]] << 16); #else *(dest++) = (palptr[src[i++]] << 16) | pixel; #endif } dest += destinc; src += xsize; } } static void do_copy32 (int xsize, int ysize, uint32 * dest, uint8 * src) { int i, j, destinc = stride / 4; uint32 *palptr = palette; for (j = 0; j < ysize; j++) { for (i = 0; i < xsize; i++) { dest[i] = palptr[src[i]]; } dest += destinc; src += xsize; } } void ResetFrameBuffer (void) { int tbuffersize, tcachesize; void *vid_surfcache; // Calculate the sizes we want first tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); tcachesize = D_SurfaceCacheForRes (vid.width, vid.height); // Free the old z-buffer if (d_pzbuffer) { free (d_pzbuffer); d_pzbuffer = NULL; } // Free the old surface cache vid_surfcache = D_SurfaceCacheAddress (); if (vid_surfcache) { D_FlushCaches (); free (vid_surfcache); vid_surfcache = NULL; } // Allocate the new z-buffer d_pzbuffer = calloc (tbuffersize, 1); if (!d_pzbuffer) { Sys_Error ("Not enough memory for video mode\n"); } // Allocate the new surface cache; free the z-buffer if we fail vid_surfcache = calloc (tcachesize, 1); if (!vid_surfcache) { free (d_pzbuffer); d_pzbuffer = NULL; Sys_Error ("Not enough memory for video mode\n"); } D_InitCaches (vid_surfcache, tcachesize); } // Called at startup to set up translation tables, takes 256 8 bit RGB values // the palette data will go away after the call, so it must be copied off if // the video driver will need it again void VID_Init (unsigned char *pal) { int pnum; vid.width = GGI_AUTO; vid.height = GGI_AUTO; srandom (getpid ()); if (ggiInit () < 0) { Sys_Error ("VID: Unable to init LibGGI\n"); } ggivis = ggiOpen (NULL); if (!ggivis) { Sys_Error ("VID: Unable to open default visual\n"); } /* Go into async mode */ ggiSetFlags (ggivis, GGIFLAG_ASYNC); VID_GetWindowSize (320, 200); scale = COM_CheckParm ("-scale"); /* specify a LibGGI mode */ if ((pnum = COM_CheckParm ("-ggimode"))) { if (pnum >= com_argc - 1) Sys_Error ("VID: -ggimode \n"); ggiParseMode (com_argv[pnum + 1], &mode); } else { /* This will give the default mode */ ggiParseMode ("", &mode); /* Now put in any parameters given above */ mode.visible.x = vid.width; mode.visible.y = vid.height; } if (scale) { mode.visible.x *= 2; mode.visible.y *= 2; } /* We prefer 8 bit mode unless otherwise specified */ if (mode.graphtype == GT_AUTO) mode.graphtype = GT_8BIT; /* We want double buffering if possible */ if (mode.frames == GGI_AUTO) { ggi_mode tmpmode = mode; tmpmode.frames = 2; if (ggiCheckMode (ggivis, &tmpmode) == 0) { mode = tmpmode; } else { tmpmode.frames = 2; if (ggiCheckMode (ggivis, &tmpmode) == 0) { mode = tmpmode; } } } if (ggiSetMode (ggivis, &mode) != 0) { /* Try again with suggested mode */ if (ggiSetMode (ggivis, &mode) != 0) { Sys_Error ("VID: LibGGI can't set any modes!\n"); } } /* Pixel size must be 1, 2 or 4 bytes */ if (GT_SIZE (mode.graphtype) != 8 && GT_SIZE (mode.graphtype) != 16 && GT_SIZE (mode.graphtype) != 32) { if (GT_SIZE (mode.graphtype) == 24) { Sys_Error ("VID: 24 bits per pixel not supported - try using the palemu target.\n"); } else { Sys_Error ("VID: %d bits per pixel not supported by GGI Quake.\n", GT_SIZE (mode.graphtype)); } } realwidth = mode.visible.x; realheight = mode.visible.y; if (scale) { vid.width = realwidth / 2; vid.height = realheight / 2; } else { vid.width = realwidth; vid.height = realheight; } Con_CheckResize (); // Now that we have a window size, fix console if (mode.frames >= 2) doublebuffer = 1; else doublebuffer = 0; pixelsize = (GT_SIZE (mode.graphtype) + 7) / 8; if (mode.graphtype != GT_8BIT) { if ((palette = malloc (pixelsize * 256)) == NULL) { Sys_Error ("VID: Unable to allocate palette table\n"); } } VID_SetPalette (pal); usedbuf = havedbuf = 0; drawstride = vid.width; stride = realwidth * pixelsize; if ((dbuf1 = ggiDBGetBuffer (ggivis, 0)) != NULL && (dbuf1->type & GGI_DB_SIMPLE_PLB)) { havedbuf = 1; stride = dbuf1->buffer.plb.stride; if (doublebuffer) { if ((dbuf2 = ggiDBGetBuffer (ggivis, 1)) == NULL || !(dbuf2->type & GGI_DB_SIMPLE_PLB)) { /* Only one DB? No double buffering then */ doublebuffer = 0; } } if (doublebuffer) { fprintf (stderr, "VID: Got two DirectBuffers\n"); } else { fprintf (stderr, "VID: Got one DirectBuffer\n"); } if (doublebuffer && !scale && !palette) { usedbuf = 1; drawstride = stride; frameptr[0] = dbuf1->write; if (doublebuffer) { frameptr[1] = dbuf2->write; } else { frameptr[1] = frameptr[0]; } drawptr = frameptr[0]; fprintf (stderr, "VID: Drawing into DirectBuffer\n"); } } if (!usedbuf) { if ((drawptr = malloc (vid.width * vid.height)) == NULL) { Sys_Error ("VID: Unable to allocate draw buffer\n"); } if (!havedbuf && (scale || palette)) { int linesize = pixelsize * realwidth; if (scale) linesize *= 4; if ((oneline = malloc (linesize)) == NULL) { Sys_Error ("VID: Unable to allocate line buffer\n"); } } fprintf (stderr, "VID: Drawing into offscreen memory\n"); } ResetFrameBuffer (); curframe = 0; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.numpages = doublebuffer ? 2 : 1; vid.colormap = host_colormap; vid.buffer = drawptr; vid.rowbytes = drawstride; vid.direct = drawptr; vid.conbuffer = vid.buffer; vid.conrowbytes = vid.rowbytes; vid.conwidth = vid.width; vid.conheight = vid.height; vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); } void VID_ShiftPalette (unsigned char *pal) { VID_SetPalette (pal); } void VID_SetPalette (unsigned char *pal) { int i; ggi_color colors[256]; for (i = 0; i < 256; i++) { colors[i].r = pal[i * 3] * 257; colors[i].g = pal[i * 3 + 1] * 257; colors[i].b = pal[i * 3 + 2] * 257; } if (palette) { ggiPackColors (ggivis, palette, colors, 256); } else { ggiSetPalette (ggivis, 0, 256, colors); } } // Called at shutdown void VID_Shutdown (void) { Con_Printf ("VID_Shutdown\n"); if (!usedbuf) { free (drawptr); drawptr = NULL; } if (oneline) { free (oneline); oneline = NULL; } if (palette) { free (palette); palette = NULL; } if (ggivis) { ggiClose (ggivis); ggivis = NULL; } ggiExit (); } // flushes the given rectangles from the view buffer to the screen void VID_Update (vrect_t *rects) { int height = 0; #if 0 // if the window changes dimension, skip this frame if (config_notify) { fprintf (stderr, "config notify\n"); config_notify = 0; vid.width = config_notify_width & ~7; vid.height = config_notify_height; if (doShm) ResetSharedFrameBuffers (); else ResetFrameBuffer (); vid.rowbytes = x_framebuffer[0]->bytes_per_line; vid.buffer = x_framebuffer[curframe]->data; vid.conbuffer = vid.buffer; vid.conwidth = vid.width; vid.conheight = vid.height; vid.conrowbytes = vid.rowbytes; vid.recalc_refdef = 1; // force a surface cache flush Con_CheckResize (); Con_Clear_f (); return; } // force full update if not 8bit if (x_visinfo->depth != 8) { extern int scr_fullupdate; scr_fullupdate = 0; } #endif while (rects) { int y = rects->y + rects->height; if (y > height) height = y; rects = rects->pnext; } if (!usedbuf) { int i; if (havedbuf) { if (ggiResourceAcquire (dbuf1->resource, GGI_ACTYPE_WRITE) != 0 || (doublebuffer ? ggiResourceAcquire (dbuf2->resource, GGI_ACTYPE_WRITE) != 0 : 0)) { ggiPanic ("Unable to acquire DirectBuffer!\n"); } /* ->write is allowed to change at acquire time */ frameptr[0] = dbuf1->write; if (doublebuffer) { frameptr[1] = dbuf2->write; } else { frameptr[1] = frameptr[0]; } } if (scale) { switch (pixelsize) { case 1: if (havedbuf) { do_scale8 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale8 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; case 2: if (havedbuf) { do_scale16 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale16 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; case 4: if (havedbuf) { do_scale32 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_scale32 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); buf += vid.width; } } break; } } else if (palette) { switch (pixelsize) { case 1: if (havedbuf) { do_copy8 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy8 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; case 2: if (havedbuf) { do_copy16 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy16 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; case 4: if (havedbuf) { do_copy32 (vid.width, height, frameptr[curframe], drawptr); } else { uint8 *buf = drawptr; for (i = 0; i < height; i++) { do_copy32 (vid.width, 1, oneline, buf); ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); buf += vid.width; } } break; } } else { ggiPutBox (ggivis, 0, 0, vid.width, height, drawptr); } if (havedbuf) { ggiResourceRelease (dbuf1->resource); if (doublebuffer) { ggiResourceRelease (dbuf2->resource); } } } if (doublebuffer) { ggiSetDisplayFrame (ggivis, curframe); curframe = !curframe; if (usedbuf) { vid.buffer = vid.conbuffer = vid.direct = drawptr = frameptr[curframe]; } ggiSetWriteFrame (ggivis, curframe); } #if 0 if (GT_SIZE (mode.graphtype) == 16) { do_copy16 (vid.width, height, (uint16 *) frameptr, drawptr); } else if (GT_SIZE (mode.graphtype) == 32) { do_copy32 (vid.width, height, (uint32 *) frameptr, drawptr); } #endif ggiFlush (ggivis); } void D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) { // direct drawing of the "accessing disk" icon isn't supported under Linux } void D_EndDirectRect (int x, int y, int width, int height) { // direct drawing of the "accessing disk" icon isn't supported under Linux } void VID_Init_Cvars (void) { } void VID_LockBuffer (void) { } void VID_UnlockBuffer (void) { } void VID_SetCaption (char *text) { }