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