/* Framebuffer Graphics Libary for Linux, Copyright 1993 Harm Hanemaayer */
/* grlib.c      Main module */


#include <stdlib.h>
#include <stdio.h>
#include <vga.h>
#include "inlstring.h"		/* include inline string operations */

#include "vgagl.h"
#include "def.h"
#include "driver.h"


/* Global variables */

#ifdef DLL_CONTEXT_SHADOW

/* The current context variable is shadowed in a read-only variable for */
/* external use. */

GraphicsContext __currentcontext;	/* Internal current context. */
GraphicsContext currentcontext;	/* Copy for external use. */

#else

GraphicsContext currentcontext;

#endif

void (*__svgalib_nonaccel_fillbox)(int, int, int, int, int);
static int screenoffset = 0;	/* Used by copy(box)toscreen. */


/* Framebuffer function pointers */

static framebufferfunctions ff8 =
{
    __svgalib_driver8_setpixel,
    __svgalib_driver8_getpixel,
    __svgalib_driver8_hline,
    __svgalib_driver8_fillbox,
    __svgalib_driver8_putbox,
    __svgalib_driver8_getbox,
    __svgalib_driver8_putboxmask,
    __svgalib_driver8_putboxpart,
    __svgalib_driver8_getboxpart,
    __svgalib_driver8_copybox
};

static framebufferfunctions ff16 =
{
    __svgalib_driver16_setpixel,
    __svgalib_driver16_getpixel,
    __svgalib_driver16_hline,
    __svgalib_driver16_fillbox,
    __svgalib_driver16_putbox,
    __svgalib_driver16_getbox,
    __svgalib_driver16_putboxmask,
    __svgalib_driver16_putboxpart,
    __svgalib_driver16_getboxpart,
    __svgalib_driver16_copybox
};

static framebufferfunctions ff24 =
{
    __svgalib_driver24_setpixel,
    __svgalib_driver24_getpixel,
    __svgalib_driver24_hline,
    __svgalib_driver24_fillbox,
    __svgalib_driver24_putbox,
    __svgalib_driver24_getbox,
    __svgalib_driver24_putboxmask,
    __svgalib_driver24_putboxpart,
    __svgalib_driver24_getboxpart,
    __svgalib_driver24_copybox
};

static framebufferfunctions ff32 =
{
    __svgalib_driver32_setpixel,
    __svgalib_driver32_getpixel,
    __svgalib_driver32_hline,
    __svgalib_driver32_fillbox,
    __svgalib_driver32_putbox,
    __svgalib_driver32_getbox,
    __svgalib_driver32_putboxmask,
    __svgalib_driver32_putboxpart,
    __svgalib_driver32_getboxpart,
    __svgalib_driver32_copybox
};

static framebufferfunctions ff8paged =
{
    __svgalib_driver8p_setpixel,
    __svgalib_driver8p_getpixel,
    __svgalib_driver8p_hline,
    __svgalib_driver8p_fillbox,
    __svgalib_driver8p_putbox,
    __svgalib_driver8p_getbox,
    __svgalib_driver8p_putboxmask,
    __svgalib_driver8p_putboxpart,
    __svgalib_driver8p_getboxpart,
    __svgalib_driver8p_copybox
};

static framebufferfunctions ff16paged =
{
    __svgalib_driver16p_setpixel,
    __svgalib_driver16p_getpixel,
    __svgalib_driver16p_hline,
    __svgalib_driver16p_fillbox,
    __svgalib_driver16p_putbox,
    __svgalib_driver16p_getbox,
    __svgalib_driver16p_putboxmask,
    __svgalib_driver16p_putboxpart,
    __svgalib_driver16p_getboxpart,
    __svgalib_driver16p_copybox
};

static framebufferfunctions ff24paged =
{
    __svgalib_driver24p_setpixel,
    __svgalib_driver24p_getpixel,
    __svgalib_driver24p_hline,
    __svgalib_driver24p_fillbox,
    __svgalib_driver24p_putbox,
    __svgalib_driver24p_getbox,
    __svgalib_driver24p_putboxmask,
    __svgalib_driver24p_putboxpart,
    __svgalib_driver24p_getboxpart,
    __svgalib_driver24p_copybox
};

