#include "../ref_gl/gl_local.h"
#include "glw_win.h"
#include "winquake.h"
#include <assert.h>
#include <windows.h>
static bool GLimp_SwitchFullscreen(int width, int height);
bool GLimp_InitGL(void);
glwstate_t glw_state;
extern cvar_t *vid_fullscreen;
extern cvar_t *vid_ref;
static bool VerifyDriver(void) {
char buffer[1024];
strcpy(buffer, qglGetString(GL_RENDERER));
strlwr(buffer);
if(strcmp(buffer, "gdi generic") == 0)
if(!glw_state.mcd_accelerated)
return false;
return true;
}
#define WINDOW_CLASS_NAME "Quake 2"
bool VID_CreateWindow(int width, int height, bool fullscreen) {
WNDCLASS wc;
RECT r;
cvar_t *vid_xpos, *vid_ypos;
int stylebits;
int x, y, w, h;
int exstyle;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)glw_state.wndproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = glw_state.hInstance;
wc.hIcon = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (void *)COLOR_GRAYTEXT;
wc.lpszMenuName = 0;
wc.lpszClassName = WINDOW_CLASS_NAME;
if(!RegisterClass(&wc))
ri.Sys_Error(ERR_FATAL, "Couldn't register window class");
if(fullscreen) {
exstyle = WS_EX_TOPMOST;
stylebits = WS_POPUP | WS_VISIBLE;
} else {
exstyle = 0;
stylebits = WINDOW_STYLE;
}
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
AdjustWindowRect(&r, stylebits, FALSE);
w = r.right - r.left;
h = r.bottom - r.top;
if(fullscreen) {
x = 0;
y = 0;
} else {
vid_xpos = ri.Cvar_Get("vid_xpos", "0", 0);
vid_ypos = ri.Cvar_Get("vid_ypos", "0", 0);
x = vid_xpos->value;
y = vid_ypos->value;
}
glw_state.hWnd = CreateWindowEx(exstyle, WINDOW_CLASS_NAME, "Quake 2", stylebits, x, y, w, h, NULL, NULL,
glw_state.hInstance, NULL);
if(!glw_state.hWnd)
ri.Sys_Error(ERR_FATAL, "Couldn't create window");
ShowWindow(glw_state.hWnd, SW_SHOW);
UpdateWindow(glw_state.hWnd);
if(!GLimp_InitGL()) {
ri.Con_Printf(PRINT_ALL, "VID_CreateWindow() - GLimp_InitGL failed\n");
return false;
}
SetForegroundWindow(glw_state.hWnd);
SetFocus(glw_state.hWnd);
ri.Vid_NewWindow(width, height);
return true;
}
rserr_t GLimp_SetMode(int *pwidth, int *pheight, int mode, bool fullscreen) {
int width, height;
const char *win_fs[] = {"W", "FS"};
ri.Con_Printf(PRINT_ALL, "Initializing OpenGL display\n");
ri.Con_Printf(PRINT_ALL, "...setting mode %d:", mode);
if(!ri.Vid_GetModeInfo(&width, &height, mode)) {
ri.Con_Printf(PRINT_ALL, " invalid mode\n");
return rserr_invalid_mode;
}
ri.Con_Printf(PRINT_ALL, " %d %d %s\n", width, height, win_fs[fullscreen]);
if(glw_state.hWnd) {
GLimp_Shutdown();
}
if(fullscreen) {
DEVMODE dm;
ri.Con_Printf(PRINT_ALL, "...attempting fullscreen\n");
memset(&dm, 0, sizeof(dm));
dm.dmSize = sizeof(dm);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
if(gl_bitdepth->value != 0) {
dm.dmBitsPerPel = gl_bitdepth->value;
dm.dmFields |= DM_BITSPERPEL;
ri.Con_Printf(PRINT_ALL, "...using gl_bitdepth of %d\n", (int)gl_bitdepth->value);
} else {
HDC hdc = GetDC(NULL);
int bitspixel = GetDeviceCaps(hdc, BITSPIXEL);
ri.Con_Printf(PRINT_ALL, "...using desktop display depth of %d\n", bitspixel);
ReleaseDC(0, hdc);
}
ri.Con_Printf(PRINT_ALL, "...calling CDS: ");
if(ChangeDisplaySettings(&dm, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) {
*pwidth = width;
*pheight = height;
gl_state.fullscreen = true;
ri.Con_Printf(PRINT_ALL, "ok\n");
if(!VID_CreateWindow(width, height, true))
return rserr_invalid_mode;
return rserr_ok;
} else {
*pwidth = width;
*pheight = height;
ri.Con_Printf(PRINT_ALL, "failed\n");
ri.Con_Printf(PRINT_ALL, "...calling CDS assuming dual monitors:");
dm.dmPelsWidth = width * 2;
dm.dmPelsHeight = height;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
if(gl_bitdepth->value != 0) {
dm.dmBitsPerPel = gl_bitdepth->value;
dm.dmFields |= DM_BITSPERPEL;
}
if(ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
ri.Con_Printf(PRINT_ALL, " failed\n");
ri.Con_Printf(PRINT_ALL, "...setting windowed mode\n");
ChangeDisplaySettings(0, 0);
*pwidth = width;
*pheight = height;
gl_state.fullscreen = false;
if(!VID_CreateWindow(width, height, false))
return rserr_invalid_mode;
return rserr_invalid_fullscreen;
} else {
ri.Con_Printf(PRINT_ALL, " ok\n");
if(!VID_CreateWindow(width, height, true))
return rserr_invalid_mode;
gl_state.fullscreen = true;
return rserr_ok;
}
}
} else {
ri.Con_Printf(PRINT_ALL, "...setting windowed mode\n");
*pwidth = width;
*pheight = height;
gl_state.fullscreen = false;
if(!VID_CreateWindow(width, height, false))
return rserr_invalid_mode;
}
return rserr_ok;
}
void GLimp_Shutdown(void) {
if(qwglMakeCurrent && !qwglMakeCurrent(NULL, NULL))
ri.Con_Printf(PRINT_ALL, "ref_gl::R_Shutdown() - wglMakeCurrent failed\n");
if(glw_state.hGLRC) {
if(qwglDeleteContext && !qwglDeleteContext(glw_state.hGLRC))
ri.Con_Printf(PRINT_ALL, "ref_gl::R_Shutdown() - wglDeleteContext failed\n");
glw_state.hGLRC = NULL;
}
if(glw_state.hDC) {
if(!ReleaseDC(glw_state.hWnd, glw_state.hDC))
ri.Con_Printf(PRINT_ALL, "ref_gl::R_Shutdown() - ReleaseDC failed\n");
glw_state.hDC = NULL;
}
if(glw_state.hWnd) {
DestroyWindow(glw_state.hWnd);
glw_state.hWnd = NULL;
}
if(glw_state.log_fp) {
fclose(glw_state.log_fp);
glw_state.log_fp = 0;
}
UnregisterClass(WINDOW_CLASS_NAME, glw_state.hInstance);
if(gl_state.fullscreen) {
ChangeDisplaySettings(0, 0);
gl_state.fullscreen = false;
}
}
int GLimp_Init(void *hinstance, void *wndproc) {
#define OSR2_BUILD_NUMBER 1111
OSVERSIONINFO vinfo;
vinfo.dwOSVersionInfoSize = sizeof(vinfo);
glw_state.allowdisplaydepthchange = false;
if(GetVersionEx(&vinfo)) {
if(vinfo.dwMajorVersion > 4) {
glw_state.allowdisplaydepthchange = true;
} else if(vinfo.dwMajorVersion == 4) {
if(vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
glw_state.allowdisplaydepthchange = true;
} else if(vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
if(LOWORD(vinfo.dwBuildNumber) >= OSR2_BUILD_NUMBER) {
glw_state.allowdisplaydepthchange = true;
}
}
}
} else {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - GetVersionEx failed\n");
return false;
}
glw_state.hInstance = (HINSTANCE)hinstance;
glw_state.wndproc = wndproc;
return true;
}
bool GLimp_InitGL(void) {
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,
0,
0,
0,
0,
0, 0, 0, 0, 0,
0,
0,
0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0,
0,
0 };
int pixelformat;
cvar_t *stereo;
stereo = ri.Cvar_Get("cl_stereo", "0", 0);
if(stereo->value != 0) {
ri.Con_Printf(PRINT_ALL, "...attempting to use stereo\n");
pfd.dwFlags |= PFD_STEREO;
gl_state.stereo_enabled = true;
} else {
gl_state.stereo_enabled = false;
}
if(strstr(gl_driver->string, "opengl32") != 0)
glw_state.minidriver = false;
else
glw_state.minidriver = true;
if(glw_state.hDC != NULL)
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - non-NULL DC exists\n");
if((glw_state.hDC = GetDC(glw_state.hWnd)) == NULL) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - GetDC failed\n");
return false;
}
if(glw_state.minidriver) {
if((pixelformat = qwglChoosePixelFormat(glw_state.hDC, &pfd)) == 0) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - qwglChoosePixelFormat failed\n");
return false;
}
if(qwglSetPixelFormat(glw_state.hDC, pixelformat, &pfd) == FALSE) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - qwglSetPixelFormat failed\n");
return false;
}
qwglDescribePixelFormat(glw_state.hDC, pixelformat, sizeof(pfd), &pfd);
} else {
if((pixelformat = ChoosePixelFormat(glw_state.hDC, &pfd)) == 0) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - ChoosePixelFormat failed\n");
return false;
}
if(SetPixelFormat(glw_state.hDC, pixelformat, &pfd) == FALSE) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - SetPixelFormat failed\n");
return false;
}
DescribePixelFormat(glw_state.hDC, pixelformat, sizeof(pfd), &pfd);
if(!(pfd.dwFlags & PFD_GENERIC_ACCELERATED)) {
extern cvar_t *gl_allow_software;
if(gl_allow_software->value)
glw_state.mcd_accelerated = true;
else
glw_state.mcd_accelerated = false;
} else {
glw_state.mcd_accelerated = true;
}
}
if(!(pfd.dwFlags & PFD_STEREO) && (stereo->value != 0)) {
ri.Con_Printf(PRINT_ALL, "...failed to select stereo pixel format\n");
ri.Cvar_SetValue("cl_stereo", 0);
gl_state.stereo_enabled = false;
}
if((glw_state.hGLRC = qwglCreateContext(glw_state.hDC)) == 0) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - qwglCreateContext failed\n");
goto fail;
}
if(!qwglMakeCurrent(glw_state.hDC, glw_state.hGLRC)) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - qwglMakeCurrent failed\n");
goto fail;
}
if(!VerifyDriver()) {
ri.Con_Printf(PRINT_ALL, "GLimp_Init() - no hardware acceleration detected\n");
goto fail;
}
ri.Con_Printf(PRINT_ALL, "GL PFD: color(%d-bits) Z(%d-bit)\n", (int)pfd.cColorBits, (int)pfd.cDepthBits);
return true;
fail:
if(glw_state.hGLRC) {
qwglDeleteContext(glw_state.hGLRC);
glw_state.hGLRC = NULL;
}
if(glw_state.hDC) {
ReleaseDC(glw_state.hWnd, glw_state.hDC);
glw_state.hDC = NULL;
}
return false;
}
void GLimp_BeginFrame(float camera_separation) {
if(gl_bitdepth->modified) {
if(gl_bitdepth->value != 0 && !glw_state.allowdisplaydepthchange) {
ri.Cvar_SetValue("gl_bitdepth", 0);
ri.Con_Printf(PRINT_ALL, "gl_bitdepth requires Win95 OSR2.x or WinNT 4.x\n");
}
gl_bitdepth->modified = false;
}
if(camera_separation < 0 && gl_state.stereo_enabled) {
qglDrawBuffer(GL_BACK_LEFT);
} else if(camera_separation > 0 && gl_state.stereo_enabled) {
qglDrawBuffer(GL_BACK_RIGHT);
} else {
qglDrawBuffer(GL_BACK);
}
}
void GLimp_EndFrame(void) {
int err;
err = qglGetError();
assert(err == GL_NO_ERROR);
if(stricmp(gl_drawbuffer->string, "GL_BACK") == 0) {
if(!qwglSwapBuffers(glw_state.hDC))
ri.Sys_Error(ERR_FATAL, "GLimp_EndFrame() - SwapBuffers() failed!\n");
}
}
void GLimp_AppActivate(bool active) {
if(active) {
SetForegroundWindow(glw_state.hWnd);
ShowWindow(glw_state.hWnd, SW_RESTORE);
} else {
if(vid_fullscreen->value)
ShowWindow(glw_state.hWnd, SW_MINIMIZE);
}
}