//-----------------------------------------------------------------------------
//
//  $Logfile:: /win32/vid_dll.c                                               $
// $Revision:: 1                                                              $
//   $Author:: Burger                                                         $
//     $Date:: 12/27/00 7:57p                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /win32/vid_dll.c                                                    $
// * 
// * 1     12/27/00 7:57p Burger
// 
// 30    1/07/99 6:44p Markd
// made vid_checkamd default to 1
// 
// 29    11/17/98 8:24p Markd
// Added a sound restart when VID_NewWindow comes up
// 
// 28    11/12/98 11:11p Markd
// put in checkamd variable
// 
// 27    11/12/98 6:49p Markd
// Put in 3DNOW check
// 
// 26    10/19/98 4:12p Markd
// stopped all soudns when changin refs
// 
// 25    9/27/98 6:27p Markd
// auto-switch to 640x480 when in software for the menus
// 
// 24    9/27/98 5:06p Markd
// Removed SIN CIN_movie stuff
// 
// 23    9/24/98 5:38p Markd
// Took out begin and end loading plaque out of vid_checkchanges
// 
// 22    9/23/98 3:09p Aldie
// Added status for changing ref
// 
// 21    8/19/98 6:39p Markd
// Clear out temp models when switching modes
// 
// 20    8/02/98 9:01p Markd
// Merged code 3.17
//
// 19    7/26/98 3:59a Markd
// Put in Ion's mouse filter stuff
//
// 18    6/19/98 1:49p Markd
// Added SCR_CenterPrintf
//
// 17    6/17/98 11:35a Markd
// Put in code to clear out triangles if vid_ref has been modified.
//
// 16    6/06/98 5:39p Markd
// Added windows_cinematic support
//
// 15    5/24/98 6:34p Jimdose
// Changed char *'s to const char *'s
//
// 14    5/20/98 4:22p Aldie
// Added resizing check when video changes.
//
// 13    4/28/98 3:58p Markd
// Added IN_NewWindow call
//
// 12    4/27/98 3:20p Jimdose
// Added DebugLines
//
// 11    4/05/98 5:17p Aldie
// Rearranged console rendering
//
// 10    3/24/98 10:21a Markd
// Added console process back to the ref
//
// 9     3/24/98 10:04a Markd
// Added FreeDef ref command
//
// 8     3/23/98 1:05p Aldie
// Added console support to the ref import.
//
// 7     2/28/98 5:36p Markd
// Added SINMDL stuff to import structure
//
// 6     1/24/98 7:30p Jimdose
// Added cvar scr_shotname for naming screenshots
//
// 5     1/24/98 3:45p Jimdose
//
// 4     1/24/98 3:39p Jimdose
// Addded FS_Playerdir
//
// 3     12/30/97 6:06p Jimdose
// Added header text
//
// DESCRIPTION:
// Main windowed and fullscreen graphics interface module. This module
// is used for both the software and OpenGL rendering versions of the
// Quake refresh engine.
//

#include <assert.h>

#include "client.h"
#include "winquake.h"
#include "sinmdl.h"
#include "console3d.h"
#include "ref.h"

extern debugline_t	*DebugLines;
extern int			numDebugLines;

// Structure containing functions exported from refresh DLL
refexport_t	re;

cvar_t *win_noalttab;

#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST+1)  // message that will be supported by the OS
#endif

static UINT MSH_MOUSEWHEEL;

// Console variables that we need to access from this module
cvar_t		*vid_gamma;
cvar_t		*vid_ref;			// Name of Refresh DLL loaded
cvar_t		*vid_xpos;			// X coordinate of window position
cvar_t		*vid_ypos;			// Y coordinate of window position
cvar_t		*vid_fullscreen;
cvar_t		*r_customwidth; /* FS */
cvar_t		*r_customheight; /* FS */

cvar_t		*scr_shotname;
cvar_t		*vid_checkamd;

// Global variables used internally by this module
viddef_t	viddef;				// global video state; used by other modules

#ifdef REF_HARD_LINKED
refexport_t GetRefAPI (refimport_t rimp);
#else
HINSTANCE	reflib_library;		// Handle to refresh DLL
#endif

qboolean	reflib_active = 0;

HWND        cl_hwnd;            // Main window handle for life of program

#define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) )

static qboolean s_alttab_disabled;

extern	unsigned	sys_msg_time;

extern qboolean s_win95;