static framebufferfunctions ff32paged =
{
    __svgalib_driver32p_setpixel,
    __svgalib_driver32p_getpixel,
    __svgalib_driver32p_hline,
    __svgalib_driver32p_fillbox,
    __svgalib_driver32p_putbox,
    __svgalib_driver32p_getbox,
    __svgalib_driver32p_putboxmask,
    __svgalib_driver32p_putboxpart,
    __svgalib_driver32p_getboxpart,
    __svgalib_driver32p_copybox
};

static framebufferfunctions ffplanar256 =
{
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    __svgalib_driverplanar256_putbox,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
    (void *) __svgalib_driverplanar256_nothing,
};

#if 0				/* Not yet used */
static framebufferfunctions ffplanar16 =
{
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
    (void *) __svgalib_driverplanar16_nothing,
};
#endif


/* Initialization and graphics contexts */

#define SCREENSIZE(gc) ((gc).bytewidth * (gc).height)

static int colorbits(int c)
{
    switch (c) {
    default:
    case 256:
	return 8;
    case 32768:
	return 15;
    case 65536:
	return 16;
    case 256 * 65536:
	return 24;
    }
}

int gl_setcontextvga(int m)
{
    framebufferfunctions *ff;
    vga_modeinfo *modeinfo;
    int accelfuncs;
    if (!vga_hasmode(m))
	return -1;
    modeinfo = vga_getmodeinfo(m);
    /* Set graphics context */
    WIDTH = modeinfo->width;
    HEIGHT = modeinfo->height;
    BYTESPERPIXEL = modeinfo->bytesperpixel;
    COLORS = modeinfo->colors;
    BITSPERPIXEL = colorbits(COLORS);
    BYTEWIDTH = modeinfo->linewidth;
    VBUF = vga_getgraphmem();
    MODEFLAGS = 0;
    __clip = 0;
    ff = &(__currentcontext.ff);
    if (modeinfo->flags & IS_MODEX) {
	/* Pretend it's a regular (linear) context. */
	BYTESPERPIXEL = 1;
	BYTEWIDTH *= 4;
	MODETYPE = CONTEXT_MODEX;
	if (BYTEWIDTH * HEIGHT * 2 <= 256 * 1024)
	    MODEFLAGS |= MODEFLAG_PAGEFLIPPING_CAPABLE;
	if (BYTEWIDTH * HEIGHT * 3 <= 256 * 1024)
	    MODEFLAGS |= MODEFLAG_TRIPLEBUFFERING_CAPABLE;
	__currentcontext.ff = ffplanar256;
    } else if (modeinfo->colors == 16) {
	/* Pretend it's a regular one byte per pixel context. */
	BYTESPERPIXEL = 1;
	BYTEWIDTH *= 8;
	MODETYPE = CONTEXT_PLANAR16;
	if (BYTEWIDTH * HEIGHT <= 256 * 1024)
	    MODEFLAGS |= MODEFLAG_PAGEFLIPPING_CAPABLE;
	if (BYTEWIDTH * HEIGHT * 3 / 2 <= 256 * 1024)
	    MODEFLAGS |= MODEFLAG_TRIPLEBUFFERING_CAPABLE;
    } else if ((m == G320x200x256 && modeinfo->maxpixels <= 65536) ||
	       (modeinfo->flags & IS_LINEAR)
#if 1				/* svgalib doesn't VT-switch correctly with linear addressing. */
	       || ((modeinfo->flags & CAPABLE_LINEAR)
	/* Creepy. Try linear addressing only if the mode is set. */
	&& vga_getcurrentmode() == m && (vga_setlinearaddressing() != -1))
#endif
	) {
	/* No banking. */
	/* Get get the fb address in case we set linear addressing. */
	VBUF = vga_getgraphmem();
	MODETYPE = CONTEXT_LINEAR;
	if (modeinfo->maxpixels >= WIDTH * HEIGHT * 2)
	    MODEFLAGS |= MODEFLAG_PAGEFLIPPING_CAPABLE;
	if (modeinfo->maxpixels >= WIDTH * HEIGHT * 3)
	    MODEFLAGS |= MODEFLAG_TRIPLEBUFFERING_CAPABLE;
	switch (BYTESPERPIXEL) {
	case 1:
	    __currentcontext.ff = ff8;
	    break;
	case 2:
	    __currentcontext.ff = ff16;
	    break;
	case 3:
	    __currentcontext.ff = ff24;
	    break;
	case 4:
	    __currentcontext.ff = ff32;
	    break;
	}
	if (modeinfo->flags & RGB_MISORDERED)
	    MODEFLAGS |= MODEFLAG_32BPP_SHIFT8;
    } else {
	/* Banked mode. */
	MODETYPE = CONTEXT_PAGED;
	if (modeinfo->maxpixels >= WIDTH * HEIGHT * 2)
	    MODEFLAGS |= MODEFLAG_PAGEFLIPPING_CAPABLE;
	if (modeinfo->maxpixels >= WIDTH * HEIGHT * 3)
	    MODEFLAGS |= MODEFLAG_TRIPLEBUFFERING_CAPABLE;
	if ((modeinfo->startaddressrange & 0x1ffff) == 0x10000) {
	    /* This hack is required for 320x200x256 page flipping */
	    /* on Trident, which doesn't work with bank boundary */
	    /* within the second page. */
	    MODEFLAGS |= MODEFLAG_FLIPPAGE_BANKALIGNED;
	}
	switch (BYTESPERPIXEL) {
	case 1:
	    __currentcontext.ff = ff8paged;
	    break;
	case 2:
	    __currentcontext.ff = ff16paged;
	    break;
	case 3:
	    __currentcontext.ff = ff24paged;
	    break;
	case 4:
	    __currentcontext.ff = ff32paged;
	    break;
	}
	if (modeinfo->flags & RGB_MISORDERED)
	    MODEFLAGS |= MODEFLAG_32BPP_SHIFT8;
    }
    if (vga_getcurrentmode() == m) {
	accelfuncs = vga_ext_set(VGA_EXT_AVAILABLE, VGA_AVAIL_ACCEL);
	__svgalib_nonaccel_fillbox = __currentcontext.ff.driver_fillbox_func;
	if (accelfuncs & ACCELFLAG_FILLBOX)
	    __currentcontext.ff.driver_fillbox_func =
		__svgalib_driver8a_fillbox;
	if (accelfuncs & ACCELFLAG_SCREENCOPY)
	    __currentcontext.ff.driver_copybox_func =
		__svgalib_driver8a_copybox;
    }
#ifdef DLL_CONTEXT_SHADOW
    currentcontext = __currentcontext;
#endif
    return 0;
}

