mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 06:10:56 +00:00
f12b3ea134
This cleans up the tangled mess of attempting to unload the gl driver in X11: for whatever reason, the display gets tied in to the library.
496 lines
12 KiB
C
496 lines
12 KiB
C
/*
|
|
vid_win.c
|
|
|
|
Win32 SW vid component
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "winquake.h"
|
|
#include <ddraw.h>
|
|
|
|
#include "QF/cvar.h"
|
|
#include "QF/qargs.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/vid.h"
|
|
|
|
#include "context_win.h"
|
|
#include "r_internal.h"
|
|
#include "vid_internal.h"
|
|
#include "vid_sw.h"
|
|
|
|
typedef union {
|
|
byte bgra[4];
|
|
uint32_t value;
|
|
} win_palette_t;
|
|
|
|
static win_palette_t st2d_8to32table[256];
|
|
static byte current_palette[768];
|
|
static int palette_changed;
|
|
|
|
static LPDIRECTDRAW dd_Object;
|
|
static HINSTANCE hInstDDraw;
|
|
static LPDIRECTDRAWSURFACE dd_frontbuffer;
|
|
static LPDIRECTDRAWSURFACE dd_backbuffer;
|
|
static RECT src_rect;
|
|
static RECT dst_rect;
|
|
static HDC win_gdi;
|
|
static HDC dib_section;
|
|
static HBITMAP dib_bitmap;
|
|
static HGDIOBJ previous_dib;
|
|
static int using_ddraw;
|
|
|
|
static LPDIRECTDRAWCLIPPER dd_Clipper;
|
|
|
|
typedef HRESULT (WINAPI *ddCreateProc_t) (GUID FAR *,
|
|
LPDIRECTDRAW FAR *,
|
|
IUnknown FAR *);
|
|
static ddCreateProc_t ddCreate;
|
|
|
|
static int dd_window_width = 640;
|
|
static int dd_window_height = 480;
|
|
|
|
static sw_framebuffer_t swfb;
|
|
static framebuffer_t fb = { .buffer = &swfb };
|
|
|
|
static void
|
|
DD_UpdateRects (int width, int height)
|
|
{
|
|
POINT p = { .x = 0, .y = 0 };
|
|
// first we need to figure out where on the primary surface our window
|
|
// lives
|
|
ClientToScreen (win_mainwindow, &p);
|
|
GetClientRect (win_mainwindow, &dst_rect);
|
|
OffsetRect (&dst_rect, p.x, p.y);
|
|
SetRect (&src_rect, 0, 0, width, height);
|
|
}
|
|
|
|
|
|
static void
|
|
VID_CreateDDrawDriver (int width, int height)
|
|
{
|
|
DDSURFACEDESC ddsd;
|
|
|
|
using_ddraw = false;
|
|
dd_window_width = width;
|
|
dd_window_height = height;
|
|
|
|
fb.width = width;
|
|
fb.height = height;
|
|
swfb.color = malloc (width * height);
|
|
swfb.rowbytes = width;
|
|
|
|
if (!(hInstDDraw = LoadLibrary ("ddraw.dll"))) {
|
|
return;
|
|
}
|
|
if (!(ddCreate = (ddCreateProc_t) GetProcAddress (hInstDDraw,
|
|
"DirectDrawCreate"))) {
|
|
return;
|
|
}
|
|
|
|
if (FAILED (ddCreate (0, &dd_Object, 0))) {
|
|
return;
|
|
}
|
|
if (FAILED (dd_Object->lpVtbl->SetCooperativeLevel (dd_Object,
|
|
win_mainwindow,
|
|
DDSCL_NORMAL))) {
|
|
return;
|
|
}
|
|
|
|
// the primary surface in windowed mode is the full screen
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (ddsd);
|
|
ddsd.dwFlags = DDSD_CAPS;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
|
|
|
|
// ...and create it
|
|
if (FAILED (dd_Object->lpVtbl->CreateSurface (dd_Object, &ddsd,
|
|
&dd_frontbuffer, 0))) {
|
|
return;
|
|
}
|
|
|
|
// not using a clipper will slow things down and switch aero off
|
|
if (FAILED (IDirectDraw_CreateClipper (dd_Object, 0, &dd_Clipper, 0))) {
|
|
return;
|
|
}
|
|
if (FAILED (IDirectDrawClipper_SetHWnd (dd_Clipper, 0, win_mainwindow))) {
|
|
return;
|
|
}
|
|
if (FAILED (IDirectDrawSurface_SetClipper (dd_frontbuffer,
|
|
dd_Clipper))) {
|
|
return;
|
|
}
|
|
|
|
// the secondary surface is an offscreen surface that is the currect
|
|
// dimensions
|
|
// this will be blitted to the correct location on the primary surface
|
|
// (which is the full screen) during our draw op
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (ddsd);
|
|
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
|
|
ddsd.dwWidth = width;
|
|
ddsd.dwHeight = height;
|
|
|
|
if (FAILED (IDirectDraw_CreateSurface (dd_Object, &ddsd,
|
|
&dd_backbuffer, 0))) {
|
|
return;
|
|
}
|
|
|
|
// direct draw is working now
|
|
using_ddraw = true;
|
|
|
|
// create initial rects
|
|
DD_UpdateRects (dd_window_width, dd_window_height);
|
|
}
|
|
|
|
static void
|
|
VID_CreateGDIDriver (int width, int height, const byte *palette, byte **buffer,
|
|
int *rowbytes)
|
|
{
|
|
// common bitmap definition
|
|
typedef struct dibinfo {
|
|
BITMAPINFOHEADER header;
|
|
RGBQUAD acolors[256];
|
|
} dibinfo_t;
|
|
|
|
dibinfo_t dibheader;
|
|
BITMAPINFO *pbmiDIB = (BITMAPINFO *) & dibheader;
|
|
int i;
|
|
byte *dib_base = 0;
|
|
|
|
win_gdi = GetDC (win_mainwindow);
|
|
memset (&dibheader, 0, sizeof (dibheader));
|
|
|
|
// fill in the bitmap info
|
|
pbmiDIB->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
|
|
pbmiDIB->bmiHeader.biWidth = width;
|
|
pbmiDIB->bmiHeader.biHeight = height;
|
|
pbmiDIB->bmiHeader.biPlanes = 1;
|
|
pbmiDIB->bmiHeader.biCompression = BI_RGB;
|
|
pbmiDIB->bmiHeader.biSizeImage = 0;
|
|
pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmiDIB->bmiHeader.biClrUsed = 256;
|
|
pbmiDIB->bmiHeader.biClrImportant = 256;
|
|
pbmiDIB->bmiHeader.biBitCount = 8;
|
|
|
|
// fill in the palette
|
|
for (i = 0; i < 256; i++) {
|
|
// d_8to24table isn't filled in yet so this is just for testing
|
|
dibheader.acolors[i].rgbRed = palette[i * 3];
|
|
dibheader.acolors[i].rgbGreen = palette[i * 3 + 1];
|
|
dibheader.acolors[i].rgbBlue = palette[i * 3 + 2];
|
|
}
|
|
|
|
// create the DIB section
|
|
dib_bitmap = CreateDIBSection (win_gdi,
|
|
pbmiDIB,
|
|
DIB_RGB_COLORS,
|
|
(void **) &dib_base, 0, 0);
|
|
|
|
// set video buffers
|
|
if (pbmiDIB->bmiHeader.biHeight > 0) {
|
|
// bottom up
|
|
buffer[0] = dib_base + (height - 1) * width;
|
|
rowbytes[0] = -width;
|
|
} else {
|
|
// top down
|
|
buffer[0] = dib_base;
|
|
rowbytes[0] = width;
|
|
}
|
|
|
|
// clear the buffer
|
|
memset (dib_base, 0xff, width * height);
|
|
|
|
if ((dib_section = CreateCompatibleDC (win_gdi)) == 0)
|
|
Sys_Error ("DIB_Init() - CreateCompatibleDC failed\n");
|
|
|
|
if ((previous_dib = SelectObject (dib_section, dib_bitmap)) == 0)
|
|
Sys_Error ("DIB_Init() - SelectObject failed\n");
|
|
|
|
// create a palette
|
|
VID_InitGamma (palette);
|
|
viddef.vid_internal->set_palette (viddef.vid_internal->ctx, palette);
|
|
}
|
|
|
|
void
|
|
Win_UnloadAllDrivers (void)
|
|
{
|
|
// shut down ddraw
|
|
if (swfb.color) {
|
|
free (swfb.color);
|
|
swfb.color = 0;
|
|
}
|
|
if (swfb.depth) {
|
|
free (swfb.depth);
|
|
swfb.depth = 0;
|
|
}
|
|
|
|
if (dd_Clipper) {
|
|
IDirectDrawClipper_Release (dd_Clipper);
|
|
dd_Clipper = 0;
|
|
}
|
|
|
|
if (dd_frontbuffer) {
|
|
IDirectDrawSurface_Release (dd_frontbuffer);
|
|
dd_frontbuffer = 0;
|
|
}
|
|
|
|
if (dd_backbuffer) {
|
|
IDirectDrawSurface_Release (dd_backbuffer);
|
|
dd_backbuffer = 0;
|
|
}
|
|
|
|
if (dd_Object) {
|
|
IDirectDraw_Release (dd_Object);
|
|
dd_Object = 0;
|
|
}
|
|
|
|
if (hInstDDraw) {
|
|
FreeLibrary (hInstDDraw);
|
|
hInstDDraw = 0;
|
|
}
|
|
|
|
ddCreate = 0;
|
|
|
|
// shut down gdi
|
|
if (dib_section) {
|
|
SelectObject (dib_section, previous_dib);
|
|
DeleteDC (dib_section);
|
|
dib_section = 0;
|
|
}
|
|
|
|
if (dib_bitmap) {
|
|
DeleteObject (dib_bitmap);
|
|
dib_bitmap = 0;
|
|
}
|
|
|
|
if (win_gdi) {
|
|
// if win_gdi exists then win_mainwindow must also be valid
|
|
ReleaseDC (win_mainwindow, win_gdi);
|
|
win_gdi = 0;
|
|
}
|
|
// not using ddraw now
|
|
using_ddraw = false;
|
|
}
|
|
|
|
static void
|
|
Win_CreateDriver (void)
|
|
{
|
|
if (vid_ddraw) {
|
|
VID_CreateDDrawDriver (viddef.width, viddef.height);
|
|
}
|
|
if (!using_ddraw) {
|
|
// directdraw failed or was not requested
|
|
//
|
|
// if directdraw failed, it may be partially initialized, so make sure
|
|
// the slate is clean
|
|
Win_UnloadAllDrivers ();
|
|
|
|
VID_CreateGDIDriver (viddef.width, viddef.height, viddef.palette,
|
|
&swfb.color, &swfb.rowbytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
win_init_bufers (void *data)
|
|
{
|
|
sw_ctx_t *ctx = data;
|
|
|
|
ctx->framebuffer = &fb;
|
|
|
|
Win_UnloadAllDrivers ();
|
|
Win_CreateDriver ();
|
|
}
|
|
|
|
static void
|
|
win_set_palette (sw_ctx_t *ctx, const byte *palette)
|
|
{
|
|
palette_changed = 1;
|
|
if (palette != current_palette) {
|
|
memcpy (current_palette, palette, sizeof (current_palette));
|
|
}
|
|
for (int i = 0; i < 256; i++) {
|
|
const byte *pal = palette + 3 * i;
|
|
st2d_8to32table[i].bgra[0] = viddef.gammatable[pal[2]];
|
|
st2d_8to32table[i].bgra[1] = viddef.gammatable[pal[1]];
|
|
st2d_8to32table[i].bgra[2] = viddef.gammatable[pal[0]];
|
|
st2d_8to32table[i].bgra[3] = 255;
|
|
}
|
|
if (!win_minimized && !using_ddraw && dib_section) {
|
|
RGBQUAD colors[256];
|
|
memcpy (colors, st2d_8to32table, sizeof (colors));
|
|
for (int i = 0; i < 256; i++) {
|
|
colors[i].rgbReserved = 0;
|
|
}
|
|
colors[0].rgbRed = 0;
|
|
colors[0].rgbGreen = 0;
|
|
colors[0].rgbBlue = 0;
|
|
colors[255].rgbRed = 0xff;
|
|
colors[255].rgbGreen = 0xff;
|
|
colors[255].rgbBlue = 0xff;
|
|
|
|
if (SetDIBColorTable (dib_section, 0, 256, colors) == 0) {
|
|
Sys_Printf ("win_set_palette() - SetDIBColorTable failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dd_blit_rect (vrect_t *rect)
|
|
{
|
|
RECT TheRect;
|
|
RECT sRect, dRect;
|
|
DDSURFACEDESC ddsd;
|
|
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (DDSURFACEDESC);
|
|
|
|
// lock the correct subrect
|
|
TheRect.left = rect->x;
|
|
TheRect.right = rect->x + rect->width;
|
|
TheRect.top = rect->y;
|
|
TheRect.bottom = rect->y + rect->height;
|
|
|
|
if (IDirectDrawSurface_Lock (dd_backbuffer, &TheRect, &ddsd,
|
|
DDLOCK_WRITEONLY | DDLOCK_SURFACEMEMORYPTR,
|
|
0) == DDERR_WASSTILLDRAWING) {
|
|
return;
|
|
}
|
|
|
|
// convert pitch to 32-bit addressable
|
|
ddsd.lPitch >>= 2;
|
|
|
|
byte *src = swfb.color + rect->y * swfb.rowbytes + rect->x;
|
|
unsigned *dst = ddsd.lpSurface;
|
|
for (int y = rect->height; y-- > 0; ) {
|
|
for (int x = rect->width; x-- > 0; ) {
|
|
*dst++ = st2d_8to32table[*src++].value;
|
|
}
|
|
src += swfb.rowbytes - rect->width;
|
|
dst += ddsd.lPitch - rect->width;
|
|
}
|
|
|
|
IDirectDrawSurface_Unlock (dd_backbuffer, 0);
|
|
|
|
// correctly offset source
|
|
sRect.left = src_rect.left + rect->x;
|
|
sRect.right = src_rect.left + rect->x + rect->width;
|
|
sRect.top = src_rect.top + rect->y;
|
|
sRect.bottom = src_rect.top + rect->y + rect->height;
|
|
|
|
// correctly offset dest
|
|
dRect.left = dst_rect.left + rect->x;
|
|
dRect.right = dst_rect.left + rect->x + rect->width;
|
|
dRect.top = dst_rect.top + rect->y;
|
|
dRect.bottom = dst_rect.top + rect->y + rect->height;
|
|
|
|
// copy to front buffer
|
|
IDirectDrawSurface_Blt (dd_frontbuffer, &dRect, dd_backbuffer,
|
|
&sRect, 0, 0);
|
|
}
|
|
|
|
static void
|
|
win_sw_update (sw_ctx_t *ctx, vrect_t *rects)
|
|
{
|
|
vrect_t full_rect;
|
|
if (!win_palettized && palette_changed) {
|
|
palette_changed = false;
|
|
full_rect.x = 0;
|
|
full_rect.y = 0;
|
|
full_rect.width = viddef.width;
|
|
full_rect.height = viddef.height;
|
|
full_rect.next = 0;
|
|
rects = &full_rect;
|
|
}
|
|
|
|
if (using_ddraw) {
|
|
while (rects) {
|
|
dd_blit_rect (rects);
|
|
rects = rects->next;
|
|
}
|
|
} else if (dib_section) {
|
|
while (rects) {
|
|
BitBlt (win_gdi, rects->x, rects->y,
|
|
rects->x + rects->width, rects->y + rects->height,
|
|
dib_section, rects->x, rects->y, SRCCOPY);
|
|
rects = rects->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
win_choose_visual (sw_ctx_t *ctx)
|
|
{
|
|
}
|
|
|
|
static void
|
|
win_set_background (void)
|
|
{
|
|
// because we have set the background brush for the window to 0 (to
|
|
// avoid flickering when re-sizing the window on the desktop), we clear
|
|
// the window to black when created, otherwise it will be empty while
|
|
// Quake starts up. This also prevents a screen flash to white when
|
|
// switching drivers. it still flashes, but at least it's black now
|
|
HDC hdc = GetDC (win_mainwindow);
|
|
PatBlt (hdc, 0, 0, win_rect.right, win_rect.bottom, BLACKNESS);
|
|
ReleaseDC (win_mainwindow, hdc);
|
|
}
|
|
|
|
static void
|
|
win_create_context (sw_ctx_t *ctx)
|
|
{
|
|
// shutdown any old driver that was active
|
|
Win_UnloadAllDrivers ();
|
|
|
|
win_sw_context = ctx;
|
|
|
|
win_set_background ();
|
|
|
|
// create the new driver
|
|
using_ddraw = false;
|
|
|
|
viddef.vid_internal->init_buffers = win_init_bufers;
|
|
}
|
|
|
|
sw_ctx_t *
|
|
Win_SW_Context (vid_internal_t *vi)
|
|
{
|
|
sw_ctx_t *ctx = calloc (1, sizeof (sw_ctx_t));
|
|
ctx->set_palette = win_set_palette;
|
|
ctx->choose_visual = win_choose_visual;
|
|
ctx->create_context = win_create_context;
|
|
ctx->update = win_sw_update;
|
|
|
|
vi->ctx = ctx;
|
|
return ctx;
|
|
}
|
|
|
|
void
|
|
Win_SW_Init_Cvars (void)
|
|
{
|
|
}
|