quakeforge/qw/source/vid_ggi.c

694 lines
15 KiB
C

/*
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 <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <ggi/ggi.h>
#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 <mode>\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)
{
}