int gl_setcontextvgavirtual(int m)
{
    vga_modeinfo *modeinfo;
    if (!vga_hasmode(m))
	return -1;
    modeinfo = vga_getmodeinfo(m);
    /* Set graphics context */
    WIDTH = modeinfo->width;
    HEIGHT = modeinfo->height;
    if (modeinfo->flags & IS_MODEX) {
	/* Use a regular virtual screen for planar 256 color modes. */
	BYTESPERPIXEL = 1;
	BYTEWIDTH = modeinfo->linewidth * 4;
    } else if (modeinfo->colors == 16) {
	/* Use a regular one byte per pixel virtual screen for */
	/* planar 16 color modes. */
	BYTESPERPIXEL = 1;
	BYTEWIDTH = modeinfo->linewidth * 8;
    } else {
	BYTESPERPIXEL = modeinfo->bytesperpixel;
	BYTEWIDTH = modeinfo->linewidth;
    }
    COLORS = modeinfo->colors;
    BITSPERPIXEL = colorbits(COLORS);
    VBUF = malloc(SCREENSIZE(__currentcontext));
    MODETYPE = CONTEXT_VIRTUAL;
    MODEFLAGS = 0;
    __clip = 0;
    switch (BYTESPERPIXEL) {
    case 1:
	__currentcontext.ff = ff8;
	break;
    case 2:
	__currentcontext.ff = ff16;
	break;
    case 3:
	__currentcontext.ff = ff24;
	break;
    case 4:
	__currentcontext.ff = ff32;
	break;
    }
#ifdef DLL_CONTEXT_SHADOW
    currentcontext = __currentcontext;
#endif
    return 0;
}

void gl_setcontextvirtual(int w, int h, int bpp, int bitspp, void *v)
{
    WIDTH = w;
    HEIGHT = h;
    BYTESPERPIXEL = bpp;
    BITSPERPIXEL = bitspp;
    COLORS = 1 << bitspp;
    BYTEWIDTH = WIDTH * BYTESPERPIXEL;
    VBUF = v;
    MODETYPE = CONTEXT_VIRTUAL;
    MODEFLAGS = 0;
    switch (BYTESPERPIXEL) {
    case 1:
	__currentcontext.ff = ff8;
	break;
    case 2:
	__currentcontext.ff = ff16;
	break;
    case 3:
	__currentcontext.ff = ff24;
	break;
    case 4:
	__currentcontext.ff = ff32;
	break;
    }
    __clip = 0;
#ifdef DLL_CONTEXT_SHADOW
    currentcontext = __currentcontext;
#endif
}