#ifndef SPI_SCREENSAVERRUNNING
#define SPI_SCREENSAVERRUNNING 0x0061
#endif

static void WIN_DisableAltTab( void )
{
	if ( s_alttab_disabled )
		return;

	if ( s_win95 )
	{
		BOOL old;

		SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, &old, 0 );
	}
	else
	{
		RegisterHotKey( 0, 0, MOD_ALT, VK_TAB );
		RegisterHotKey( 0, 1, MOD_ALT, VK_RETURN );
	}
	s_alttab_disabled = true;
}

static void WIN_EnableAltTab( void )
{
	if ( s_alttab_disabled )
	{
		if ( s_win95 )
		{
			BOOL old;

			SystemParametersInfo( SPI_SCREENSAVERRUNNING, 0, &old, 0 );
		}
		else
		{
			UnregisterHotKey( 0, 0 );
			UnregisterHotKey( 0, 1 );
		}

		s_alttab_disabled = false;
	}
}

/*
==========================================================================

DLL GLUE

==========================================================================
*/

#define	MAXPRINTMSG	4096
static void __attribute__((__format__(__printf__,2,3)))
VID_Printf (int print_level, const char *fmt, ...)
{
	va_list		argptr;
	char		msg[MAXPRINTMSG];

	va_start (argptr,fmt);
	_vsnprintf (msg,sizeof(msg),fmt,argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	if (print_level == PRINT_ALL)
		Com_Printf ("%s", msg);
	else if ( print_level == PRINT_DEVELOPER )
		Com_DPrintf (DEVELOPER_MSG_GFX, "%s", msg);
	else if ( print_level == PRINT_ALERT )
	{
		MessageBox( 0, msg, "PRINT_ALERT", MB_ICONWARNING );
		OutputDebugString( msg );
	}
}

static void __attribute__((__noreturn__, __format__(__printf__,2,3)))
VID_Error (int err_level, const char *fmt, ...)
{
	va_list		argptr;
	char		msg[MAXPRINTMSG];

	va_start (argptr,fmt);
	_vsnprintf (msg,sizeof(msg),fmt,argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	Com_Error (err_level,"%s", msg);
}

//==========================================================================

byte        scantokey[128] = 
					{ 
//  0           1       2       3       4       5       6       7 
//  8           9       A       B       C       D       E       F 
	0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6', 
	'7',    '8',    '9',    '0',    '-',    '=',    K_BACKSPACE, 9, // 0 
	'q',    'w',    'e',    'r',    't',    'y',    'u',    'i', 
	'o',    'p',    '[',    ']',    13 ,    K_CTRL,'a',  's',      // 1 
	'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';', 
	'\'' ,    '`',    K_SHIFT,'\\',  'z',    'x',    'c',    'v',      // 2 
	'b',    'n',    'm',    ',',    '.',    '/',    K_SHIFT,'*', 
	K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3 
	K_F6, K_F7, K_F8, K_F9, K_F10,  K_PAUSE,    0  , K_HOME, 
	K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 
	K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11, 
	K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6 
	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7 
}; 

/*
=======
MapKey

Map from windows to quake keynums
=======
*/
int MapKey (int key)
{
	int result;
	int modified = ( key >> 16 ) & 255;
	qboolean is_extended = false;

	if ( modified > 127)
		return 0;

	if ( key & ( 1 << 24 ) )
		is_extended = true;

	result = scantokey[modified];

	if ( !is_extended )
	{
		switch ( result )
		{
		case K_HOME:
			return K_KP_HOME;
		case K_UPARROW:
			return K_KP_UPARROW;
		case K_PGUP:
			return K_KP_PGUP;
		case K_LEFTARROW:
			return K_KP_LEFTARROW;
		case K_RIGHTARROW:
			return K_KP_RIGHTARROW;
		case K_END:
			return K_KP_END;
		case K_DOWNARROW:
			return K_KP_DOWNARROW;
		case K_PGDN:
			return K_KP_PGDN;
		case K_INS:
			return K_KP_INS;
		case K_DEL:
			return K_KP_DEL;
		default:
			return result;
		}
	}
	else
	{
		switch ( result )
		{
		case 0x0D:
			return K_KP_ENTER;
		case 0x2F:
			return K_KP_SLASH;
		case 0xAF:
			return K_KP_PLUS;
		}
		return result;
	}
}

void AppActivate(BOOL fActive, BOOL minimize)
{
	Minimized = minimize;

	Key_ClearStates();

	// we don't want to act like we're active if we're minimized
	if (fActive && !Minimized)
		ActiveApp = true;
	else
		ActiveApp = false;

	// minimize/restore mouse-capture on demand
	if (!ActiveApp)
	{
		IN_Activate (false);
		CDAudio_Activate (false);
		S_Activate (false);

		if ( win_noalttab->value )
		{
			WIN_EnableAltTab();
		}
	}
	else
	{
		IN_Activate (true);
		CDAudio_Activate (true);
		S_Activate (true);
		if ( win_noalttab->value )
		{
			WIN_DisableAltTab();
		}
	}
}

/*
====================
MainWndProc

main window procedure
====================
*/
LRESULT WINAPI MainWndProc (
    HWND    hWnd,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam)
{
	if ( uMsg == MSH_MOUSEWHEEL )
	{
		if (((int) wParam) > 0)
		{
			Key_Event( K_MWHEELUP, true, sys_msg_time );
			Key_Event( K_MWHEELUP, false, sys_msg_time );
		}
		else
		{
			Key_Event( K_MWHEELDOWN, true, sys_msg_time );
			Key_Event( K_MWHEELDOWN, false, sys_msg_time );
		}
		return DefWindowProc (hWnd, uMsg, wParam, lParam);
	}

	switch (uMsg)
	{
	case WM_MOUSEWHEEL:
		/*
		** this chunk of code theoretically only works under NT4 and Win98
		** since this message doesn't exist under Win95
		*/
		if ( ( short ) HIWORD( wParam ) > 0 )
		{
			Key_Event( K_MWHEELUP, true, sys_msg_time );
			Key_Event( K_MWHEELUP, false, sys_msg_time );
		}
		else
		{
			Key_Event( K_MWHEELDOWN, true, sys_msg_time );
			Key_Event( K_MWHEELDOWN, false, sys_msg_time );
		}
		break;

	case WM_HOTKEY:
		return 0;

	case WM_CREATE:
		cl_hwnd = hWnd;
		MSH_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG"); 
		return DefWindowProc (hWnd, uMsg, wParam, lParam);

	case WM_PAINT:
		SCR_DirtyScreen ();	// force entire screen to update next frame
		return DefWindowProc (hWnd, uMsg, wParam, lParam);

	case WM_DESTROY:
		// let sound and input know about this?
		cl_hwnd = NULL;
		return DefWindowProc (hWnd, uMsg, wParam, lParam);

	case WM_ACTIVATE:
		{
			int	fActive, fMinimized;

			// KJB: Watch this for problems in fullscreen modes with Alt-tabbing.
			fActive = LOWORD(wParam);
			fMinimized = (BOOL) HIWORD(wParam);

			AppActivate( fActive != WA_INACTIVE, fMinimized);

			if ( reflib_active )
				re.AppActivate( !( fActive == WA_INACTIVE ) );
		}
		return DefWindowProc (hWnd, uMsg, wParam, lParam);

	case WM_MOVE:
		{
			int		xPos, yPos;
			RECT r;
			int		style;

			if (!vid_fullscreen->value)
			{
				xPos = (short) LOWORD(lParam);    // horizontal position 
				yPos = (short) HIWORD(lParam);    // vertical position 

				r.left   = 0;
				r.top    = 0;
				r.right  = 1;
				r.bottom = 1;

				style = GetWindowLong( hWnd, GWL_STYLE );
				AdjustWindowRect( &r, style, FALSE );

				Cvar_SetValue( "vid_xpos", xPos + r.left);
				Cvar_SetValue( "vid_ypos", yPos + r.top);
				vid_xpos->modified = false;
				vid_ypos->modified = false;
				if (ActiveApp)
					IN_Activate (true);
			}
		}
		return DefWindowProc (hWnd, uMsg, wParam, lParam);

// this is complicated because Win32 seems to pack multiple mouse events into
// one update sometimes, so we always check all states and look for events
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MOUSEMOVE:
		{
			int	temp = 0;

			if (wParam & MK_LBUTTON)
				temp |= 1;
			if (wParam & MK_RBUTTON)
				temp |= 2;
			if (wParam & MK_MBUTTON)
				temp |= 4;
			IN_MouseEvent (temp);
		}
		break;

	case WM_SYSCOMMAND:
		if ( wParam == SC_SCREENSAVE )
			return 0;
		return DefWindowProc (hWnd, uMsg, wParam, lParam);
	case WM_SYSKEYDOWN:
		if ( wParam == 13 )
		{
			if ( vid_fullscreen )
			{
				Cvar_SetValue( "vid_fullscreen", !vid_fullscreen->value );
			}
			return 0;
		}
		// fall through
	case WM_KEYDOWN:
		Key_Event( MapKey( lParam ), true, sys_msg_time);
		break;

	case WM_SYSKEYUP:
	case WM_KEYUP:
		Key_Event( MapKey( lParam ), false, sys_msg_time);
		break;

	case MM_MCINOTIFY:
		{
			LRESULT CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
			return CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
		}
		break;

	default:	// pass all unhandled messages to DefWindowProc
		return DefWindowProc (hWnd, uMsg, wParam, lParam);
	}

	/* return 0 if handled message, 1 if not */
	return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

/*
============
VID_Restart_f

Console command to re-start the video mode and refresh DLL. We do this
simply by setting the modified flag for the vid_ref variable, which will
cause the entire video mode and refresh DLL to be reset on the next frame.
============
*/
void VID_Restart_f (void)
{
	vid_ref->modified = true;
}

void VID_Front_f( void )
{
	SetWindowLong( cl_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST );
	SetForegroundWindow( cl_hwnd );
}

/*
** VID_GetModeInfo
*/
typedef struct vidmode_s
{
	const char *description;
	int         width, height;
	int         mode;
} vidmode_t;

vidmode_t vid_modes[] =
{
	{ " 320 x 240",		320, 240,	0 },
	{ " 400 x 300",		400, 300,	1 },
	{ " 512 x 384",		512, 384,	2 },
	{ " 640 x 480",		640, 480,	3 },
	{ " 800 x 600",		800, 600,	4 },
	{ " 960 x 720",		960, 720,	5 },
	{ " 1024 x 768",	1024, 768,	6 },
	{ " 1152 x 864",	1152, 864,	7 },
	{ " 1280 x 960",	1280, 960,	8 },
	{ " 1600 x 1200",	1600, 1200,	10 },

	/* FS: Non 4:3 */
	{ " 640 x 400",		640, 400, 11 }, /* generic 16:10 widescreen*/
	{ " 800 x 480",		800, 480, 12 },
	{ " 800 x 500",		800, 500, 13 }, /* as found on modern notebooks */
	{ " 856 x 480",		856, 480, 14 },
	{ " 1024 x 480",	1024, 480, 15 }, /* Sony VAIO Pocketbook */
	{ " 1024 x 600",	1024, 600, 16 },
	{ " 1024 x 640",	1024, 640, 17 }, /* as found on modern notebooks    */
	{ " 1152 x 768",	1152, 768, 18 }, /* Apple TiBook */
	{ " 1280 x 854",	1280, 854, 19 }, /* Apple TiBook */
	{ " 1280 x 720",	1280, 720, 20 },
	{ " 1280 x 768",	1280, 768, 21 },
	{ " 1280 x 800",	1280, 800, 22 },
	{ " 1280 x 1024",	1280, 1024,	23 },
	{ " 1360 x 768",	1360, 768, 24 },
	{ " 1366 x 768",	1366, 768, 25 },
	{ " 1440 x 900",	1440, 900, 26 },
	{ " 1600 x 900",	1600, 900, 27 },
	{ " 1600 x 1024",	1600, 1024, 28 },
	{ " 1680 x 1050",	1680, 1050, 29 },
	{ " 1856 x 1392",	1856, 1392,	30 },
	{ " 1920 x 1024",	1920, 1024, 31 },
	{ " 1920 x 1080",	1920, 1080, 32 },
	{ " 1920 x 1200",	1920, 1200, 33 },
	{ " 1920 x 1440",	1920, 1440, 34 },
	{ " 2048 x 1536",	2048, 1536, 35 },
	{ " 2560 x 1080",	2560, 1080, 36 },
	{ " 2560 x 1440",	2560, 1440, 37 },
	{ " 2560 x 1600",	2560, 1600, 38 },
	{ " 3200 x 1800",	3200, 1800, 39 },
	{ " 3440 x 1440",	3440, 1440, 40 },
	{ " 3840 x 2160",	3840, 2160, 41 },
	{ " 3840 x 2400",	3840, 2400, 42 },
	{ " 5120 x 2880",	5120, 2880, 43 }
};

int VID_GetModeCount (void) /* FS */
{
	return VID_NUM_MODES;
}

const char *VID_GetModeDescription (int mode) /* FS */
{
	if ((mode < 0) || (mode >= VID_NUM_MODES))
	{
		return " UNKNOWN";
	}

	return vid_modes[mode].description;
}

qboolean VID_GetModeInfo( int *width, int *height, int mode )
{
	if (mode == -1) /* FS */
	{
		*width = r_customwidth->value;
		*height = r_customheight->value;
		return true;
	}

	if ( mode < 0 || mode >= VID_NUM_MODES )
		return false;

	*width  = vid_modes[mode].width;
	*height = vid_modes[mode].height;

	return true;
}

/*
** VID_UpdateWindowPosAndSize
*/
void VID_UpdateWindowPosAndSize( int x, int y )
{
	RECT r;
	int		style;
	int		w, h;

	r.left   = 0;
	r.top    = 0;
	r.right  = viddef.width;
	r.bottom = viddef.height;

	style = GetWindowLong( cl_hwnd, GWL_STYLE );
	AdjustWindowRect( &r, style, FALSE );

	w = r.right - r.left;
	h = r.bottom - r.top;

	MoveWindow( cl_hwnd, vid_xpos->value, vid_ypos->value, w, h, TRUE );
}

/*
** VID_NewWindow
*/
void VID_NewWindow ( int width, int height)
{
	viddef.width  = width;
	viddef.height = height;

	CONSOLE_CheckResize();
	if ( sound_started )
		CL_Snd_Restart_f ();

	cl.force_refdef = true;		// can't use a paused refdef
}

void VID_FreeReflib (void)
{
#ifndef REF_HARD_LINKED
	if ( !FreeLibrary( reflib_library ) )
		Com_Error( ERR_FATAL, "Reflib FreeLibrary failed" );
	reflib_library = NULL;
#endif
	memset (&re, 0, sizeof(re));
	reflib_active  = false;
}

/*
==============
VID_LoadRefresh
==============
*/
qboolean VID_LoadRefresh( char *name )
{
	refimport_t	ri;
#ifndef REF_HARD_LINKED
	GetRefAPI_t	GetRefAPI;
#endif

	if ( reflib_active )
	{
		re.Shutdown();
		VID_FreeReflib ();
	}

	Com_Printf( "------- Loading %s -------\n", name );

#ifndef REF_HARD_LINKED
	if ( ( reflib_library = LoadLibrary( name ) ) == 0 )
	{
		Com_Printf( "LoadLibrary(\"%s\") failed\n", name );
		return false;
	}
#endif

	ri.Cmd_AddCommand = Cmd_AddCommand;
	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
	ri.Cmd_Argc = Cmd_Argc;
	ri.Cmd_Argv = Cmd_Argv;
	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
	ri.Con_Printf = VID_Printf;
	ri.Sys_Error = VID_Error;
	ri.FS_LoadFile = FS_LoadFile;
	ri.FS_FreeFile = FS_FreeFile;
	ri.FS_Gamedir = FS_Gamedir;

	ri.SCR_CenterPrintf = SCR_CenterPrintf;
	ri.FS_Playerdir = FS_Playerdir;

	ri.Cvar_Get = Cvar_Get;
	ri.Cvar_Set = Cvar_Set;
	ri.Cvar_SetValue = Cvar_SetValue;
	ri.Vid_GetModeInfo = VID_GetModeInfo;
	ri.Vid_MenuInit = VID_MenuInit;
	ri.Vid_NewWindow = VID_NewWindow;

	ri.SINMDL_GetAnim = SINMDL_GetAnim;
	ri.SINMDL_GetBaseModel = SINMDL_GetBaseModel;
	ri.SINMDL_GetDef = SINMDL_GetDef;
	ri.SINMDL_FreeDef = SINMDL_FreeDef;
	ri.SINMDL_RegisterDef = SINMDL_RegisterDef;
	ri.CONSOLE_Process = CONSOLE_Process;

	ri.DebugLines		= &DebugLines;
	ri.numDebugLines	= &numDebugLines;

	ri.Cvar_SetDescription = Cvar_SetDescription; /* FS */

#ifndef REF_HARD_LINKED
	if ((GetRefAPI = (GetRefAPI_t) GetProcAddress(reflib_library, "GetRefAPI")) == NULL)
		Com_Error( ERR_FATAL, "GetProcAddress failed on %s", name );
#endif

	re = GetRefAPI( ri );

	if (re.api_version != API_VERSION)
	{
		VID_FreeReflib ();
		Com_Error (ERR_FATAL, "%s has incompatible api_version", name);
	}

	if ( re.Init( global_hInstance, MainWndProc ) == -1 )
	{
		re.Shutdown();
		VID_FreeReflib ();
		return false;
	}

	Com_Printf( "------------------------------------\n");
	reflib_active = true;
	return true;
}

/*
============
VID_CheckChanges

This function gets called once just before drawing each frame, and it's sole purpose in life
is to check to see if any of the video mode parameters have changed, and if they have to
update the rendering DLL and/or video mode to match.
============
*/
void VID_CheckChanges (void)
{
	char name[100];

	if ( win_noalttab->modified )
	{
		if ( win_noalttab->value )
		{
			WIN_DisableAltTab();
		}
		else
		{
			WIN_EnableAltTab();
		}
		win_noalttab->modified = false;
	}

	if ( vid_ref->modified )
	{
		cl.force_refdef = true;		// can't use a paused refdef
		S_StopAllSounds();
		CL_ClearTriangles();
		CL_ClearTempmodels();
	}

	while (vid_ref->modified)
	{
		/*
		** refresh has changed
		*/
		vid_ref->modified = false;
		vid_fullscreen->modified = true;
		cl.refresh_prepped = false;

		S_StopAllSounds();

		if ( !strcmp( vid_ref->string, "gl" ) && (vid_checkamd->value) && CL_Detect3DNow() )
		{
			Com_sprintf( name, sizeof(name), "ref_%s_amd.dll", vid_ref->string );
		}
		else
		{
			Com_sprintf( name, sizeof(name), "ref_%s.dll", vid_ref->string );
		}

		if ( !VID_LoadRefresh( name ) )
		{
			if ( strcmp (vid_ref->string, "soft") == 0 )
				Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
			Cvar_Set( "vid_ref", "soft" );

			/*
			** drop the console if we fail to load a refresh
			*/
			if ( cls.key_dest != key_console )
			{
				Con_ToggleConsole_f();
			}
		}
	}

	/*
	** update our window position
	*/
	if ( vid_xpos->modified || vid_ypos->modified )
	{
		if (!vid_fullscreen->value)
			VID_UpdateWindowPosAndSize( vid_xpos->value, vid_ypos->value );

		vid_xpos->modified = false;
		vid_ypos->modified = false;
	}
}

/*
============
VID_Init
============
*/
void VID_Init (void)
{
	/* Create the video variables so we know how to start the graphics drivers */
	vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE);
	vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
	vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
	vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE);
	vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE );
	win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE );
	r_customwidth = Cvar_Get ("r_customwidth", "640", CVAR_ARCHIVE); /* FS */
	r_customheight = Cvar_Get ( "r_customheight", "480", CVAR_ARCHIVE); /* FS */
	scr_shotname = Cvar_Get( "scr_shotname", SCREEN_SHOT_NAME, CVAR_ARCHIVE );
	vid_checkamd = Cvar_Get( "vid_checkamd", "1", CVAR_ARCHIVE );

	/* Add some console commands that we want to handle */
	Cmd_AddCommand ("vid_restart", VID_Restart_f);
	Cmd_AddCommand ("vid_front", VID_Front_f);

	/*
	** this is a gross hack but necessary to clamp the mode for 3Dfx
	*/
#if 0
	{
		cvar_t *gl_driver = Cvar_Get( "gl_driver", "opengl32", 0 );
		cvar_t *gl_mode = Cvar_Get( "gl_mode", "3", 0 );

		if ( Q_stricmp( gl_driver->string, "3dfxgl" ) == 0 )
		{
			Cvar_SetValue( "gl_mode", 3 );
			viddef.width  = 640;
			viddef.height = 480;
		}
	}
#endif

	/* Disable the 3Dfx splash screen */
	_putenv("FX_GLIDE_NO_SPLASH=1");
	/* don't let fxMesa cheat multitexturing */
	_putenv("FX_DONT_FAKE_MULTITEX=1");

	/* Start the graphics mode and load refresh DLL */
	VID_CheckChanges();

	SCR_EndLoadingPlaque();
}

/*
============
VID_Shutdown
============
*/
void VID_Shutdown (void)
{
	if ( reflib_active )
	{
		re.Shutdown ();
		VID_FreeReflib ();
	}
}
