1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// vid_win.c -- Win32 video driver
21
22#include "quakedef.h"
23#include "winquake.h"
24#include "d_local.h"
25#include "resource.h"
26
27#define MAX_MODE_LIST	30
28#define VID_ROW_SIZE	3
29
30qboolean	dibonly;
31
32extern int		Minimized;
33
34HWND		mainwindow;
35
36HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
37
38int			DIBWidth, DIBHeight;
39qboolean	DDActive;
40RECT		WindowRect;
41DWORD		WindowStyle, ExWindowStyle;
42
43int			window_center_x, window_center_y, window_x, window_y, window_width, window_height;
44RECT		window_rect;
45
46static DEVMODE	gdevmode;
47static qboolean	startwindowed = 0, windowed_mode_set;
48static int		firstupdate = 1;
49static qboolean	vid_initialized = false, vid_palettized;
50static int		lockcount;
51static int		vid_fulldib_on_focus_mode;
52static qboolean	force_minimized, in_mode_set, is_mode0x13, force_mode_set;
53static int		vid_stretched, windowed_mouse;
54static qboolean	palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic;
55static HICON	hIcon;
56
57viddef_t	vid;				// global video state
58
59#define MODE_WINDOWED			0
60#define MODE_SETTABLE_WINDOW	2
61#define NO_MODE					(MODE_WINDOWED - 1)
62#define MODE_FULLSCREEN_DEFAULT	(MODE_WINDOWED + 3)
63
64// Note that 0 is MODE_WINDOWED
65cvar_t		vid_mode = {"vid_mode","0", false};
66// Note that 0 is MODE_WINDOWED
67cvar_t		_vid_default_mode = {"_vid_default_mode","0", true};
68// Note that 3 is MODE_FULLSCREEN_DEFAULT
69cvar_t		_vid_default_mode_win = {"_vid_default_mode_win","3", true};
70cvar_t		vid_wait = {"vid_wait","0"};
71cvar_t		vid_nopageflip = {"vid_nopageflip","0", true};
72cvar_t		_vid_wait_override = {"_vid_wait_override", "0", true};
73cvar_t		vid_config_x = {"vid_config_x","800", true};
74cvar_t		vid_config_y = {"vid_config_y","600", true};
75cvar_t		vid_stretch_by_2 = {"vid_stretch_by_2","1", true};
76cvar_t		_windowed_mouse = {"_windowed_mouse","0", true};
77cvar_t		vid_fullscreen_mode = {"vid_fullscreen_mode","3", true};
78cvar_t		vid_windowed_mode = {"vid_windowed_mode","0", true};
79cvar_t		block_switch = {"block_switch","0", true};
80cvar_t		vid_window_x = {"vid_window_x", "0", true};
81cvar_t		vid_window_y = {"vid_window_y", "0", true};
82
83typedef struct {
84	int		width;
85	int		height;
86} lmode_t;
87
88lmode_t	lowresmodes[] = {
89	{320, 200},
90	{320, 240},
91	{400, 300},
92	{512, 384},
93};
94
95int			vid_modenum = NO_MODE;
96int			vid_testingmode, vid_realmode;
97double		vid_testendtime;
98int			vid_default = MODE_WINDOWED;
99static int	windowed_default;
100
101modestate_t	modestate = MS_UNINIT;
102
103static byte		*vid_surfcache;
104static int		vid_surfcachesize;
105static int		VID_highhunkmark;
106
107unsigned char	vid_curpal[256*3];
108
109unsigned short	d_8to16table[256];
110unsigned	d_8to24table[256];
111
112int     driver = grDETECT,mode;
113bool    useWinDirect = true, useDirectDraw = true;
114MGLDC	*mgldc = NULL,*memdc = NULL,*dibdc = NULL,*windc = NULL;
115
116typedef struct {
117	modestate_t	type;
118	int			width;
119	int			height;
120	int			modenum;
121	int			mode13;
122	int			stretched;
123	int			dib;
124	int			fullscreen;
125	int			bpp;
126	int			halfscreen;
127	char		modedesc[13];
128} vmode_t;
129
130static vmode_t	modelist[MAX_MODE_LIST];
131static int		nummodes;
132static vmode_t	*pcurrentmode;
133
134int		aPage;					// Current active display page
135int		vPage;					// Current visible display page
136int		waitVRT = true;			// True to wait for retrace on flip
137
138static vmode_t	badmode;
139
140static byte	backingbuf[48*24];
141
142void VID_MenuDraw (void);
143void VID_MenuKey (int key);
144
145LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
146void AppActivate(BOOL fActive, BOOL minimize);
147
148
149/*
150================
151VID_RememberWindowPos
152================
153*/
154void VID_RememberWindowPos (void)
155{
156	RECT	rect;
157
158	if (GetWindowRect (mainwindow, &rect))
159	{
160		if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) &&
161			(rect.top < GetSystemMetrics (SM_CYSCREEN))  &&
162			(rect.right > 0)                             &&
163			(rect.bottom > 0))
164		{
165			Cvar_SetValue ("vid_window_x", (float)rect.left);
166			Cvar_SetValue ("vid_window_y", (float)rect.top);
167		}
168	}
169}
170
171
172/*
173================
174VID_CheckWindowXY
175================
176*/
177void VID_CheckWindowXY (void)
178{
179
180	if (((int)vid_window_x.value > (GetSystemMetrics (SM_CXSCREEN) - 160)) ||
181		((int)vid_window_y.value > (GetSystemMetrics (SM_CYSCREEN) - 120)) ||
182		((int)vid_window_x.value < 0)									   ||
183		((int)vid_window_y.value < 0))
184	{
185		Cvar_SetValue ("vid_window_x", 0.0);
186		Cvar_SetValue ("vid_window_y", 0.0 );
187	}
188}
189
190
191/*
192================
193VID_UpdateWindowStatus
194================
195*/
196void VID_UpdateWindowStatus (void)
197{
198
199	window_rect.left = window_x;
200	window_rect.top = window_y;
201	window_rect.right = window_x + window_width;
202	window_rect.bottom = window_y + window_height;
203	window_center_x = (window_rect.left + window_rect.right) / 2;
204	window_center_y = (window_rect.top + window_rect.bottom) / 2;
205
206	IN_UpdateClipCursor ();
207}
208
209
210/*
211================
212ClearAllStates
213================
214*/
215void ClearAllStates (void)
216{
217	int		i;
218
219// send an up event for each key, to make sure the server clears them all
220	for (i=0 ; i<256 ; i++)
221	{
222		Key_Event (i, false);
223	}
224
225	Key_ClearStates ();
226	IN_ClearStates ();
227}
228
229
230/*
231================
232VID_CheckAdequateMem
233================
234*/
235qboolean VID_CheckAdequateMem (int width, int height)
236{
237	int		tbuffersize;
238
239	tbuffersize = width * height * sizeof (*d_pzbuffer);
240
241	tbuffersize += D_SurfaceCacheForRes (width, height);
242
243// see if there's enough memory, allowing for the normal mode 0x13 pixel,
244// z, and surface buffers
245	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
246		 0x10000 * 3) < minimum_memory)
247	{
248		return false;		// not enough memory for mode
249	}
250
251	return true;
252}
253
254
255/*
256================
257VID_AllocBuffers
258================
259*/
260qboolean VID_AllocBuffers (int width, int height)
261{
262	int		tsize, tbuffersize;
263
264	tbuffersize = width * height * sizeof (*d_pzbuffer);
265
266	tsize = D_SurfaceCacheForRes (width, height);
267
268	tbuffersize += tsize;
269
270// see if there's enough memory, allowing for the normal mode 0x13 pixel,
271// z, and surface buffers
272	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
273		 0x10000 * 3) < minimum_memory)
274	{
275		Con_SafePrintf ("Not enough memory for video mode\n");
276		return false;		// not enough memory for mode
277	}
278
279	vid_surfcachesize = tsize;
280
281	if (d_pzbuffer)
282	{
283		D_FlushCaches ();
284		Hunk_FreeToHighMark (VID_highhunkmark);
285		d_pzbuffer = NULL;
286	}
287
288	VID_highhunkmark = Hunk_HighMark ();
289
290	d_pzbuffer = Hunk_HighAllocName (tbuffersize, "video");
291
292	vid_surfcache = (byte *)d_pzbuffer +
293			width * height * sizeof (*d_pzbuffer);
294
295	return true;
296}
297
298
299void initFatalError(void)
300{
301	MGL_exit();
302	MGL_fatalError(MGL_errorMsg(MGL_result()));
303	exit(EXIT_FAILURE);
304}
305
306
307int VID_Suspend (MGLDC *dc,m_int flags)
308{
309
310	if (flags & MGL_DEACTIVATE)
311	{
312	// FIXME: this doesn't currently work on NT
313		if (block_switch.value && !WinNT)
314		{
315			return MGL_NO_DEACTIVATE;
316		}
317
318		S_BlockSound ();
319		S_ClearBuffer ();
320
321		IN_RestoreOriginalMouseState ();
322		CDAudio_Pause ();
323
324	// keep WM_PAINT from trying to redraw
325		in_mode_set = true;
326
327		block_drawing = true;	// so we don't try to draw while switched away
328
329		return MGL_NO_SUSPEND_APP;
330	}
331	else if (flags & MGL_REACTIVATE)
332	{
333		IN_SetQuakeMouseState ();
334	// fix the leftover Alt from any Alt-Tab or the like that switched us away
335		ClearAllStates ();
336		CDAudio_Resume ();
337		S_UnblockSound ();
338
339		in_mode_set = false;
340
341		vid.recalc_refdef = 1;
342
343		block_drawing = false;
344
345		return MGL_NO_SUSPEND_APP;
346	}
347
348}
349
350
351void registerAllDispDrivers(void)
352{
353	/* Event though these driver require WinDirect, we register
354	 * them so that they will still be available even if DirectDraw
355	 * is present and the user has disable the high performance
356	 * WinDirect modes.
357	 */
358	MGL_registerDriver(MGL_VGA8NAME,VGA8_driver);
359//	MGL_registerDriver(MGL_VGAXNAME,VGAX_driver);
360
361	/* Register display drivers */
362	if (useWinDirect)
363	{
364//we don't want VESA 1.X drivers		MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver);
365		MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver);
366
367		if (!COM_CheckParm ("-novbeaf"))
368			MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver);
369	}
370
371	if (useDirectDraw)
372	{
373		MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver);
374	}
375}
376
377
378void registerAllMemDrivers(void)
379{
380	/* Register memory context drivers */
381	MGL_registerDriver(MGL_PACKED8NAME,PACKED8_driver);
382}
383
384
385void VID_InitMGLFull (HINSTANCE hInstance)
386{
387	int			i, xRes, yRes, bits, vMode, lowres, curmode, temp;
388	int			lowstretchedres, stretchedmode, lowstretched;
389    uchar		*m;
390
391// FIXME: NT is checked for because MGL currently has a bug that causes it
392// to try to use WinDirect modes even on NT
393	if (COM_CheckParm("-nowindirect") ||
394		COM_CheckParm("-nowd") ||
395		COM_CheckParm("-novesa") ||
396		WinNT)
397	{
398		useWinDirect = false;
399	}
400
401	if (COM_CheckParm("-nodirectdraw") || COM_CheckParm("-noddraw") || COM_CheckParm("-nodd"))
402		useDirectDraw = false;
403
404	// Initialise the MGL
405	MGL_unregisterAllDrivers();
406	registerAllDispDrivers();
407	registerAllMemDrivers();
408	MGL_detectGraph(&driver,&mode);
409	m = MGL_availableModes();
410
411	if (m[0] != 0xFF)
412	{
413		lowres = lowstretchedres = 99999;
414		lowstretched = 0;
415		curmode = 0;
416
417	// find the lowest-res mode, or a mode we can stretch up to and get
418	// lowest-res that way
419		for (i = 0; m[i] != 0xFF; i++)
420		{
421			MGL_modeResolution(m[i], &xRes, &yRes,&bits);
422
423			if ((bits == 8) &&
424				(xRes <= MAXWIDTH) &&
425				(yRes <= MAXHEIGHT) &&
426				(curmode < MAX_MODE_LIST))
427			{
428				if (m[i] == grVGA_320x200x256)
429					is_mode0x13 = true;
430
431				if (!COM_CheckParm("-noforcevga"))
432				{
433					if (m[i] == grVGA_320x200x256)
434					{
435						mode = i;
436						break;
437					}
438				}
439
440				if (xRes < lowres)
441				{
442					lowres = xRes;
443					mode = i;
444				}
445
446				if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320))
447				{
448					lowstretchedres = xRes >> 1;
449					stretchedmode = i;
450				}
451			}
452
453			curmode++;
454		}
455
456	// if there's a mode we can stretch by 2 up to, thereby effectively getting
457	// a lower-res mode than the lowest-res real but still at least 320x200, that
458	// will be our default mode
459		if (lowstretchedres < lowres)
460		{
461			mode = stretchedmode;
462			lowres = lowstretchedres;
463			lowstretched = 1;
464		}
465
466	// build the mode list, leaving room for the low-res stretched mode, if any
467		nummodes++;		// leave room for default mode
468
469		for (i = 0; m[i] != 0xFF; i++)
470		{
471			MGL_modeResolution(m[i], &xRes, &yRes,&bits);
472
473			if ((bits == 8) &&
474				(xRes <= MAXWIDTH) &&
475				(yRes <= MAXHEIGHT) &&
476				(nummodes < MAX_MODE_LIST))
477			{
478				if (i == mode)
479				{
480					if (lowstretched)
481					{
482						stretchedmode = nummodes;
483						curmode = nummodes++;
484					}
485					else
486					{
487						curmode = MODE_FULLSCREEN_DEFAULT;
488					}
489				}
490				else
491				{
492					curmode = nummodes++;
493				}
494
495				modelist[curmode].type = MS_FULLSCREEN;
496				modelist[curmode].width = xRes;
497				modelist[curmode].height = yRes;
498				sprintf (modelist[curmode].modedesc, "%dx%d", xRes, yRes);
499
500				if (m[i] == grVGA_320x200x256)
501					modelist[curmode].mode13 = 1;
502				else
503					modelist[curmode].mode13 = 0;
504
505				modelist[curmode].modenum = m[i];
506				modelist[curmode].stretched = 0;
507				modelist[curmode].dib = 0;
508				modelist[curmode].fullscreen = 1;
509				modelist[curmode].halfscreen = 0;
510				modelist[curmode].bpp = 8;
511			}
512		}
513
514		if (lowstretched)
515		{
516			modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode];
517			modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1;
518			modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1;
519			modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1;
520			sprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, "%dx%d",
521					 modelist[MODE_FULLSCREEN_DEFAULT].width,
522					 modelist[MODE_FULLSCREEN_DEFAULT].height);
523		}
524
525		vid_default = MODE_FULLSCREEN_DEFAULT;
526
527		temp = m[0];
528
529		if (!MGL_init(&driver, &temp, ""))
530		{
531			initFatalError();
532		}
533	}
534
535	MGL_setSuspendAppCallback(VID_Suspend);
536}
537
538
539MGLDC *createDisplayDC(int forcemem)
540/****************************************************************************
541*
542* Function:     createDisplayDC
543* Returns:      Pointer to the MGL device context to use for the application
544*
545* Description:  Initialises the MGL and creates an appropriate display
546*               device context to be used by the GUI. This creates and
547*               apropriate device context depending on the system being
548*               compile for, and should be the only place where system
549*               specific code is required.
550*
551****************************************************************************/
552{
553    MGLDC			*dc;
554	pixel_format_t	pf;
555	int				npages;
556
557	// Start the specified video mode
558	if (!MGL_changeDisplayMode(mode))
559        initFatalError();
560
561	npages = MGL_availablePages(mode);
562
563	if (npages > 3)
564		npages = 3;
565
566	if (!COM_CheckParm ("-notriplebuf"))
567	{
568		if (npages > 2)
569		{
570			npages = 2;
571		}
572	}
573
574	if ((dc = MGL_createDisplayDC(npages)) == NULL)
575		return NULL;
576
577	if (!forcemem && (MGL_surfaceAccessType(dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0))
578	{
579		MGL_makeCurrentDC(dc);
580		memdc = NULL;
581	}
582	else
583	{
584		// Set up for blitting from a memory buffer
585		memdc = MGL_createMemoryDC(MGL_sizex(dc)+1,MGL_sizey(dc)+1,8,&pf);
586		MGL_makeCurrentDC(memdc);
587	}
588
589	// Enable page flipping even for even for blitted surfaces
590	if (forcemem)
591	{
592		vid.numpages = 1;
593	}
594	else
595	{
596		vid.numpages = dc->mi.maxPage + 1;
597
598		if (vid.numpages > 1)
599		{
600			// Set up for page flipping
601			MGL_setActivePage(dc, aPage = 1);
602			MGL_setVisualPage(dc, vPage = 0, false);
603		}
604
605		if (vid.numpages > 3)
606			vid.numpages = 3;
607	}
608
609	if (vid.numpages == 2)
610		waitVRT = true;
611	else
612		waitVRT = false;
613
614	return dc;
615}
616
617
618void VID_InitMGLDIB (HINSTANCE hInstance)
619{
620	WNDCLASS		wc;
621	HDC				hdc;
622	int				i;
623
624	hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2));
625
626	/* Register the frame class */
627    wc.style         = 0;
628    wc.lpfnWndProc   = (WNDPROC)MainWndProc;
629    wc.cbClsExtra    = 0;
630    wc.cbWndExtra    = 0;
631    wc.hInstance     = hInstance;
632    wc.hIcon         = 0;
633    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
634	wc.hbrBackground = NULL;
635    wc.lpszMenuName  = 0;
636    wc.lpszClassName = "WinQuake";
637
638    if (!RegisterClass (&wc) )
639		Sys_Error ("Couldn't register window class");
640
641	/* Find the size for the DIB window */
642	/* Initialise the MGL for windowed operation */
643	MGL_setAppInstance(hInstance);
644	registerAllMemDrivers();
645	MGL_initWindowed("");
646
647	modelist[0].type = MS_WINDOWED;
648	modelist[0].width = 320;
649	modelist[0].height = 240;
650	strcpy (modelist[0].modedesc, "320x240");
651	modelist[0].mode13 = 0;
652	modelist[0].modenum = MODE_WINDOWED;
653	modelist[0].stretched = 0;
654	modelist[0].dib = 1;
655	modelist[0].fullscreen = 0;
656	modelist[0].halfscreen = 0;
657	modelist[0].bpp = 8;
658
659	modelist[1].type = MS_WINDOWED;
660	modelist[1].width = 640;
661	modelist[1].height = 480;
662	strcpy (modelist[1].modedesc, "640x480");
663	modelist[1].mode13 = 0;
664	modelist[1].modenum = MODE_WINDOWED + 1;
665	modelist[1].stretched = 1;
666	modelist[1].dib = 1;
667	modelist[1].fullscreen = 0;
668	modelist[1].halfscreen = 0;
669	modelist[1].bpp = 8;
670
671	modelist[2].type = MS_WINDOWED;
672	modelist[2].width = 800;
673	modelist[2].height = 600;
674	strcpy (modelist[2].modedesc, "800x600");
675	modelist[2].mode13 = 0;
676	modelist[2].modenum = MODE_WINDOWED + 2;
677	modelist[2].stretched = 1;
678	modelist[2].dib = 1;
679	modelist[2].fullscreen = 0;
680	modelist[2].halfscreen = 0;
681	modelist[2].bpp = 8;
682
683// automatically stretch the default mode up if > 640x480 desktop resolution
684	hdc = GetDC(NULL);
685
686	if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch"))
687	{
688		vid_default = MODE_WINDOWED + 1;
689	}
690	else
691	{
692		vid_default = MODE_WINDOWED;
693	}
694
695	windowed_default = vid_default;
696
697	ReleaseDC(NULL,hdc);
698
699	nummodes = 3;	// reserve space for windowed mode
700
701	DDActive = 0;
702}
703
704
705/*
706=================
707VID_InitFullDIB
708=================
709*/
710void VID_InitFullDIB (HINSTANCE hInstance)
711{
712	DEVMODE	devmode;
713	int		i, j, modenum, cmodes, existingmode, originalnummodes, lowestres;
714	int		numlowresmodes, bpp, done;
715	int		cstretch, istretch, mstretch;
716	BOOL	stat;
717
718// enumerate 8 bpp modes
719	originalnummodes = nummodes;
720	modenum = 0;
721	lowestres = 99999;
722
723	do
724	{
725		stat = EnumDisplaySettings (NULL, modenum, &devmode);
726
727		if ((devmode.dmBitsPerPel == 8) &&
728			(devmode.dmPelsWidth <= MAXWIDTH) &&
729			(devmode.dmPelsHeight <= MAXHEIGHT) &&
730			(nummodes < MAX_MODE_LIST))
731		{
732			devmode.dmFields = DM_BITSPERPEL |
733							   DM_PELSWIDTH |
734							   DM_PELSHEIGHT;
735
736			if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
737					DISP_CHANGE_SUCCESSFUL)
738			{
739				modelist[nummodes].type = MS_FULLDIB;
740				modelist[nummodes].width = devmode.dmPelsWidth;
741				modelist[nummodes].height = devmode.dmPelsHeight;
742				modelist[nummodes].modenum = 0;
743				modelist[nummodes].mode13 = 0;
744				modelist[nummodes].stretched = 0;
745				modelist[nummodes].halfscreen = 0;
746				modelist[nummodes].dib = 1;
747				modelist[nummodes].fullscreen = 1;
748				modelist[nummodes].bpp = devmode.dmBitsPerPel;
749				sprintf (modelist[nummodes].modedesc, "%dx%d",
750						 devmode.dmPelsWidth, devmode.dmPelsHeight);
751
752			// if the width is more than twice the height, reduce it by half because this
753			// is probably a dual-screen monitor
754				if (!COM_CheckParm("-noadjustaspect"))
755				{
756					if (modelist[nummodes].width > (modelist[nummodes].height << 1))
757					{
758						modelist[nummodes].width >>= 1;
759						modelist[nummodes].halfscreen = 1;
760						sprintf (modelist[nummodes].modedesc, "%dx%d",
761								 modelist[nummodes].width,
762								 modelist[nummodes].height);
763					}
764				}
765
766				for (i=originalnummodes, existingmode = 0 ; i<nummodes ; i++)
767				{
768					if ((modelist[nummodes].width == modelist[i].width) &&
769						(modelist[nummodes].height == modelist[i].height))
770					{
771						existingmode = 1;
772						break;
773					}
774				}
775
776				if (!existingmode)
777				{
778					if (modelist[nummodes].width < lowestres)
779						lowestres = modelist[nummodes].width;
780
781					nummodes++;
782				}
783			}
784		}
785
786		modenum++;
787	} while (stat);
788
789// see if any of them were actually settable; if so, this is our mode list,
790// else enumerate all modes; our mode list is whichever ones are settable
791// with > 8 bpp
792	if (nummodes == originalnummodes)
793	{
794		modenum = 0;
795		lowestres = 99999;
796
797		Con_SafePrintf ("No 8-bpp fullscreen DIB modes found\n");
798
799		do
800		{
801			stat = EnumDisplaySettings (NULL, modenum, &devmode);
802
803			if ((((devmode.dmPelsWidth <= MAXWIDTH) &&
804				  (devmode.dmPelsHeight <= MAXHEIGHT)) ||
805				 (!COM_CheckParm("-noadjustaspect") &&
806				  (devmode.dmPelsWidth <= (MAXWIDTH*2)) &&
807				  (devmode.dmPelsWidth > (devmode.dmPelsHeight*2)))) &&
808				(nummodes < MAX_MODE_LIST) &&
809				(devmode.dmBitsPerPel > 8))
810			{
811				devmode.dmFields = DM_BITSPERPEL |
812								   DM_PELSWIDTH |
813								   DM_PELSHEIGHT;
814
815				if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
816						DISP_CHANGE_SUCCESSFUL)
817				{
818					modelist[nummodes].type = MS_FULLDIB;
819					modelist[nummodes].width = devmode.dmPelsWidth;
820					modelist[nummodes].height = devmode.dmPelsHeight;
821					modelist[nummodes].modenum = 0;
822					modelist[nummodes].mode13 = 0;
823					modelist[nummodes].stretched = 0;
824					modelist[nummodes].halfscreen = 0;
825					modelist[nummodes].dib = 1;
826					modelist[nummodes].fullscreen = 1;
827					modelist[nummodes].bpp = devmode.dmBitsPerPel;
828					sprintf (modelist[nummodes].modedesc, "%dx%d",
829							 devmode.dmPelsWidth, devmode.dmPelsHeight);
830
831				// if the width is more than twice the height, reduce it by half because this
832				// is probably a dual-screen monitor
833					if (!COM_CheckParm("-noadjustaspect"))
834					{
835						if (modelist[nummodes].width > (modelist[nummodes].height*2))
836						{
837							modelist[nummodes].width >>= 1;
838							modelist[nummodes].halfscreen = 1;
839							sprintf (modelist[nummodes].modedesc, "%dx%d",
840									 modelist[nummodes].width,
841									 modelist[nummodes].height);
842						}
843					}
844
845					for (i=originalnummodes, existingmode = 0 ; i<nummodes ; i++)
846					{
847						if ((modelist[nummodes].width == modelist[i].width) &&
848							(modelist[nummodes].height == modelist[i].height))
849						{
850						// pick the lowest available bpp
851							if (modelist[nummodes].bpp < modelist[i].bpp)
852								modelist[i] = modelist[nummodes];
853
854							existingmode = 1;
855							break;
856						}
857					}
858
859					if (!existingmode)
860					{
861						if (modelist[nummodes].width < lowestres)
862							lowestres = modelist[nummodes].width;
863
864						nummodes++;
865					}
866				}
867			}
868
869			modenum++;
870		} while (stat);
871	}
872
873// see if there are any low-res modes that aren't being reported
874	numlowresmodes = sizeof(lowresmodes) / sizeof(lowresmodes[0]);
875	bpp = 8;
876	done = 0;
877
878// first make sure the driver doesn't just answer yes to all tests
879	devmode.dmBitsPerPel = 8;
880	devmode.dmPelsWidth = 42;
881	devmode.dmPelsHeight = 37;
882	devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
883
884	if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
885			DISP_CHANGE_SUCCESSFUL)
886	{
887		done = 1;
888	}
889
890	while (!done)
891	{
892		for (j=0 ; (j<numlowresmodes) && (nummodes < MAX_MODE_LIST) ; j++)
893		{
894			devmode.dmBitsPerPel = bpp;
895			devmode.dmPelsWidth = lowresmodes[j].width;
896			devmode.dmPelsHeight = lowresmodes[j].height;
897			devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
898
899			if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
900					DISP_CHANGE_SUCCESSFUL)
901			{
902					modelist[nummodes].type = MS_FULLDIB;
903					modelist[nummodes].width = devmode.dmPelsWidth;
904					modelist[nummodes].height = devmode.dmPelsHeight;
905					modelist[nummodes].modenum = 0;
906					modelist[nummodes].mode13 = 0;
907					modelist[nummodes].stretched = 0;
908					modelist[nummodes].halfscreen = 0;
909					modelist[nummodes].dib = 1;
910					modelist[nummodes].fullscreen = 1;
911					modelist[nummodes].bpp = devmode.dmBitsPerPel;
912					sprintf (modelist[nummodes].modedesc, "%dx%d",
913							 devmode.dmPelsWidth, devmode.dmPelsHeight);
914
915			// we only want the lowest-bpp version of each mode
916				for (i=originalnummodes, existingmode = 0 ; i<nummodes ; i++)
917				{
918					if ((modelist[nummodes].width == modelist[i].width)   &&
919						(modelist[nummodes].height == modelist[i].height) &&
920						(modelist[nummodes].bpp >= modelist[i].bpp))
921					{
922						existingmode = 1;
923						break;
924					}
925				}
926
927				if (!existingmode)
928				{
929					if (modelist[nummodes].width < lowestres)
930						lowestres = modelist[nummodes].width;
931
932					nummodes++;
933				}
934			}
935		}
936
937		switch (bpp)
938		{
939			case 8:
940				bpp = 16;
941				break;
942
943			case 16:
944				bpp = 32;
945				break;
946
947			case 32:
948				done = 1;
949				break;
950		}
951	}
952
953// now add the lowest stretch-by-2 pseudo-modes between 320-wide
954// (inclusive) and lowest real res (not inclusive)
955// don't bother if we have a real VGA mode 0x13 mode
956	if (!is_mode0x13)
957	{
958		for (i=originalnummodes, cstretch=0 ; i<nummodes ; i++)
959		{
960			if (((modelist[i].width >> 1) < lowestres) &&
961				((modelist[i].width >> 1) >= 320))
962			{
963				lowestres = modelist[i].width >> 1;
964				cstretch = 1;
965				mstretch = i;
966			}
967		}
968
969		if ((nummodes + cstretch) > MAX_MODE_LIST)
970			cstretch = MAX_MODE_LIST - nummodes;
971
972		if (cstretch > 0)
973		{
974			for (i=(nummodes-1) ; i>=originalnummodes ; i--)
975				modelist[i+cstretch] = modelist[i];
976
977			nummodes += cstretch;
978			istretch = originalnummodes;
979
980			modelist[istretch] = modelist[mstretch];
981			modelist[istretch].width >>= 1;
982			modelist[istretch].height >>= 1;
983			modelist[istretch].stretched = 1;
984			sprintf (modelist[istretch].modedesc, "%dx%d",
985					 modelist[istretch].width, modelist[istretch].height);
986		}
987	}
988
989	if (nummodes != originalnummodes)
990		vid_default = MODE_FULLSCREEN_DEFAULT;
991	else
992		Con_SafePrintf ("No fullscreen DIB modes found\n");
993}
994
995
996/*
997=================
998VID_NumModes
999=================
1000*/
1001int VID_NumModes (void)
1002{
1003	return nummodes;
1004}
1005
1006
1007/*
1008=================
1009VID_GetModePtr
1010=================
1011*/
1012vmode_t *VID_GetModePtr (int modenum)
1013{
1014
1015	if ((modenum >= 0) && (modenum < nummodes))
1016		return &modelist[modenum];
1017	else
1018		return &badmode;
1019}
1020
1021
1022/*
1023=================
1024VID_CheckModedescFixup
1025=================
1026*/
1027void VID_CheckModedescFixup (int mode)
1028{
1029	int		x, y, stretch;
1030
1031	if (mode == MODE_SETTABLE_WINDOW)
1032	{
1033		modelist[mode].stretched = (int)vid_stretch_by_2.value;
1034		stretch = modelist[mode].stretched;
1035
1036		if (vid_config_x.value < (320 << stretch))
1037			vid_config_x.value = 320 << stretch;
1038
1039		if (vid_config_y.value < (200 << stretch))
1040			vid_config_y.value = 200 << stretch;
1041
1042		x = (int)vid_config_x.value;
1043		y = (int)vid_config_y.value;
1044		sprintf (modelist[mode].modedesc, "%dx%d", x, y);
1045		modelist[mode].width = x;
1046		modelist[mode].height = y;
1047	}
1048}
1049
1050
1051/*
1052=================
1053VID_GetModeDescriptionMemCheck
1054=================
1055*/
1056char *VID_GetModeDescriptionMemCheck (int mode)
1057{
1058	char		*pinfo;
1059	vmode_t		*pv;
1060
1061	if ((mode < 0) || (mode >= nummodes))
1062		return NULL;
1063
1064	VID_CheckModedescFixup (mode);
1065
1066	pv = VID_GetModePtr (mode);
1067	pinfo = pv->modedesc;
1068
1069	if (VID_CheckAdequateMem (pv->width, pv->height))
1070	{
1071		return pinfo;
1072	}
1073	else
1074	{
1075		return NULL;
1076	}
1077}
1078
1079
1080/*
1081=================
1082VID_GetModeDescription
1083=================
1084*/
1085char *VID_GetModeDescription (int mode)
1086{
1087	char		*pinfo;
1088	vmode_t		*pv;
1089
1090	if ((mode < 0) || (mode >= nummodes))
1091		return NULL;
1092
1093	VID_CheckModedescFixup (mode);
1094
1095	pv = VID_GetModePtr (mode);
1096	pinfo = pv->modedesc;
1097	return pinfo;
1098}
1099
1100
1101/*
1102=================
1103VID_GetModeDescription2
1104
1105Tacks on "windowed" or "fullscreen"
1106=================
1107*/
1108char *VID_GetModeDescription2 (int mode)
1109{
1110	static char	pinfo[40];
1111	vmode_t		*pv;
1112
1113	if ((mode < 0) || (mode >= nummodes))
1114		return NULL;
1115
1116	VID_CheckModedescFixup (mode);
1117
1118	pv = VID_GetModePtr (mode);
1119
1120	if (modelist[mode].type == MS_FULLSCREEN)
1121	{
1122		sprintf(pinfo,"%s fullscreen", pv->modedesc);
1123	}
1124	else if (modelist[mode].type == MS_FULLDIB)
1125	{
1126		sprintf(pinfo,"%s fullscreen", pv->modedesc);
1127	}
1128	else
1129	{
1130		sprintf(pinfo, "%s windowed", pv->modedesc);
1131	}
1132
1133	return pinfo;
1134}
1135
1136
1137// KJB: Added this to return the mode driver name in description for console
1138
1139char *VID_GetExtModeDescription (int mode)
1140{
1141	static char	pinfo[40];
1142	vmode_t		*pv;
1143
1144	if ((mode < 0) || (mode >= nummodes))
1145		return NULL;
1146
1147	VID_CheckModedescFixup (mode);
1148
1149	pv = VID_GetModePtr (mode);
1150	if (modelist[mode].type == MS_FULLSCREEN)
1151	{
1152		sprintf(pinfo,"%s fullscreen %s",pv->modedesc,
1153				MGL_modeDriverName(pv->modenum));
1154	}
1155	else if (modelist[mode].type == MS_FULLDIB)
1156	{
1157		sprintf(pinfo,"%s fullscreen DIB", pv->modedesc);
1158	}
1159	else
1160	{
1161		sprintf(pinfo, "%s windowed", pv->modedesc);
1162	}
1163
1164	return pinfo;
1165}
1166
1167
1168void DestroyDIBWindow (void)
1169{
1170
1171	if (modestate == MS_WINDOWED)
1172	{
1173	// destroy the associated MGL DC's; the window gets reused
1174		if (windc)
1175			MGL_destroyDC(windc);
1176		if (dibdc)
1177			MGL_destroyDC(dibdc);
1178		windc = dibdc = NULL;
1179	}
1180}
1181
1182
1183void DestroyFullscreenWindow (void)
1184{
1185
1186	if (modestate == MS_FULLSCREEN)
1187	{
1188	// destroy the existing fullscreen mode and DC's
1189		if (mgldc)
1190			MGL_destroyDC (mgldc);
1191		if (memdc)
1192			MGL_destroyDC (memdc);
1193		mgldc = memdc = NULL;
1194	}
1195}
1196
1197
1198
1199void DestroyFullDIBWindow (void)
1200{
1201	if (modestate == MS_FULLDIB)
1202	{
1203		ChangeDisplaySettings (NULL, CDS_FULLSCREEN);
1204
1205	// Destroy the fullscreen DIB window and associated MGL DC's
1206		if (windc)
1207			MGL_destroyDC(windc);
1208		if (dibdc)
1209			MGL_destroyDC(dibdc);
1210		windc = dibdc = NULL;
1211	}
1212}
1213
1214
1215qboolean VID_SetWindowedMode (int modenum)
1216{
1217	HDC				hdc;
1218	pixel_format_t	pf;
1219	qboolean		stretched;
1220	int				lastmodestate;
1221	LONG			wlong;
1222
1223	if (!windowed_mode_set)
1224	{
1225		if (COM_CheckParm ("-resetwinpos"))
1226		{
1227			Cvar_SetValue ("vid_window_x", 0.0);
1228			Cvar_SetValue ("vid_window_y", 0.0);
1229		}
1230
1231		windowed_mode_set;
1232	}
1233
1234	VID_CheckModedescFixup (modenum);
1235
1236	DDActive = 0;
1237	lastmodestate = modestate;
1238
1239	DestroyFullscreenWindow ();
1240	DestroyFullDIBWindow ();
1241
1242	if (windc)
1243		MGL_destroyDC(windc);
1244	if (dibdc)
1245		MGL_destroyDC(dibdc);
1246	windc = dibdc = NULL;
1247
1248// KJB: Signal to the MGL that we are going back to windowed mode
1249	if (!MGL_changeDisplayMode(grWINDOWED))
1250		initFatalError();
1251
1252	WindowRect.top = WindowRect.left = 0;
1253
1254	WindowRect.right = modelist[modenum].width;
1255	WindowRect.bottom = modelist[modenum].height;
1256	stretched = modelist[modenum].stretched;
1257
1258	DIBWidth = modelist[modenum].width;
1259	DIBHeight = modelist[modenum].height;
1260
1261	if (stretched)
1262	{
1263		DIBWidth >>= 1;
1264		DIBHeight >>= 1;
1265	}
1266
1267	WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU |
1268				  WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS |
1269				  WS_CLIPCHILDREN;
1270	ExWindowStyle = 0;
1271	AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0);
1272
1273// the first time we're called to set the mode, create the window we'll use
1274// for the rest of the session
1275	if (!vid_mode_set)
1276	{
1277		mainwindow = CreateWindowEx (
1278			 ExWindowStyle,
1279			 "WinQuake",
1280			 "WinQuake",
1281			 WindowStyle,
1282			 0, 0,
1283			 WindowRect.right - WindowRect.left,
1284			 WindowRect.bottom - WindowRect.top,
1285			 NULL,
1286			 NULL,
1287			 global_hInstance,
1288			 NULL);
1289
1290		if (!mainwindow)
1291			Sys_Error ("Couldn't create DIB window");
1292
1293	// tell MGL to use this window for fullscreen modes
1294		MGL_registerFullScreenWindow (mainwindow);
1295
1296		vid_mode_set = true;
1297	}
1298	else
1299	{
1300		SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE);
1301		SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle);
1302	}
1303
1304	if (!SetWindowPos (mainwindow,
1305					   NULL,
1306					   0, 0,
1307					   WindowRect.right - WindowRect.left,
1308					   WindowRect.bottom - WindowRect.top,
1309					   SWP_NOCOPYBITS | SWP_NOZORDER |
1310						SWP_HIDEWINDOW))
1311	{
1312		Sys_Error ("Couldn't resize DIB window");
1313	}
1314
1315	if (hide_window)
1316		return true;
1317
1318// position and show the DIB window
1319	VID_CheckWindowXY ();
1320	SetWindowPos (mainwindow, NULL, (int)vid_window_x.value,
1321				  (int)vid_window_y.value, 0, 0,
1322				  SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
1323
1324	if (force_minimized)
1325		ShowWindow (mainwindow, SW_MINIMIZE);
1326	else
1327		ShowWindow (mainwindow, SW_SHOWDEFAULT);
1328
1329	UpdateWindow (mainwindow);
1330
1331	modestate = MS_WINDOWED;
1332	vid_fulldib_on_focus_mode = 0;
1333
1334// because we have set the background brush for the window to NULL
1335// (to avoid flickering when re-sizing the window on the desktop),
1336// we clear the window to black when created, otherwise it will be
1337// empty while Quake starts up.
1338	hdc = GetDC(mainwindow);
1339	PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);
1340	ReleaseDC(mainwindow, hdc);
1341
1342	/* Create the MGL window DC and the MGL memory DC */
1343	if ((windc = MGL_createWindowedDC(mainwindow)) == NULL)
1344		MGL_fatalError("Unable to create Windowed DC!");
1345
1346	if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL)
1347		MGL_fatalError("Unable to create Memory DC!");
1348
1349	MGL_makeCurrentDC(dibdc);
1350
1351	vid.buffer = vid.conbuffer = vid.direct = dibdc->surface;
1352	vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine;
1353	vid.numpages = 1;
1354	vid.maxwarpwidth = WARP_WIDTH;
1355	vid.maxwarpheight = WARP_HEIGHT;
1356	vid.height = vid.conheight = DIBHeight;
1357	vid.width = vid.conwidth = DIBWidth;
1358	vid.aspect = ((float)vid.height / (float)vid.width) *
1359				(320.0 / 240.0);
1360
1361	vid_stretched = stretched;
1362
1363	SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon);
1364	SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon);
1365
1366	return true;
1367}
1368
1369
1370qboolean VID_SetFullscreenMode (int modenum)
1371{
1372
1373	DDActive = 1;
1374
1375	DestroyDIBWindow ();
1376	DestroyFullDIBWindow ();
1377
1378	mode = modelist[modenum].modenum;
1379
1380	// Destroy old DC's, resetting back to fullscreen mode
1381	if (mgldc)
1382		MGL_destroyDC (mgldc);
1383	if (memdc)
1384		MGL_destroyDC (memdc);
1385	mgldc = memdc = NULL;
1386
1387	if ((mgldc = createDisplayDC (modelist[modenum].stretched ||
1388		 (int)vid_nopageflip.value)) == NULL)
1389	{
1390		return false;
1391	}
1392
1393	modestate = MS_FULLSCREEN;
1394	vid_fulldib_on_focus_mode = 0;
1395
1396	vid.buffer = vid.conbuffer = vid.direct = NULL;
1397	vid.maxwarpwidth = WARP_WIDTH;
1398	vid.maxwarpheight = WARP_HEIGHT;
1399	DIBHeight = vid.height = vid.conheight = modelist[modenum].height;
1400	DIBWidth = vid.width = vid.conwidth = modelist[modenum].width;
1401	vid.aspect = ((float)vid.height / (float)vid.width) *
1402				(320.0 / 240.0);
1403
1404	vid_stretched = modelist[modenum].stretched;
1405
1406// needed because we're not getting WM_MOVE messages fullscreen on NT
1407	window_x = 0;
1408	window_y = 0;
1409
1410// set the large icon, so the Quake icon will show up in the taskbar
1411	SendMessage (mainwindow, WM_SETICON, (WPARAM)1, (LPARAM)hIcon);
1412	SendMessage (mainwindow, WM_SETICON, (WPARAM)0, (LPARAM)hIcon);
1413
1414// shouldn't be needed, but Kendall needs to let us get the activation
1415// message for this not to be needed on NT
1416	AppActivate (true, false);
1417
1418	return true;
1419}
1420
1421
1422qboolean VID_SetFullDIBMode (int modenum)
1423{
1424	HDC				hdc;
1425	pixel_format_t	pf;
1426	int				lastmodestate;
1427
1428	DDActive = 0;
1429
1430	DestroyFullscreenWindow ();
1431	DestroyDIBWindow ();
1432
1433	if (windc)
1434		MGL_destroyDC(windc);
1435	if (dibdc)
1436		MGL_destroyDC(dibdc);
1437	windc = dibdc = NULL;
1438
1439// KJB: Signal to the MGL that we are going back to windowed mode
1440	if (!MGL_changeDisplayMode(grWINDOWED))
1441		initFatalError();
1442
1443	gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
1444	gdevmode.dmBitsPerPel = modelist[modenum].bpp;
1445	gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].stretched <<
1446						   modelist[modenum].halfscreen;
1447	gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched;
1448	gdevmode.dmSize = sizeof (gdevmode);
1449
1450	if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1451		Sys_Error ("Couldn't set fullscreen DIB mode");
1452
1453	lastmodestate = modestate;
1454	modestate = MS_FULLDIB;
1455	vid_fulldib_on_focus_mode = modenum;
1456
1457	WindowRect.top = WindowRect.left = 0;
1458
1459	hdc = GetDC(NULL);
1460
1461	WindowRect.right = modelist[modenum].width << modelist[modenum].stretched;
1462	WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched;
1463
1464	ReleaseDC(NULL,hdc);
1465
1466	DIBWidth = modelist[modenum].width;
1467	DIBHeight = modelist[modenum].height;
1468
1469	WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1470	ExWindowStyle = 0;
1471	AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0);
1472
1473	SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE);
1474	SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle);
1475
1476	if (!SetWindowPos (mainwindow,
1477					   NULL,
1478					   0, 0,
1479					   WindowRect.right - WindowRect.left,
1480					   WindowRect.bottom - WindowRect.top,
1481					   SWP_NOCOPYBITS | SWP_NOZORDER))
1482	{
1483		Sys_Error ("Couldn't resize DIB window");
1484	}
1485
1486// position and show the DIB window
1487	SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0,
1488				  SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME);
1489	ShowWindow (mainwindow, SW_SHOWDEFAULT);
1490	UpdateWindow (mainwindow);
1491
1492	// Because we have set the background brush for the window to NULL
1493	// (to avoid flickering when re-sizing the window on the desktop), we
1494	// clear the window to black when created, otherwise it will be
1495	// empty while Quake starts up.
1496	hdc = GetDC(mainwindow);
1497	PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);
1498	ReleaseDC(mainwindow, hdc);
1499
1500	/* Create the MGL window DC and the MGL memory DC */
1501	if ((windc = MGL_createWindowedDC(mainwindow)) == NULL)
1502		MGL_fatalError("Unable to create Fullscreen DIB DC!");
1503
1504	if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL)
1505		MGL_fatalError("Unable to create Memory DC!");
1506
1507	MGL_makeCurrentDC(dibdc);
1508
1509	vid.buffer = vid.conbuffer = vid.direct = dibdc->surface;
1510	vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine;
1511	vid.numpages = 1;
1512	vid.maxwarpwidth = WARP_WIDTH;
1513	vid.maxwarpheight = WARP_HEIGHT;
1514	vid.height = vid.conheight = DIBHeight;
1515	vid.width = vid.conwidth = DIBWidth;
1516	vid.aspect = ((float)vid.height / (float)vid.width) *
1517				(320.0 / 240.0);
1518
1519	vid_stretched = modelist[modenum].stretched;
1520
1521// needed because we're not getting WM_MOVE messages fullscreen on NT
1522	window_x = 0;
1523	window_y = 0;
1524
1525	return true;
1526}
1527
1528
1529void VID_RestoreOldMode (int original_mode)
1530{
1531	static qboolean	inerror = false;
1532
1533	if (inerror)
1534		return;
1535
1536	in_mode_set = false;
1537	inerror = true;
1538
1539// make sure mode set happens (video mode changes)
1540	vid_modenum = original_mode - 1;
1541
1542	if (!VID_SetMode (original_mode, vid_curpal))
1543	{
1544		vid_modenum = MODE_WINDOWED - 1;
1545
1546		if (!VID_SetMode (windowed_default, vid_curpal))
1547			Sys_Error ("Can't set any video mode");
1548	}
1549
1550	inerror = false;
1551}
1552
1553
1554void VID_SetDefaultMode (void)
1555{
1556
1557	if (vid_initialized)
1558		VID_SetMode (0, vid_curpal);
1559
1560	IN_DeactivateMouse ();
1561}
1562
1563
1564int VID_SetMode (int modenum, unsigned char *palette)
1565{
1566	int				original_mode, temp, dummy;
1567	qboolean		stat;
1568    MSG				msg;
1569	HDC				hdc;
1570
1571	while ((modenum >= nummodes) || (modenum < 0))
1572	{
1573		if (vid_modenum == NO_MODE)
1574		{
1575			if (modenum == vid_default)
1576			{
1577				modenum = windowed_default;
1578			}
1579			else
1580			{
1581				modenum = vid_default;
1582			}
1583
1584			Cvar_SetValue ("vid_mode", (float)modenum);
1585		}
1586		else
1587		{
1588			Cvar_SetValue ("vid_mode", (float)vid_modenum);
1589			return 0;
1590		}
1591	}
1592
1593	if (!force_mode_set && (modenum == vid_modenum))
1594		return true;
1595
1596// so Con_Printfs don't mess us up by forcing vid and snd updates
1597	temp = scr_disabled_for_loading;
1598	scr_disabled_for_loading = true;
1599	in_mode_set = true;
1600
1601	CDAudio_Pause ();
1602	S_ClearBuffer ();
1603
1604	if (vid_modenum == NO_MODE)
1605		original_mode = windowed_default;
1606	else
1607		original_mode = vid_modenum;
1608
1609	// Set either the fullscreen or windowed mode
1610	if (modelist[modenum].type == MS_WINDOWED)
1611	{
1612		if (_windowed_mouse.value)
1613		{
1614			stat = VID_SetWindowedMode(modenum);
1615			IN_ActivateMouse ();
1616			IN_HideMouse ();
1617		}
1618		else
1619		{
1620			IN_DeactivateMouse ();
1621			IN_ShowMouse ();
1622			stat = VID_SetWindowedMode(modenum);
1623		}
1624	}
1625	else if (modelist[modenum].type == MS_FULLDIB)
1626	{
1627		stat = VID_SetFullDIBMode(modenum);
1628		IN_ActivateMouse ();
1629		IN_HideMouse ();
1630	}
1631	else
1632	{
1633		stat = VID_SetFullscreenMode(modenum);
1634		IN_ActivateMouse ();
1635		IN_HideMouse ();
1636	}
1637
1638	window_width = vid.width << vid_stretched;
1639	window_height = vid.height << vid_stretched;
1640	VID_UpdateWindowStatus ();
1641
1642	CDAudio_Resume ();
1643	scr_disabled_for_loading = temp;
1644
1645	if (!stat)
1646	{
1647		VID_RestoreOldMode (original_mode);
1648		return false;
1649	}
1650
1651	if (hide_window)
1652		return true;
1653
1654// now we try to make sure we get the focus on the mode switch, because
1655// sometimes in some systems we don't.  We grab the foreground, then
1656// finish setting up, pump all our messages, and sleep for a little while
1657// to let messages finish bouncing around the system, then we put
1658// ourselves at the top of the z order, then grab the foreground again,
1659// Who knows if it helps, but it probably doesn't hurt
1660	if (!force_minimized)
1661		SetForegroundWindow (mainwindow);
1662
1663	hdc = GetDC(NULL);
1664
1665	if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
1666		vid_palettized = true;
1667	else
1668		vid_palettized = false;
1669
1670	VID_SetPalette (palette);
1671
1672	ReleaseDC(NULL,hdc);
1673
1674	vid_modenum = modenum;
1675	Cvar_SetValue ("vid_mode", (float)vid_modenum);
1676
1677	if (!VID_AllocBuffers (vid.width, vid.height))
1678	{
1679	// couldn't get memory for this mode; try to fall back to previous mode
1680		VID_RestoreOldMode (original_mode);
1681		return false;
1682	}
1683
1684	D_InitCaches (vid_surfcache, vid_surfcachesize);
1685
1686	while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1687	{
1688      	TranslateMessage (&msg);
1689      	DispatchMessage (&msg);
1690	}
1691
1692	Sleep (100);
1693
1694	if (!force_minimized)
1695	{
1696		SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0,
1697				  SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
1698				  SWP_NOCOPYBITS);
1699
1700		SetForegroundWindow (mainwindow);
1701	}
1702
1703// fix the leftover Alt from any Alt-Tab or the like that switched us away
1704	ClearAllStates ();
1705
1706	if (!msg_suppress_1)
1707		Con_SafePrintf ("%s\n", VID_GetModeDescription (vid_modenum));
1708
1709	VID_SetPalette (palette);
1710
1711	in_mode_set = false;
1712	vid.recalc_refdef = 1;
1713
1714	return true;
1715}
1716
1717void VID_LockBuffer (void)
1718{
1719
1720	if (dibdc)
1721		return;
1722
1723	lockcount++;
1724
1725	if (lockcount > 1)
1726		return;
1727
1728	MGL_beginDirectAccess();
1729
1730	if (memdc)
1731	{
1732		// Update surface pointer for linear access modes
1733		vid.buffer = vid.conbuffer = vid.direct = memdc->surface;
1734		vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine;
1735	}
1736	else if (mgldc)
1737	{
1738		// Update surface pointer for linear access modes
1739		vid.buffer = vid.conbuffer = vid.direct = mgldc->surface;
1740		vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine;
1741	}
1742
1743	if (r_dowarp)
1744		d_viewbuffer = r_warpbuffer;
1745	else
1746		d_viewbuffer = (void *)(byte *)vid.buffer;
1747
1748	if (r_dowarp)
1749		screenwidth = WARP_WIDTH;
1750	else
1751		screenwidth = vid.rowbytes;
1752
1753	if (lcd_x.value)
1754		screenwidth <<= 1;
1755}
1756
1757
1758void VID_UnlockBuffer (void)
1759{
1760	if (dibdc)
1761		return;
1762
1763	lockcount--;
1764
1765	if (lockcount > 0)
1766		return;
1767
1768	if (lockcount < 0)
1769		Sys_Error ("Unbalanced unlock");
1770
1771	MGL_endDirectAccess();
1772
1773// to turn up any unlocked accesses
1774	vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL;
1775
1776}
1777
1778
1779int VID_ForceUnlockedAndReturnState (void)
1780{
1781	int	lk;
1782
1783	if (!lockcount)
1784		return 0;
1785
1786	lk = lockcount;
1787
1788	if (dibdc)
1789	{
1790		lockcount = 0;
1791	}
1792	else
1793	{
1794		lockcount = 1;
1795		VID_UnlockBuffer ();
1796	}
1797
1798	return lk;
1799}
1800
1801
1802void VID_ForceLockState (int lk)
1803{
1804
1805	if (!dibdc && lk)
1806	{
1807		lockcount = 0;
1808		VID_LockBuffer ();
1809	}
1810
1811	lockcount = lk;
1812}
1813
1814
1815void	VID_SetPalette (unsigned char *palette)
1816{
1817	INT			i;
1818	palette_t	pal[256];
1819    HDC			hdc;
1820
1821	if (!Minimized)
1822	{
1823		palette_changed = true;
1824
1825	// make sure we have the static colors if we're the active app
1826		hdc = GetDC(NULL);
1827
1828		if (vid_palettized && ActiveApp)
1829		{
1830			if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC)
1831			{
1832			// switch to SYSPAL_NOSTATIC and remap the colors
1833				SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
1834				syscolchg = true;
1835				pal_is_nostatic = true;
1836			}
1837		}
1838
1839		ReleaseDC(NULL,hdc);
1840
1841		// Translate the palette values to an MGL palette array and
1842		// set the values.
1843		for (i = 0; i < 256; i++)
1844		{
1845			pal[i].red = palette[i*3];
1846			pal[i].green = palette[i*3+1];
1847			pal[i].blue = palette[i*3+2];
1848		}
1849
1850		if (DDActive)
1851		{
1852			if (!mgldc)
1853				return;
1854
1855			MGL_setPalette(mgldc,pal,256,0);
1856			MGL_realizePalette(mgldc,256,0,false);
1857			if (memdc)
1858				MGL_setPalette(memdc,pal,256,0);
1859		}
1860		else
1861		{
1862			if (!windc)
1863				return;
1864
1865			MGL_setPalette(windc,pal,256,0);
1866			MGL_realizePalette(windc,256,0,false);
1867			if (dibdc)
1868			{
1869				MGL_setPalette(dibdc,pal,256,0);
1870				MGL_realizePalette(dibdc,256,0,false);
1871			}
1872		}
1873	}
1874
1875	memcpy (vid_curpal, palette, sizeof(vid_curpal));
1876
1877	if (syscolchg)
1878	{
1879		PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0);
1880		syscolchg = false;
1881	}
1882}
1883
1884
1885void	VID_ShiftPalette (unsigned char *palette)
1886{
1887	VID_SetPalette (palette);
1888}
1889
1890
1891/*
1892=================
1893VID_DescribeCurrentMode_f
1894=================
1895*/
1896void VID_DescribeCurrentMode_f (void)
1897{
1898	Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum));
1899}
1900
1901
1902/*
1903=================
1904VID_NumModes_f
1905=================
1906*/
1907void VID_NumModes_f (void)
1908{
1909
1910	if (nummodes == 1)
1911		Con_Printf ("%d video mode is available\n", nummodes);
1912	else
1913		Con_Printf ("%d video modes are available\n", nummodes);
1914}
1915
1916
1917/*
1918=================
1919VID_DescribeMode_f
1920=================
1921*/
1922void VID_DescribeMode_f (void)
1923{
1924	int		modenum;
1925
1926	modenum = Q_atoi (Cmd_Argv(1));
1927
1928	Con_Printf ("%s\n", VID_GetExtModeDescription (modenum));
1929}
1930
1931
1932/*
1933=================
1934VID_DescribeModes_f
1935=================
1936*/
1937void VID_DescribeModes_f (void)
1938{
1939	int			i, lnummodes;
1940	char		*pinfo;
1941	qboolean	na;
1942	vmode_t		*pv;
1943
1944	na = false;
1945
1946	lnummodes = VID_NumModes ();
1947
1948	for (i=0 ; i<lnummodes ; i++)
1949	{
1950		pv = VID_GetModePtr (i);
1951		pinfo = VID_GetExtModeDescription (i);
1952
1953		if (VID_CheckAdequateMem (pv->width, pv->height))
1954		{
1955			Con_Printf ("%2d: %s\n", i, pinfo);
1956		}
1957		else
1958		{
1959			Con_Printf ("**: %s\n", pinfo);
1960			na = true;
1961		}
1962	}
1963
1964	if (na)
1965	{
1966		Con_Printf ("\n[**: not enough system RAM for mode]\n");
1967	}
1968}
1969
1970
1971/*
1972=================
1973VID_TestMode_f
1974=================
1975*/
1976void VID_TestMode_f (void)
1977{
1978	int		modenum;
1979	double	testduration;
1980
1981	if (!vid_testingmode)
1982	{
1983		modenum = Q_atoi (Cmd_Argv(1));
1984
1985		if (VID_SetMode (modenum, vid_curpal))
1986		{
1987			vid_testingmode = 1;
1988			testduration = Q_atof (Cmd_Argv(2));
1989			if (testduration == 0)
1990				testduration = 5.0;
1991			vid_testendtime = realtime + testduration;
1992		}
1993	}
1994}
1995
1996
1997/*
1998=================
1999VID_Windowed_f
2000=================
2001*/
2002void VID_Windowed_f (void)
2003{
2004
2005	VID_SetMode ((int)vid_windowed_mode.value, vid_curpal);
2006}
2007
2008
2009/*
2010=================
2011VID_Fullscreen_f
2012=================
2013*/
2014void VID_Fullscreen_f (void)
2015{
2016
2017	VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal);
2018}
2019
2020
2021/*
2022=================
2023VID_Minimize_f
2024=================
2025*/
2026void VID_Minimize_f (void)
2027{
2028
2029// we only support minimizing windows; if you're fullscreen,
2030// switch to windowed first
2031	if (modestate == MS_WINDOWED)
2032		ShowWindow (mainwindow, SW_MINIMIZE);
2033}
2034
2035
2036
2037/*
2038=================
2039VID_ForceMode_f
2040=================
2041*/
2042void VID_ForceMode_f (void)
2043{
2044	int		modenum;
2045	double	testduration;
2046
2047	if (!vid_testingmode)
2048	{
2049		modenum = Q_atoi (Cmd_Argv(1));
2050
2051		force_mode_set = 1;
2052		VID_SetMode (modenum, vid_curpal);
2053		force_mode_set = 0;
2054	}
2055}
2056
2057
2058void	VID_Init (unsigned char *palette)
2059{
2060	int		i, bestmatch, bestmatchmetric, t, dr, dg, db;
2061	int		basenummodes;
2062	byte	*ptmp;
2063
2064	Cvar_RegisterVariable (&vid_mode);
2065	Cvar_RegisterVariable (&vid_wait);
2066	Cvar_RegisterVariable (&vid_nopageflip);
2067	Cvar_RegisterVariable (&_vid_wait_override);
2068	Cvar_RegisterVariable (&_vid_default_mode);
2069	Cvar_RegisterVariable (&_vid_default_mode_win);
2070	Cvar_RegisterVariable (&vid_config_x);
2071	Cvar_RegisterVariable (&vid_config_y);
2072	Cvar_RegisterVariable (&vid_stretch_by_2);
2073	Cvar_RegisterVariable (&_windowed_mouse);
2074	Cvar_RegisterVariable (&vid_fullscreen_mode);
2075	Cvar_RegisterVariable (&vid_windowed_mode);
2076	Cvar_RegisterVariable (&block_switch);
2077	Cvar_RegisterVariable (&vid_window_x);
2078	Cvar_RegisterVariable (&vid_window_y);
2079
2080	Cmd_AddCommand ("vid_testmode", VID_TestMode_f);
2081	Cmd_AddCommand ("vid_nummodes", VID_NumModes_f);
2082	Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f);
2083	Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f);
2084	Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f);
2085	Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f);
2086	Cmd_AddCommand ("vid_windowed", VID_Windowed_f);
2087	Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f);
2088	Cmd_AddCommand ("vid_minimize", VID_Minimize_f);
2089
2090	if (COM_CheckParm ("-dibonly"))
2091		dibonly = true;
2092
2093	VID_InitMGLDIB (global_hInstance);
2094
2095	basenummodes = nummodes;
2096
2097	if (!dibonly)
2098		VID_InitMGLFull (global_hInstance);
2099
2100// if there are no non-windowed modes, or only windowed and mode 0x13, then use
2101// fullscreen DIBs as well
2102	if (((nummodes == basenummodes) ||
2103		 ((nummodes == (basenummodes + 1)) && is_mode0x13)) &&
2104		!COM_CheckParm ("-nofulldib"))
2105
2106	{
2107		VID_InitFullDIB (global_hInstance);
2108	}
2109
2110	vid.maxwarpwidth = WARP_WIDTH;
2111	vid.maxwarpheight = WARP_HEIGHT;
2112	vid.colormap = host_colormap;
2113	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
2114	vid_testingmode = 0;
2115
2116// GDI doesn't let us remap palette index 0, so we'll remap color
2117// mappings from that black to another one
2118	bestmatchmetric = 256*256*3;
2119
2120	for (i=1 ; i<256 ; i++)
2121	{
2122		dr = palette[0] - palette[i*3];
2123		dg = palette[1] - palette[i*3+1];
2124		db = palette[2] - palette[i*3+2];
2125
2126		t = (dr * dr) + (dg * dg) + (db * db);
2127
2128		if (t < bestmatchmetric)
2129		{
2130			bestmatchmetric = t;
2131			bestmatch = i;
2132
2133			if (t == 0)
2134				break;
2135		}
2136	}
2137
2138	for (i=0, ptmp = vid.colormap ; i<(1<<(VID_CBITS+8)) ; i++, ptmp++)
2139	{
2140		if (*ptmp == 0)
2141			*ptmp = bestmatch;
2142	}
2143
2144	if (COM_CheckParm("-startwindowed"))
2145	{
2146		startwindowed = 1;
2147		vid_default = windowed_default;
2148	}
2149
2150	if (hwnd_dialog)
2151		DestroyWindow (hwnd_dialog);
2152
2153// sound initialization has to go here, preceded by a windowed mode set,
2154// so there's a window for DirectSound to work with but we're not yet
2155// fullscreen so the "hardware already in use" dialog is visible if it
2156// gets displayed
2157
2158// keep the window minimized until we're ready for the first real mode set
2159	hide_window = true;
2160	VID_SetMode (MODE_WINDOWED, palette);
2161	hide_window = false;
2162	S_Init ();
2163
2164	vid_initialized = true;
2165
2166	force_mode_set = true;
2167	VID_SetMode (vid_default, palette);
2168	force_mode_set = false;
2169
2170	vid_realmode = vid_modenum;
2171
2172	VID_SetPalette (palette);
2173
2174	vid_menudrawfn = VID_MenuDraw;
2175	vid_menukeyfn = VID_MenuKey;
2176
2177	strcpy (badmode.modedesc, "Bad mode");
2178}
2179
2180
2181void	VID_Shutdown (void)
2182{
2183	HDC				hdc;
2184	int				dummy;
2185
2186	if (vid_initialized)
2187	{
2188		if (modestate == MS_FULLDIB)
2189			ChangeDisplaySettings (NULL, CDS_FULLSCREEN);
2190
2191		PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0);
2192		PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0);
2193
2194		AppActivate(false, false);
2195		DestroyDIBWindow ();
2196		DestroyFullscreenWindow ();
2197		DestroyFullDIBWindow ();
2198
2199		if (hwnd_dialog)
2200			DestroyWindow (hwnd_dialog);
2201
2202		if (mainwindow)
2203			DestroyWindow(mainwindow);
2204
2205		MGL_exit();
2206
2207		vid_testingmode = 0;
2208		vid_initialized = 0;
2209	}
2210}
2211
2212
2213/*
2214================
2215FlipScreen
2216================
2217*/
2218void FlipScreen(vrect_t *rects)
2219{
2220	HRESULT		ddrval;
2221
2222	// Flip the surfaces
2223
2224	if (DDActive)
2225	{
2226		if (mgldc)
2227		{
2228			if (memdc)
2229			{
2230				while (rects)
2231				{
2232					if (vid_stretched)
2233					{
2234						MGL_stretchBltCoord(mgldc, memdc,
2235									rects->x,
2236									rects->y,
2237									rects->x + rects->width,
2238									rects->y + rects->height,
2239									rects->x << 1,
2240									rects->y << 1,
2241									(rects->x + rects->width) << 1,
2242									(rects->y + rects->height) << 1);
2243					}
2244					else
2245					{
2246						MGL_bitBltCoord(mgldc, memdc,
2247									rects->x, rects->y,
2248									(rects->x + rects->width),
2249									(rects->y + rects->height),
2250									rects->x, rects->y, MGL_REPLACE_MODE);
2251					}
2252
2253					rects = rects->pnext;
2254				}
2255			}
2256
2257			if (vid.numpages > 1)
2258			{
2259				// We have a flipping surface, so do a hard page flip
2260				aPage = (aPage+1) % vid.numpages;
2261				vPage = (vPage+1) % vid.numpages;
2262				MGL_setActivePage(mgldc,aPage);
2263				MGL_setVisualPage(mgldc,vPage,waitVRT);
2264			}
2265		}
2266	}
2267	else
2268	{
2269		HDC hdcScreen;
2270
2271		hdcScreen = GetDC(mainwindow);
2272
2273		if (windc && dibdc)
2274		{
2275			MGL_setWinDC(windc,hdcScreen);
2276
2277			while (rects)
2278			{
2279				if (vid_stretched)
2280				{
2281					MGL_stretchBltCoord(windc,dibdc,
2282						rects->x, rects->y,
2283						rects->x + rects->width, rects->y + rects->height,
2284						rects->x << 1, rects->y << 1,
2285						(rects->x + rects->width) << 1,
2286						(rects->y + rects->height) << 1);
2287				}
2288				else
2289				{
2290					MGL_bitBltCoord(windc,dibdc,
2291						rects->x, rects->y,
2292						rects->x + rects->width, rects->y + rects->height,
2293						rects->x, rects->y, MGL_REPLACE_MODE);
2294				}
2295
2296				rects = rects->pnext;
2297			}
2298		}
2299
2300		ReleaseDC(mainwindow, hdcScreen);
2301	}
2302}
2303
2304
2305void	VID_Update (vrect_t *rects)
2306{
2307	vrect_t	rect;
2308	RECT	trect;
2309
2310	if (!vid_palettized && palette_changed)
2311	{
2312		palette_changed = false;
2313		rect.x = 0;
2314		rect.y = 0;
2315		rect.width = vid.width;
2316		rect.height = vid.height;
2317		rect.pnext = NULL;
2318		rects = &rect;
2319	}
2320
2321	if (firstupdate)
2322	{
2323		if (modestate == MS_WINDOWED)
2324		{
2325			GetWindowRect (mainwindow, &trect);
2326
2327			if ((trect.left != (int)vid_window_x.value) ||
2328				(trect.top  != (int)vid_window_y.value))
2329			{
2330				if (COM_CheckParm ("-resetwinpos"))
2331				{
2332					Cvar_SetValue ("vid_window_x", 0.0);
2333					Cvar_SetValue ("vid_window_y", 0.0);
2334				}
2335
2336				VID_CheckWindowXY ();
2337				SetWindowPos (mainwindow, NULL, (int)vid_window_x.value,
2338				  (int)vid_window_y.value, 0, 0,
2339				  SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
2340			}
2341		}
2342
2343		if ((_vid_default_mode_win.value != vid_default) &&
2344			(!startwindowed || (_vid_default_mode_win.value < MODE_FULLSCREEN_DEFAULT)))
2345		{
2346			firstupdate = 0;
2347
2348			if (COM_CheckParm ("-resetwinpos"))
2349			{
2350				Cvar_SetValue ("vid_window_x", 0.0);
2351				Cvar_SetValue ("vid_window_y", 0.0);
2352			}
2353
2354			if ((_vid_default_mode_win.value < 0) ||
2355				(_vid_default_mode_win.value >= nummodes))
2356			{
2357				Cvar_SetValue ("_vid_default_mode_win", windowed_default);
2358			}
2359
2360			Cvar_SetValue ("vid_mode", _vid_default_mode_win.value);
2361		}
2362	}
2363
2364	// We've drawn the frame; copy it to the screen
2365	FlipScreen (rects);
2366
2367	if (vid_testingmode)
2368	{
2369		if (realtime >= vid_testendtime)
2370		{
2371			VID_SetMode (vid_realmode, vid_curpal);
2372			vid_testingmode = 0;
2373		}
2374	}
2375	else
2376	{
2377		if ((int)vid_mode.value != vid_realmode)
2378		{
2379			VID_SetMode ((int)vid_mode.value, vid_curpal);
2380			Cvar_SetValue ("vid_mode", (float)vid_modenum);
2381								// so if mode set fails, we don't keep on
2382								//  trying to set that mode
2383			vid_realmode = vid_modenum;
2384		}
2385	}
2386
2387// handle the mouse state when windowed if that's changed
2388	if (modestate == MS_WINDOWED)
2389	{
2390		if ((int)_windowed_mouse.value != windowed_mouse)
2391		{
2392			if (_windowed_mouse.value)
2393			{
2394				IN_ActivateMouse ();
2395				IN_HideMouse ();
2396			}
2397			else
2398			{
2399				IN_DeactivateMouse ();
2400				IN_ShowMouse ();
2401			}
2402
2403			windowed_mouse = (int)_windowed_mouse.value;
2404		}
2405	}
2406}
2407
2408
2409/*
2410================
2411D_BeginDirectRect
2412================
2413*/
2414void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
2415{
2416	int		i, j, reps, repshift;
2417	vrect_t	rect;
2418
2419	if (!vid_initialized)
2420		return;
2421
2422	if (vid.aspect > 1.5)
2423	{
2424		reps = 2;
2425		repshift = 1;
2426	}
2427	else
2428	{
2429		reps = 1;
2430		repshift = 0;
2431	}
2432
2433	if (vid.numpages == 1)
2434	{
2435		VID_LockBuffer ();
2436
2437		if (!vid.direct)
2438			Sys_Error ("NULL vid.direct pointer");
2439
2440		for (i=0 ; i<(height << repshift) ; i += reps)
2441		{
2442			for (j=0 ; j<reps ; j++)
2443			{
2444				memcpy (&backingbuf[(i + j) * 24],
2445						vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes,
2446						width);
2447				memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes,
2448						&pbitmap[(i >> repshift) * width],
2449						width);
2450			}
2451		}
2452
2453		VID_UnlockBuffer ();
2454
2455		rect.x = x;
2456		rect.y = y;
2457		rect.width = width;
2458		rect.height = height << repshift;
2459		rect.pnext = NULL;
2460
2461		FlipScreen (&rect);
2462	}
2463	else
2464	{
2465	// unlock if locked
2466		if (lockcount > 0)
2467			MGL_endDirectAccess();
2468
2469	// set the active page to the displayed page
2470		MGL_setActivePage (mgldc, vPage);
2471
2472	// lock the screen
2473		MGL_beginDirectAccess ();
2474
2475	// save from and draw to screen
2476		for (i=0 ; i<(height << repshift) ; i += reps)
2477		{
2478			for (j=0 ; j<reps ; j++)
2479			{
2480				memcpy (&backingbuf[(i + j) * 24],
2481						(byte *)mgldc->surface + x +
2482						 ((y << repshift) + i + j) * mgldc->mi.bytesPerLine,
2483						width);
2484				memcpy ((byte *)mgldc->surface + x +
2485						 ((y << repshift) + i + j) * mgldc->mi.bytesPerLine,
2486						&pbitmap[(i >> repshift) * width],
2487						width);
2488			}
2489		}
2490
2491	// unlock the screen
2492		MGL_endDirectAccess ();
2493
2494	// restore the original active page
2495		MGL_setActivePage (mgldc, aPage);
2496
2497	// relock the screen if it was locked
2498		if (lockcount > 0)
2499			MGL_beginDirectAccess();
2500	}
2501}
2502
2503
2504/*
2505================
2506D_EndDirectRect
2507================
2508*/
2509void D_EndDirectRect (int x, int y, int width, int height)
2510{
2511	int		i, j, reps, repshift;
2512	vrect_t	rect;
2513
2514	if (!vid_initialized)
2515		return;
2516
2517	if (vid.aspect > 1.5)
2518	{
2519		reps = 2;
2520		repshift = 1;
2521	}
2522	else
2523	{
2524		reps = 1;
2525		repshift = 0;
2526	}
2527
2528	if (vid.numpages == 1)
2529	{
2530		VID_LockBuffer ();
2531
2532		if (!vid.direct)
2533			Sys_Error ("NULL vid.direct pointer");
2534
2535		for (i=0 ; i<(height << repshift) ; i += reps)
2536		{
2537			for (j=0 ; j<reps ; j++)
2538			{
2539				memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes,
2540						&backingbuf[(i + j) * 24],
2541						width);
2542			}
2543		}
2544
2545		VID_UnlockBuffer ();
2546
2547		rect.x = x;
2548		rect.y = y;
2549		rect.width = width;
2550		rect.height = height << repshift;
2551		rect.pnext = NULL;
2552
2553		FlipScreen (&rect);
2554	}
2555	else
2556	{
2557	// unlock if locked
2558		if (lockcount > 0)
2559			MGL_endDirectAccess();
2560
2561	// set the active page to the displayed page
2562		MGL_setActivePage (mgldc, vPage);
2563
2564	// lock the screen
2565		MGL_beginDirectAccess ();
2566
2567	// restore to the screen
2568		for (i=0 ; i<(height << repshift) ; i += reps)
2569		{
2570			for (j=0 ; j<reps ; j++)
2571			{
2572				memcpy ((byte *)mgldc->surface + x +
2573						 ((y << repshift) + i + j) * mgldc->mi.bytesPerLine,
2574						&backingbuf[(i + j) * 24],
2575						width);
2576			}
2577		}
2578
2579	// unlock the screen
2580		MGL_endDirectAccess ();
2581
2582	// restore the original active page
2583		MGL_setActivePage (mgldc, aPage);
2584
2585	// relock the screen if it was locked
2586		if (lockcount > 0)
2587			MGL_beginDirectAccess();
2588	}
2589}
2590
2591
2592//==========================================================================
2593
2594byte        scantokey[128] =
2595					{
2596//  0           1       2       3       4       5       6       7
2597//  8           9       A       B       C       D       E       F
2598	0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6',
2599	'7',    '8',    '9',    '0',    '-',    '=',    K_BACKSPACE, 9, // 0
2600	'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
2601	'o',    'p',    '[',    ']',    13 ,    K_CTRL,'a',  's',      // 1
2602	'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
2603	'\'' ,    '`',    K_SHIFT,'\\',  'z',    'x',    'c',    'v',      // 2
2604	'b',    'n',    'm',    ',',    '.',    '/',    K_SHIFT,'*',
2605	K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3
2606	K_F6, K_F7, K_F8, K_F9, K_F10,  K_PAUSE,    0  , K_HOME,
2607	K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, //4
2608	K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11,
2609	K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
2610	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
2611	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6
2612	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
2613	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7
2614};
2615
2616/*
2617=======
2618MapKey
2619
2620Map from windows to quake keynums
2621=======
2622*/
2623int MapKey (int key)
2624{
2625	key = (key>>16)&255;
2626	if (key > 127)
2627		return 0;
2628
2629	return scantokey[key];
2630}
2631
2632void AppActivate(BOOL fActive, BOOL minimize)
2633/****************************************************************************
2634*
2635* Function:     AppActivate
2636* Parameters:   fActive - True if app is activating
2637*
2638* Description:  If the application is activating, then swap the system
2639*               into SYSPAL_NOSTATIC mode so that our palettes will display
2640*               correctly.
2641*
2642****************************************************************************/
2643{
2644    HDC			hdc;
2645    int			i, t;
2646	static BOOL	sound_active;
2647
2648	ActiveApp = fActive;
2649
2650// messy, but it seems to work
2651	if (vid_fulldib_on_focus_mode)
2652	{
2653		Minimized = minimize;
2654
2655		if (Minimized)
2656			ActiveApp = false;
2657	}
2658
2659	MGL_appActivate(windc, ActiveApp);
2660
2661	if (vid_initialized)
2662	{
2663	// yield the palette if we're losing the focus
2664		hdc = GetDC(NULL);
2665
2666		if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
2667		{
2668			if (ActiveApp)
2669			{
2670				if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB))
2671				{
2672					if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC)
2673					{
2674					// switch to SYSPAL_NOSTATIC and remap the colors
2675						SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
2676						syscolchg = true;
2677						pal_is_nostatic = true;
2678					}
2679				}
2680			}
2681			else if (pal_is_nostatic)
2682			{
2683				if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC)
2684				{
2685				// switch back to SYSPAL_STATIC and the old mapping
2686					SetSystemPaletteUse(hdc, SYSPAL_STATIC);
2687					syscolchg = true;
2688				}
2689
2690				pal_is_nostatic = false;
2691			}
2692		}
2693
2694		if (!Minimized)
2695			VID_SetPalette (vid_curpal);
2696
2697		scr_fullupdate = 0;
2698
2699		ReleaseDC(NULL,hdc);
2700	}
2701
2702// enable/disable sound on focus gain/loss
2703	if (!ActiveApp && sound_active)
2704	{
2705		S_BlockSound ();
2706		S_ClearBuffer ();
2707		sound_active = false;
2708	}
2709	else if (ActiveApp && !sound_active)
2710	{
2711		S_UnblockSound ();
2712		S_ClearBuffer ();
2713		sound_active = true;
2714	}
2715
2716// minimize/restore fulldib windows/mouse-capture normal windows on demand
2717	if (!in_mode_set)
2718	{
2719		if (ActiveApp)
2720		{
2721			if (vid_fulldib_on_focus_mode)
2722			{
2723				if (vid_initialized)
2724				{
2725					msg_suppress_1 = true;	// don't want to see normal mode set message
2726					VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal);
2727					msg_suppress_1 = false;
2728
2729					t = in_mode_set;
2730					in_mode_set = true;
2731					AppActivate (true, false);
2732					in_mode_set = t;
2733				}
2734
2735				IN_ActivateMouse ();
2736				IN_HideMouse ();
2737			}
2738			else if ((modestate == MS_WINDOWED) && _windowed_mouse.value)
2739			{
2740				IN_ActivateMouse ();
2741				IN_HideMouse ();
2742			}
2743		}
2744
2745		if (!ActiveApp)
2746		{
2747			if (modestate == MS_FULLDIB)
2748			{
2749				if (vid_initialized)
2750				{
2751					force_minimized = true;
2752					i = vid_fulldib_on_focus_mode;
2753					msg_suppress_1 = true;	// don't want to see normal mode set message
2754					VID_SetMode (windowed_default, vid_curpal);
2755					msg_suppress_1 = false;
2756					vid_fulldib_on_focus_mode = i;
2757					force_minimized = false;
2758
2759				// we never seem to get WM_ACTIVATE inactive from this mode set, so we'll
2760				// do it manually
2761					t = in_mode_set;
2762					in_mode_set = true;
2763					AppActivate (false, true);
2764					in_mode_set = t;
2765				}
2766
2767				IN_DeactivateMouse ();
2768				IN_ShowMouse ();
2769			}
2770			else if ((modestate == MS_WINDOWED) && _windowed_mouse.value)
2771			{
2772				IN_DeactivateMouse ();
2773				IN_ShowMouse ();
2774			}
2775		}
2776	}
2777}
2778
2779
2780/*
2781================
2782VID_HandlePause
2783================
2784*/
2785void VID_HandlePause (qboolean pause)
2786{
2787
2788	if ((modestate == MS_WINDOWED) && _windowed_mouse.value)
2789	{
2790		if (pause)
2791		{
2792			IN_DeactivateMouse ();
2793			IN_ShowMouse ();
2794		}
2795		else
2796		{
2797			IN_ActivateMouse ();
2798			IN_HideMouse ();
2799		}
2800	}
2801}
2802
2803
2804/*
2805===================================================================
2806
2807MAIN WINDOW
2808
2809===================================================================
2810*/
2811
2812LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
2813
2814/* main window procedure */
2815LONG WINAPI MainWndProc (
2816    HWND    hWnd,
2817    UINT    uMsg,
2818    WPARAM  wParam,
2819    LPARAM  lParam)
2820{
2821	LONG			lRet = 0;
2822	int				fwKeys, xPos, yPos, fActive, fMinimized, temp;
2823	HDC				hdc;
2824	PAINTSTRUCT		ps;
2825	static int		recursiveflag;
2826
2827	switch (uMsg)
2828	{
2829		case WM_CREATE:
2830			break;
2831
2832		case WM_SYSCOMMAND:
2833
2834		// Check for maximize being hit
2835			switch (wParam & ~0x0F)
2836			{
2837				case SC_MAXIMIZE:
2838				// if minimized, bring up as a window before going fullscreen,
2839				// so MGL will have the right state to restore
2840					if (Minimized)
2841					{
2842						force_mode_set = true;
2843						VID_SetMode (vid_modenum, vid_curpal);
2844						force_mode_set = false;
2845					}
2846
2847					VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal);
2848					break;
2849
2850                case SC_SCREENSAVE:
2851                case SC_MONITORPOWER:
2852					if (modestate != MS_WINDOWED)
2853					{
2854					// don't call DefWindowProc() because we don't want to start
2855					// the screen saver fullscreen
2856						break;
2857					}
2858
2859				// fall through windowed and allow the screen saver to start
2860
2861				default:
2862					if (!in_mode_set)
2863					{
2864						S_BlockSound ();
2865						S_ClearBuffer ();
2866					}
2867
2868					lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
2869
2870					if (!in_mode_set)
2871					{
2872						S_UnblockSound ();
2873					}
2874			}
2875			break;
2876
2877		case WM_MOVE:
2878			window_x = (int) LOWORD(lParam);
2879			window_y = (int) HIWORD(lParam);
2880			VID_UpdateWindowStatus ();
2881
2882			if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized)
2883				VID_RememberWindowPos ();
2884
2885			break;
2886
2887		case WM_SIZE:
2888			Minimized = false;
2889
2890			if (!(wParam & SIZE_RESTORED))
2891			{
2892				if (wParam & SIZE_MINIMIZED)
2893					Minimized = true;
2894			}
2895			break;
2896
2897		case WM_SYSCHAR:
2898		// keep Alt-Space from happening
2899			break;
2900
2901		case WM_ACTIVATE:
2902			fActive = LOWORD(wParam);
2903			fMinimized = (BOOL) HIWORD(wParam);
2904			AppActivate(!(fActive == WA_INACTIVE), fMinimized);
2905
2906		// fix the leftover Alt from any Alt-Tab or the like that switched us away
2907			ClearAllStates ();
2908
2909			if (!in_mode_set)
2910			{
2911				if (windc)
2912					MGL_activatePalette(windc,true);
2913
2914				VID_SetPalette(vid_curpal);
2915			}
2916
2917			break;
2918
2919		case WM_PAINT:
2920			hdc = BeginPaint(hWnd, &ps);
2921
2922			if (!in_mode_set && host_initialized)
2923				SCR_UpdateWholeScreen ();
2924
2925			EndPaint(hWnd, &ps);
2926			break;
2927
2928		case WM_KEYDOWN:
2929		case WM_SYSKEYDOWN:
2930			if (!in_mode_set)
2931				Key_Event (MapKey(lParam), true);
2932			break;
2933
2934		case WM_KEYUP:
2935		case WM_SYSKEYUP:
2936			if (!in_mode_set)
2937				Key_Event (MapKey(lParam), false);
2938			break;
2939
2940	// this is complicated because Win32 seems to pack multiple mouse events into
2941	// one update sometimes, so we always check all states and look for events
2942		case WM_LBUTTONDOWN:
2943		case WM_LBUTTONUP:
2944		case WM_RBUTTONDOWN:
2945		case WM_RBUTTONUP:
2946		case WM_MBUTTONDOWN:
2947		case WM_MBUTTONUP:
2948		case WM_MOUSEMOVE:
2949			if (!in_mode_set)
2950			{
2951				temp = 0;
2952
2953				if (wParam & MK_LBUTTON)
2954					temp |= 1;
2955
2956				if (wParam & MK_RBUTTON)
2957					temp |= 2;
2958
2959				if (wParam & MK_MBUTTON)
2960					temp |= 4;
2961
2962				IN_MouseEvent (temp);
2963			}
2964			break;
2965
2966		// JACK: This is the mouse wheel with the Intellimouse
2967		// Its delta is either positive or neg, and we generate the proper
2968		// Event.
2969		case WM_MOUSEWHEEL:
2970			if ((short) HIWORD(wParam) > 0) {
2971				Key_Event(K_MWHEELUP, true);
2972				Key_Event(K_MWHEELUP, false);
2973			} else {
2974				Key_Event(K_MWHEELDOWN, true);
2975				Key_Event(K_MWHEELDOWN, false);
2976			}
2977			break;
2978		// KJB: Added these new palette functions
2979		case WM_PALETTECHANGED:
2980			if ((HWND)wParam == hWnd)
2981				break;
2982			/* Fall through to WM_QUERYNEWPALETTE */
2983		case WM_QUERYNEWPALETTE:
2984			hdc = GetDC(NULL);
2985
2986			if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
2987				vid_palettized = true;
2988			else
2989				vid_palettized = false;
2990
2991			ReleaseDC(NULL,hdc);
2992
2993			scr_fullupdate = 0;
2994
2995			if (vid_initialized && !in_mode_set && windc && MGL_activatePalette(windc,false) && !Minimized)
2996			{
2997				VID_SetPalette (vid_curpal);
2998				InvalidateRect (mainwindow, NULL, false);
2999
3000			// specifically required if WM_QUERYNEWPALETTE realizes a new palette
3001				lRet = TRUE;
3002			}
3003			break;
3004
3005		case WM_DISPLAYCHANGE:
3006			if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode)
3007			{
3008				force_mode_set = true;
3009				VID_SetMode (vid_modenum, vid_curpal);
3010				force_mode_set = false;
3011			}
3012			break;
3013
3014   	    case WM_CLOSE:
3015		// this causes Close in the right-click task bar menu not to work, but right
3016		// now bad things happen if Close is handled in that case (garbage and a
3017		// crash on Win95)
3018			if (!in_mode_set)
3019			{
3020				if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit",
3021							MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
3022				{
3023					Sys_Quit ();
3024				}
3025			}
3026			break;
3027
3028		case MM_MCINOTIFY:
3029            lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
3030			break;
3031
3032		default:
3033            /* pass all unhandled messages to DefWindowProc */
3034            lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
3035	        break;
3036    }
3037
3038    /* return 0 if handled message, 1 if not */
3039    return lRet;
3040}
3041
3042
3043extern void M_Menu_Options_f (void);
3044extern void M_Print (int cx, int cy, char *str);
3045extern void M_PrintWhite (int cx, int cy, char *str);
3046extern void M_DrawCharacter (int cx, int line, int num);
3047extern void M_DrawTransPic (int x, int y, qpic_t *pic);
3048extern void M_DrawPic (int x, int y, qpic_t *pic);
3049
3050static int	vid_line, vid_wmodes;
3051
3052typedef struct
3053{
3054	int		modenum;
3055	char	*desc;
3056	int		iscur;
3057	int		ismode13;
3058	int		width;
3059} modedesc_t;
3060
3061#define MAX_COLUMN_SIZE		5
3062#define MODE_AREA_HEIGHT	(MAX_COLUMN_SIZE + 6)
3063#define MAX_MODEDESCS		(MAX_COLUMN_SIZE*3)
3064
3065static modedesc_t	modedescs[MAX_MODEDESCS];
3066
3067/*
3068================
3069VID_MenuDraw
3070================
3071*/
3072void VID_MenuDraw (void)
3073{
3074	qpic_t		*p;
3075	char		*ptr;
3076	int			lnummodes, i, j, k, column, row, dup, dupmode;
3077	char		temp[100];
3078	vmode_t		*pv;
3079	modedesc_t	tmodedesc;
3080
3081	p = Draw_CachePic ("gfx/vidmodes.lmp");
3082	M_DrawPic ( (320-p->width)/2, 4, p);
3083
3084	for (i=0 ; i<3 ; i++)
3085	{
3086		ptr = VID_GetModeDescriptionMemCheck (i);
3087		modedescs[i].modenum = modelist[i].modenum;
3088		modedescs[i].desc = ptr;
3089		modedescs[i].ismode13 = 0;
3090		modedescs[i].iscur = 0;
3091
3092		if (vid_modenum == i)
3093			modedescs[i].iscur = 1;
3094	}
3095
3096	vid_wmodes = 3;
3097	lnummodes = VID_NumModes ();
3098
3099	for (i=3 ; i<lnummodes ; i++)
3100	{
3101		ptr = VID_GetModeDescriptionMemCheck (i);
3102		pv = VID_GetModePtr (i);
3103
3104	// we only have room for 15 fullscreen modes, so don't allow
3105	// 360-wide modes, because if there are 5 320-wide modes and
3106	// 5 360-wide modes, we'll run out of space
3107		if (ptr && ((pv->width != 360) || COM_CheckParm("-allow360")))
3108		{
3109			dup = 0;
3110
3111			for (j=3 ; j<vid_wmodes ; j++)
3112			{
3113				if (!strcmp (modedescs[j].desc, ptr))
3114				{
3115					dup = 1;
3116					dupmode = j;
3117					break;
3118				}
3119			}
3120
3121			if (dup || (vid_wmodes < MAX_MODEDESCS))
3122			{
3123				if (!dup || !modedescs[dupmode].ismode13 || COM_CheckParm("-noforcevga"))
3124				{
3125					if (dup)
3126					{
3127						k = dupmode;
3128					}
3129					else
3130					{
3131						k = vid_wmodes;
3132					}
3133
3134					modedescs[k].modenum = i;
3135					modedescs[k].desc = ptr;
3136					modedescs[k].ismode13 = pv->mode13;
3137					modedescs[k].iscur = 0;
3138					modedescs[k].width = pv->width;
3139
3140					if (i == vid_modenum)
3141						modedescs[k].iscur = 1;
3142
3143					if (!dup)
3144						vid_wmodes++;
3145				}
3146			}
3147		}
3148	}
3149
3150// sort the modes on width (to handle picking up oddball dibonly modes
3151// after all the others)
3152	for (i=3 ; i<(vid_wmodes-1) ; i++)
3153	{
3154		for (j=(i+1) ; j<vid_wmodes ; j++)
3155		{
3156			if (modedescs[i].width > modedescs[j].width)
3157			{
3158				tmodedesc = modedescs[i];
3159				modedescs[i] = modedescs[j];
3160				modedescs[j] = tmodedesc;
3161			}
3162		}
3163	}
3164
3165
3166	M_Print (13*8, 36, "Windowed Modes");
3167
3168	column = 16;
3169	row = 36+2*8;
3170
3171	for (i=0 ; i<3; i++)
3172	{
3173		if (modedescs[i].iscur)
3174			M_PrintWhite (column, row, modedescs[i].desc);
3175		else
3176			M_Print (column, row, modedescs[i].desc);
3177
3178		column += 13*8;
3179	}
3180
3181	if (vid_wmodes > 3)
3182	{
3183		M_Print (12*8, 36+4*8, "Fullscreen Modes");
3184
3185		column = 16;
3186		row = 36+6*8;
3187
3188		for (i=3 ; i<vid_wmodes ; i++)
3189		{
3190			if (modedescs[i].iscur)
3191				M_PrintWhite (column, row, modedescs[i].desc);
3192			else
3193				M_Print (column, row, modedescs[i].desc);
3194
3195			column += 13*8;
3196
3197			if (((i - 3) % VID_ROW_SIZE) == (VID_ROW_SIZE - 1))
3198			{
3199				column = 16;
3200				row += 8;
3201			}
3202		}
3203	}
3204
3205// line cursor
3206	if (vid_testingmode)
3207	{
3208		sprintf (temp, "TESTING %s",
3209				modedescs[vid_line].desc);
3210		M_Print (13*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4, temp);
3211		M_Print (9*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6,
3212				"Please wait 5 seconds...");
3213	}
3214	else
3215	{
3216		M_Print (9*8, 36 + MODE_AREA_HEIGHT * 8 + 8,
3217				"Press Enter to set mode");
3218		M_Print (6*8, 36 + MODE_AREA_HEIGHT * 8 + 8*3,
3219				"T to test mode for 5 seconds");
3220		ptr = VID_GetModeDescription2 (vid_modenum);
3221
3222		if (ptr)
3223		{
3224			sprintf (temp, "D to set default: %s", ptr);
3225			M_Print (2*8, 36 + MODE_AREA_HEIGHT * 8 + 8*5, temp);
3226		}
3227
3228		ptr = VID_GetModeDescription2 ((int)_vid_default_mode_win.value);
3229
3230		if (ptr)
3231		{
3232			sprintf (temp, "Current default: %s", ptr);
3233			M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, temp);
3234		}
3235
3236		M_Print (15*8, 36 + MODE_AREA_HEIGHT * 8 + 8*8,
3237				"Esc to exit");
3238
3239		row = 36 + 2*8 + (vid_line / VID_ROW_SIZE) * 8;
3240		column = 8 + (vid_line % VID_ROW_SIZE) * 13*8;
3241
3242		if (vid_line >= 3)
3243			row += 3*8;
3244
3245		M_DrawCharacter (column, row, 12+((int)(realtime*4)&1));
3246	}
3247}
3248
3249
3250/*
3251================
3252VID_MenuKey
3253================
3254*/
3255void VID_MenuKey (int key)
3256{
3257	if (vid_testingmode)
3258		return;
3259
3260	switch (key)
3261	{
3262	case K_ESCAPE:
3263		S_LocalSound ("misc/menu1.wav");
3264		M_Menu_Options_f ();
3265		break;
3266
3267	case K_LEFTARROW:
3268		S_LocalSound ("misc/menu1.wav");
3269		vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) +
3270				   ((vid_line + 2) % VID_ROW_SIZE);
3271
3272		if (vid_line >= vid_wmodes)
3273			vid_line = vid_wmodes - 1;
3274		break;
3275
3276	case K_RIGHTARROW:
3277		S_LocalSound ("misc/menu1.wav");
3278		vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) +
3279				   ((vid_line + 4) % VID_ROW_SIZE);
3280
3281		if (vid_line >= vid_wmodes)
3282			vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE;
3283		break;
3284
3285	case K_UPARROW:
3286		S_LocalSound ("misc/menu1.wav");
3287		vid_line -= VID_ROW_SIZE;
3288
3289		if (vid_line < 0)
3290		{
3291			vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) /
3292					VID_ROW_SIZE) * VID_ROW_SIZE;
3293
3294			while (vid_line >= vid_wmodes)
3295				vid_line -= VID_ROW_SIZE;
3296		}
3297		break;
3298
3299	case K_DOWNARROW:
3300		S_LocalSound ("misc/menu1.wav");
3301		vid_line += VID_ROW_SIZE;
3302
3303		if (vid_line >= vid_wmodes)
3304		{
3305			vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) /
3306					VID_ROW_SIZE) * VID_ROW_SIZE;
3307
3308			while (vid_line < 0)
3309				vid_line += VID_ROW_SIZE;
3310		}
3311		break;
3312
3313	case K_ENTER:
3314		S_LocalSound ("misc/menu1.wav");
3315		VID_SetMode (modedescs[vid_line].modenum, vid_curpal);
3316		break;
3317
3318	case 'T':
3319	case 't':
3320		S_LocalSound ("misc/menu1.wav");
3321	// have to set this before setting the mode because WM_PAINT
3322	// happens during the mode set and does a VID_Update, which
3323	// checks vid_testingmode
3324		vid_testingmode = 1;
3325		vid_testendtime = realtime + 5.0;
3326
3327		if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal))
3328		{
3329			vid_testingmode = 0;
3330		}
3331		break;
3332
3333	case 'D':
3334	case 'd':
3335		S_LocalSound ("misc/menu1.wav");
3336		firstupdate = 0;
3337		Cvar_SetValue ("_vid_default_mode_win", vid_modenum);
3338		break;
3339
3340	default:
3341		break;
3342	}
3343}
3344