GraphicsContext *
 gl_allocatecontext()
{
    return malloc(sizeof(GraphicsContext));
}

void gl_setcontext(GraphicsContext * gc)
{
    __currentcontext = *gc;
#ifdef DLL_CONTEXT_SHADOW
    currentcontext = *gc;
#endif
}

void gl_getcontext(GraphicsContext * gc)
{
    *gc = __currentcontext;
}

void gl_freecontext(GraphicsContext * gc)
{
    if (gc->modetype == CONTEXT_VIRTUAL)
	free(gc->vbuf);
}

void gl_setcontextwidth(int w)
{
    __currentcontext.width = currentcontext.width = w;
    __currentcontext.bytewidth = currentcontext.bytewidth =
	w * BYTESPERPIXEL;
}

void gl_setcontextheight(int h)
{
    __currentcontext.height = currentcontext.height = h;
}


/* Clipping */

void gl_setclippingwindow(int x1, int y1, int x2, int y2)
{
    __clip = 1;
    __clipx1 = x1;
    __clipy1 = y1;
    __clipx2 = x2;
    __clipy2 = y2;
}

void gl_enableclipping()
{
    __clip = 1;
    __clipx1 = 0;
    __clipy1 = 0;
    __clipx2 = WIDTH - 1;
    __clipy2 = HEIGHT - 1;
}

void gl_disableclipping()
{
    __clip = 0;
}


/* Primitive functions */

void gl_setpixel(int x, int y, int c)
{
    if (__clip && outside(x, y))
	return;
    setpixel(x, y, c);
}

int gl_getpixel(int x, int y)
{
    if (__clip && outside(x, y))
	return -1;
    return getpixel(x, y);
}

void gl_hline(int x1, int y, int x2, int c)
{
    if (__clip) {
	if (y_outside(y))
	    return;
	clipxleft(x1);
	clipxright(x2);
    }
    if (x1 > x2)
	return;
    hline(x1, y, x2, c);
}

#define ADJUSTBITMAPBOX() \
	nw = w; nh = h; nx = x; ny = y;				\
	if (nx + nw < __clipx1 || nx > __clipx2)		\
		return;						\
	if (ny + nh < __clipy1 || ny > __clipy2)		\
		return;						\
	if (nx < __clipx1) {		/* left adjust */	\
		nw += nx - __clipx1;				\
		nx = __clipx1;					\
	}							\
	if (ny < __clipy1) {		/* top adjust */	\
		nh += ny - __clipy1;				\
		ny = __clipy1;					\
	}							\
	if (nx + nw > __clipx2)		/* right adjust */	\
		nw = __clipx2 - nx + 1;				\
	if (ny + nh > __clipy2)		/* bottom adjust */	\
		nh = __clipy2 - ny + 1;				\

void gl_fillbox(int x, int y, int w, int h, int c)
{
    if (__clip) {
	if (x + w < __clipx1 || x > __clipx2)
	    return;
	if (y + h < __clipy1 || y > __clipy2)
	    return;
	if (x < __clipx1) {
	    w -= __clipx1 - x;
	    x = __clipx1;
	}
	if (y < __clipy1) {
	    h -= __clipy1 - y;
	    y = __clipy1;
	}
	if (x + w > __clipx2 + 1)
	    w = __clipx2 - x + 1;
	if (y + h > __clipy2 + 1)
	    h = __clipy2 - y + 1;
    }
    if (w <= 0 || h <= 0)
	return;
    fillbox(x, y, w, h, c);
}

void gl_putboxpart(int x, int y, int w, int h, int ow, int oh, void *b,
		   int ox, int oy)
{
    putboxpart(x, y, w, h, ow, oh, b, ox, oy);
}

void gl_putbox(int x, int y, int w, int h, void *b)
{
    uchar *bp = b;
    if (w <= 0 || h <= 0)
	return;
    if (__clip) {
	int nx, ny, nw, nh;
	ADJUSTBITMAPBOX();
	if (nw <= 0 || nh <= 0)
	    return;
	if (nw != w || nh != h) {
	    putboxpart(nx, ny, nw, nh, w, h, bp, nx - x, ny - y);
	    return;
	}
    }
    putbox(x, y, w, h, bp, w);
}

static void emulate_putboxmask(int x, int y, int w, int h, void *b)
{
    void *box;
    GraphicsContext gc;
    box = alloca(w * h * BYTESPERPIXEL);
    gl_getbox(x, y, w, h, box);	/* does clipping */

    gl_getcontext(&gc);		/* save context */

    /* create context that is only the box */
    gl_setcontextvirtual(w, h, BYTESPERPIXEL, BITSPERPIXEL, box);
    gl_putboxmask(0, 0, w, h, b);

    gl_setcontext(&gc);		/* restore context */
    gl_putbox(x, y, w, h, box);
}

void gl_putboxmask(int x, int y, int w, int h, void *b)
{
    if (w <= 0 || h <= 0)
	return;
    if (__clip) {
	if (x + w < __clipx1 || x > __clipx2)
	    return;
	if (y + h < __clipy1 || y > __clipy2)
	    return;
	if (x < __clipx1 || y < __clipy1
	    || x + w > __clipx2 + 1 || y + h > __clipy2 + 1) {
	    /* clipping is not directly implemented */
	    emulate_putboxmask(x, y, w, h, b);
	    return;
	}
    }
    if (MODETYPE == CONTEXT_PAGED)
	/* paged primitive is not implemented */
	emulate_putboxmask(x, y, w, h, b);
    else
	putboxmask(x, y, w, h, b);
}

void gl_getbox(int x, int y, int w, int h, void *b)
{
    if (__clip) {
	int nx, ny, nw, nh;
	ADJUSTBITMAPBOX();
	if (nw <= 0 || nh <= 0)
	    return;
	if (nw != w || nh != h) {
	    getboxpart(nx, ny, nw, nh, w, h, b, nx - x, ny - y);
	    return;
	}
    }
    getbox(x, y, w, h, b, w);
}

void gl_copybox(int x1, int y1, int w, int h, int x2, int y2)
{
/* Doesn't handle clipping. */
    if (MODETYPE == CONTEXT_PAGED) {
	/* Paged primitive is not implemented. */
	void *box;
	box = alloca(w * h * BYTESPERPIXEL);
	getbox(x1, y1, w, h, box, w);
	putbox(x2, y2, w, h, box, w);
	return;
    }
    copybox(x1, y1, w, h, x2, y2);
}


/* Miscellaneous functions */

void gl_clearscreen(int c)
{
    gl_fillbox(0, 0, WIDTH, HEIGHT, c);
}

int gl_rgbcolor(int r, int g, int b)
{
    unsigned v;
    switch (BITSPERPIXEL) {
    case 8:
	/* assumes RGB palette at index 0-255 */
	/* bits 0-2 = blue (3 bits) */
	/*      3-5 = green (3 bits) */
	/*      6-7 = red (2 bits) */
	return (r & 0xc0) + ((g & 0xe0) >> 2) + (b >> 5);
    case 24:
    case 32:
	v = (r << 16) + (g << 8) + b;
	if (MODEFLAGS & MODEFLAG_32BPP_SHIFT8)
	    return v << 8;
	return v;
    case 15:
	return ((r & 0xf8) << 7) + ((g & 0xf8) << 2) + (b >> 3);
    case 16:
	return ((r & 0xf8) << 8) + ((g & 0xfc) << 3) + (b >> 3);
    case 4:
	/* Now this is real fun. Map to standard EGA palette. */
	v = 0;
	if (b >= 64)
	    v += 1;
	if (g >= 64)
	    v += 2;
	if (r >= 64)
	    v += 4;
	if (b >= 192 || g >= 192 || r >= 192)
	    v += 8;
	return v;
    }
    return -1;
}

void gl_setpixelrgb(int x, int y, int r, int g, int b)
{
/* Color components range from 0 to 255 */
    if (__clip && outside(x, y))
	return;
    setpixel(x, y, gl_rgbcolor(r, g, b));
}

void gl_getpixelrgb(int x, int y, int *r, int *g, int *b)
{
    unsigned c;
    if (__clip && outside(x, y)) {
	*r = *g = *b = -1;
	return;
    }
    c = getpixel(x, y);
    switch (BITSPERPIXEL) {
    case 8:
	*b = (c & (1 + 2 + 4)) << 5;	/* bits 0-2 */
	*g = (c & (8 + 16 + 32)) << 2;	/* bits 3-5 */
	*r = (c & (64 + 128));	/* bits 6-7 */
	break;
    case 32:
	if (MODEFLAGS & MODEFLAG_32BPP_SHIFT8) {
	    *b = (c & 0xff00) >> 8;
	    *g = (c & 0xff0000) >> 16;
	    *r = c >> 24;
	    break;
	}
    case 24:
	*b = c & 0xff;
	*g = (c & 0xff00) >> 8;
	*r = c >> 16;
	break;
    case 15:
	*b = (c & (1 + 2 + 4 + 8 + 16)) << 3;
	*g = (c & (32 + 64 + 128 + 256 + 512)) >> 2;
	*r = (c & (1024 + 2048 + 4096 + 8192 + 16384)) >> 7;
	break;
    case 16:
	*b = (c & (1 + 2 + 4 + 8 + 16)) << 3;
	*g = (c & (32 + 64 + 128 + 256 + 512 + 1024)) >> 3;
	*r = (c & (2048 + 4096 + 8192 + 16384 + 32768)) >> 8;
	break;
    case 4:
	*b = (c & 1) * ((c & 8) ? 255 : 128);
	*g = (c & 2) * ((c & 8) ? 255 : 128);
	*r = (c & 4) * ((c & 8) ? 255 : 128);
	break;
    }
}

void gl_setdisplaystart(int x, int y)
{
    vga_setdisplaystart(y * BYTEWIDTH + x * BYTESPERPIXEL);
}


/* Screen copying */

void gl_setscreenoffset(int o)
{
    screenoffset = o;
}

int gl_enablepageflipping(GraphicsContext * gc)
{
    if (gc->modeflags & MODEFLAG_PAGEFLIPPING_CAPABLE) {
	gc->modeflags |= MODEFLAG_PAGEFLIPPING_ENABLED;
    }
    if (gc->modeflags & MODEFLAG_TRIPLEBUFFERING_CAPABLE) {
	gc->modeflags &= ~(MODEFLAG_PAGEFLIPPING_ENABLED);
	gc->modeflags |= MODEFLAG_TRIPLEBUFFERING_ENABLED;
    }
    gc->flippage = 0;
    if (gc->modeflags & MODEFLAG_TRIPLEBUFFERING_ENABLED)
	return 3;
    if (gc->modeflags & MODEFLAG_PAGEFLIPPING_ENABLED)
	return 2;
    return 0;
}

void gl_copyscreen(GraphicsContext * gc)
{
    int size;
    void *svp, *dvp;

    if (gc->modeflags & MODEFLAG_PAGEFLIPPING_ENABLED)
	gc->flippage ^= 1;
    if (gc->modeflags & MODEFLAG_TRIPLEBUFFERING_ENABLED)
	gc->flippage = (gc->flippage + 1) % 3;
    if (gc->modeflags & (MODEFLAG_PAGEFLIPPING_ENABLED |
			 MODEFLAG_TRIPLEBUFFERING_ENABLED)) {
	/* Calculate screen offset in bytes. */
	screenoffset = gc->bytewidth * HEIGHT * gc->flippage;
	if (gc->modeflags & MODEFLAG_FLIPPAGE_BANKALIGNED)
	    screenoffset = ((screenoffset + 0xffff) & ~0xffff);
    }
    if (gc->modetype == CONTEXT_MODEX) {
	vga_copytoplanar256(VBUF, BYTEWIDTH, screenoffset / 4,
			    gc->bytewidth / 4, WIDTH, HEIGHT);
	goto end;
    }
    if (gc->modetype == CONTEXT_PLANAR16) {
	if (WIDTH == 1024 && HEIGHT >= 512 &&
	    ((screenoffset / 8) & 0xffff) == 0) {
	    /* Kludge to allow 1024x768x16 with page flipping. */
	    int page;
	    page = (screenoffset / 8) >> 16;
	    vga_setpage(page);
	    vga_copytoplanar16(VBUF, BYTEWIDTH, 0,
			       gc->bytewidth / 8, WIDTH, 512);
	    vga_setpage(page + 1);
	    vga_copytoplanar16(VBUF + WIDTH * 512, BYTEWIDTH,
			       0, gc->bytewidth / 8, WIDTH, HEIGHT - 512);
	    return;
	}
	if (WIDTH * HEIGHT >= 512 * 1024)
	    /* We don't handle banking. */
	    return;

	vga_copytoplanar16(VBUF, BYTEWIDTH, screenoffset / 8,
			   gc->bytewidth / 8, WIDTH, HEIGHT);
	goto end;
    }
    if (BYTESPERPIXEL == 4 && gc->bytesperpixel == 3) {
	/* Special case. */
	int soffset, doffset;
	if (BYTEWIDTH / 4 != gc->bytewidth / 3) {
	    /* Even more special case for physical truecolor */
	    /* modes that have extra scanline padding. */
	    /* This has the effect of slowing down */
	    /* '3d' in some truecolor modes on ATI mach32. */
	    gl_copyboxtocontext(0, 0, WIDTH, HEIGHT, gc, 0, 0);
	    goto end;
	}
	soffset = 0;
	doffset = screenoffset;
	size = WIDTH * HEIGHT;
	while (soffset / 4 < size) {
	    int schunk, dchunk;
	    int count;
	    schunk = __svgalib_driver_setread(&__currentcontext, soffset, &svp);
	    dchunk = __svgalib_driver_setwrite(gc, doffset, &dvp);
	    if (dchunk == 1) {
		/* One byte left in segment. */
		int pix;
		pix = *(unsigned *) svp;	/* 32-bit pixel */
		*(unsigned char *) dvp = pix;
		dchunk = __svgalib_driver_setwrite(gc, doffset + 1, &dvp);
		*(unsigned short *) dvp = pix >> 8;
		count = 1;	/* 1 pixel handled. */
	    } else if (dchunk == 2) {
		/* Two bytes left. */
		int pix;
		pix = *(unsigned *) svp;	/* 32-bit pixel */
		*(unsigned short *) dvp = pix;
		dchunk = __svgalib_driver_setwrite(gc, doffset + 2, &dvp);
		*(unsigned char *) dvp = pix >> 16;
		count = 1;	/* 1 pixel handled. */
	    } else {
		count = min(min(schunk / 4, dchunk / 3),
			    size - (soffset / 4));
		__svgalib_memcpy4to3(dvp, svp, count);
	    }
	    soffset += count * 4;
	    doffset += count * 3;
	}
	goto end;
    }
    if (BYTESPERPIXEL == 4 && gc->bytesperpixel == 4 &&
	(gc->modeflags & MODEFLAG_32BPP_SHIFT8)) {
	int soffset = 0;
	int doffset = screenoffset;
	size = SCREENSIZE(__currentcontext);
	while (soffset < size) {
	    int schunk, dchunk;
	    int count;
	    schunk = __svgalib_driver_setread(&__currentcontext, soffset, &svp);
	    dchunk = __svgalib_driver_setwrite(gc, doffset, &dvp);
	    count = min(min(schunk, dchunk), (size - soffset));
	    __svgalib_memcpy32shift8(dvp, svp, count / 4);
	    soffset += count;
	    doffset += count;
	}
    } else {
	int soffset = 0;
	int doffset = screenoffset;
	size = SCREENSIZE(__currentcontext);
	while (soffset < size) {
	    int schunk, dchunk;
	    int count;
	    schunk = __svgalib_driver_setread(&__currentcontext, soffset, &svp);
	    dchunk = __svgalib_driver_setwrite(gc, doffset, &dvp);
	    count = min(min(schunk, dchunk), (size - soffset));
	    __memcpy(dvp, svp, count);
	    soffset += count;
	    doffset += count;
	}
    }

  end:
    if (gc->modeflags & (MODEFLAG_PAGEFLIPPING_ENABLED |
			 MODEFLAG_TRIPLEBUFFERING_ENABLED)) {
	GraphicsContext save;
	/* setdisplaystart will use BYTEWIDTH of the virtual screen, */
	/* which is what we want since vga_setdisplaystart is */
	/* defined in terms of pixel offset (except for hicolor */
	/* modes, which are defined in terms of bytes). */
	gl_getcontext(&save);
	gl_setcontext(gc);
	if (gc->modeflags & MODEFLAG_FLIPPAGE_BANKALIGNED)
	    vga_setdisplaystart(screenoffset);
	else
	    gl_setdisplaystart(0, gc->height * gc->flippage);
	gl_setcontext(&save);
	/* For page flipping, it might be appropriate to add a */
	/* waitverticalretrace here. */
    }
    screenoffset = 0;
}

void gl_copyboxtocontext(int x1, int y1, int w, int h, GraphicsContext * gc,
			 int x2, int y2)
{
/* This is now reasonably efficient if clipping is not enabled. */
    void *buf;
    GraphicsContext save;
    gl_getcontext(&save);
    if ((MODETYPE == CONTEXT_LINEAR || MODETYPE == CONTEXT_VIRTUAL) &&
	(BYTESPERPIXEL == gc->bytesperpixel) &&
	!__clip && !gc->clip) {
#ifdef DLL_CONTEXT_SHADOW
	__currentcontext = *gc;
#else
	gl_setcontext(gc);
#endif
/*
 * Note: Using save.bytewidth / BYTESPERPIXEL is probably not an optimal hack here.
 * it would be better to transfer save.bytewidth to putbox as it is what is really
 * used there. However, putbox is used all over interpreting the last entry as a
 * pixel count, so we keep it this way to avoid problems if some other place not
 * updated by accident.
 */
	putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h, save.vbuf +
	       y1 * save.bytewidth + x1 * BYTESPERPIXEL,
	       save.bytewidth / BYTESPERPIXEL);
	goto end;
    }
    buf = alloca(w * h * BYTESPERPIXEL);
    gl_getbox(x1, y1, w, h, buf);
#ifdef DLL_CONTEXT_SHADOW
    __currentcontext = *gc;
#else
    gl_setcontext(gc);
#endif

    if (save.bytesperpixel == 4 && gc->bytesperpixel == 3) {
	/* Special case conversion from 32-bit virtual screen to */
	/* 24-bit truecolor framebuffer. */
	if (gc->modetype == CONTEXT_PAGED || gc->clip) {
	    /* For paged modes or clipping, use another buffer. */
	    void *buf2;
	    buf2 = alloca(w * h * 3);
	    __svgalib_memcpy4to3(buf2, buf, w * h);
	    gl_putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h,
		      buf2);
	} else
	    /* No clipping, linear. */
	    __svgalib_driver24_putbox32(x2, y2, w, h, buf, w);
    } else			/* Contexts assumed to have same pixel size. */
	gl_putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h, buf);

  end:
#ifdef DLL_CONTEXT_SHADOW
    __currentcontext = save;
#else
    gl_setcontext(&save);
#endif
}

void gl_copyboxfromcontext(GraphicsContext * gc, int x1, int y1, int w, int h,
			   int x2, int y2)
{
    void *buf;
    GraphicsContext save;
    if ((gc->modetype == CONTEXT_LINEAR || gc->modetype == CONTEXT_VIRTUAL) &&
	(BYTESPERPIXEL == gc->bytesperpixel) &&
	!__clip && !gc->clip) {
/*
 * see above on  gc->bytewidth / BYTESPERPIXEL.
 */
	putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h, gc->vbuf +
	       y1 * gc->bytewidth + x1 * BYTESPERPIXEL,
	       gc->bytewidth / BYTESPERPIXEL);
	return;
    }
    gl_getcontext(&save);
#ifdef DLL_CONTEXT_SHADOW
    __currentcontext = *gc;
#else
    gl_setcontext(gc);
#endif
    buf = alloca(w * h * BYTESPERPIXEL);
    gl_getbox(x1, y1, w, h, buf);
#ifdef DLL_CONTEXT_SHADOW
    __currentcontext = save;
#else
    gl_setcontext(&save);
#endif

    if (gc->bytesperpixel == 4 && save.bytesperpixel == 3) {
	/* Special case conversion from 32-bit virtual screen to */
	/* 24-bit truecolor framebuffer. */
	if (save.modetype == CONTEXT_PAGED || save.clip) {
	    /* For paged modes or clipping, use another buffer. */
	    void *buf2;
	    buf2 = alloca(w * h * 3);
	    __svgalib_memcpy4to3(buf2, buf, w * h);
	    gl_putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h,
		      buf2);
	} else
	    /* No clipping, linear. */
	    __svgalib_driver24_putbox32(x2, y2, w, h, buf, w);
    } else			/* Contexts assumed to have same pixel size. */
	gl_putbox(x2, y2 + screenoffset / BYTEWIDTH, w, h, buf);
}
