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//
21// vid_dos.c: DOS-specific video routines
22//
23
24#include <unistd.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <dos.h>
29#include <dpmi.h>
30#include <go32.h>
31
32#include "quakedef.h"
33#include "d_local.h"
34#include "dosisms.h"
35#include "vid_dos.h"
36
37int			vid_modenum;
38vmode_t		*pcurrentmode = NULL;
39int			vid_testingmode, vid_realmode;
40double		vid_testendtime;
41
42cvar_t		vid_mode = {"vid_mode","0", false};
43cvar_t		vid_wait = {"vid_wait","0"};
44cvar_t		vid_nopageflip = {"vid_nopageflip","0", true};
45cvar_t		_vid_wait_override = {"_vid_wait_override", "0", true};
46cvar_t		_vid_default_mode = {"_vid_default_mode","0", true};
47cvar_t		_vid_default_mode_win = {"_vid_default_mode_win","1", true};
48cvar_t		vid_config_x = {"vid_config_x","800", true};
49cvar_t		vid_config_y = {"vid_config_y","600", true};
50cvar_t		vid_stretch_by_2 = {"vid_stretch_by_2","1", true};
51cvar_t		_windowed_mouse = {"_windowed_mouse","0", true};
52cvar_t		vid_fullscreen_mode = {"vid_fullscreen_mode","3", true};
53cvar_t		vid_windowed_mode = {"vid_windowed_mode","0", true};
54cvar_t		block_switch = {"block_switch","0", true};
55cvar_t		vid_window_x = {"vid_window_x", "0", true};
56cvar_t		vid_window_y = {"vid_window_y", "0", true};
57
58int	d_con_indirect = 0;
59
60int		numvidmodes;
61vmode_t	*pvidmodes;
62
63static int	firstupdate = 1;
64
65extern regs_t regs;
66
67void VID_TestMode_f (void);
68void VID_NumModes_f (void);
69void VID_DescribeCurrentMode_f (void);
70void VID_DescribeMode_f (void);
71void VID_DescribeModes_f (void);
72
73byte	vid_current_palette[768];	// save for mode changes
74
75
76static qboolean	nomodecheck = false;
77
78unsigned short	d_8to16table[256];	// not used in 8 bpp mode
79unsigned		d_8to24table[256];	// not used in 8 bpp mode
80
81void VID_MenuDraw (void);
82void VID_MenuKey (int key);
83
84
85/*
86================
87VID_Init
88================
89*/
90void    VID_Init (unsigned char *palette)
91{
92	Cvar_RegisterVariable (&vid_mode);
93	Cvar_RegisterVariable (&vid_wait);
94	Cvar_RegisterVariable (&vid_nopageflip);
95	Cvar_RegisterVariable (&_vid_wait_override);
96	Cvar_RegisterVariable (&_vid_default_mode);
97	Cvar_RegisterVariable (&_vid_default_mode_win);
98	Cvar_RegisterVariable (&vid_config_x);
99	Cvar_RegisterVariable (&vid_config_y);
100	Cvar_RegisterVariable (&vid_stretch_by_2);
101	Cvar_RegisterVariable (&_windowed_mouse);
102	Cvar_RegisterVariable (&vid_fullscreen_mode);
103	Cvar_RegisterVariable (&vid_windowed_mode);
104	Cvar_RegisterVariable (&block_switch);
105
106	Cmd_AddCommand ("vid_testmode", VID_TestMode_f);
107	Cmd_AddCommand ("vid_nummodes", VID_NumModes_f);
108	Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f);
109	Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f);
110	Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f);
111
112// set up the mode list; note that later inits link in their modes ahead of
113// earlier ones, so the standard VGA modes are always first in the list. This
114// is important because mode 0 must always be VGA mode 0x13
115	if (!COM_CheckParm ("-stdvid"))
116		VID_InitExtra ();
117	VGA_Init ();
118
119	vid_testingmode = 0;
120
121	vid_modenum = vid_mode.value;
122
123	VID_SetMode (vid_modenum, palette);
124
125	vid_realmode = vid_modenum;
126
127	vid_menudrawfn = VID_MenuDraw;
128	vid_menukeyfn = VID_MenuKey;
129}
130
131
132/*
133=================
134VID_GetModePtr
135=================
136*/
137vmode_t *VID_GetModePtr (int modenum)
138{
139	vmode_t	*pv;
140
141	pv = pvidmodes;
142	if (!pv)
143		Sys_Error ("VID_GetModePtr: empty vid mode list");
144
145	while (modenum--)
146	{
147		pv = pv->pnext;
148		if (!pv)
149			Sys_Error ("VID_GetModePtr: corrupt vid mode list");
150	}
151
152	return pv;
153}
154
155/*
156================
157VID_NumModes
158================
159*/
160int VID_NumModes ()
161{
162	return (numvidmodes);
163}
164
165
166/*
167================
168VID_ModeInfo
169================
170*/
171char *VID_ModeInfo (int modenum, char **ppheader)
172{
173	static char	*badmodestr = "Bad mode number";
174	vmode_t		*pv;
175
176	pv = VID_GetModePtr (modenum);
177
178	if (!pv)
179	{
180		if (ppheader)
181			*ppheader = NULL;
182		return badmodestr;
183	}
184	else
185	{
186		if (ppheader)
187			*ppheader = pv->header;
188		return pv->name;
189	}
190}
191
192
193/*
194================
195VID_SetMode
196================
197*/
198int VID_SetMode (int modenum, unsigned char *palette)
199{
200	int		stat;
201	vmode_t	*pnewmode, *poldmode;
202
203	if ((modenum >= numvidmodes) || (modenum < 0))
204	{
205		Cvar_SetValue ("vid_mode", (float)vid_modenum);
206
207		nomodecheck = true;
208		Con_Printf ("No such video mode: %d\n", modenum);
209		nomodecheck = false;
210
211		if (pcurrentmode == NULL)
212		{
213			modenum = 0;	// mode hasn't been set yet, so initialize to base
214							//  mode since they gave us an invalid initial mode
215		}
216		else
217		{
218			return 0;
219		}
220	}
221
222	pnewmode = VID_GetModePtr (modenum);
223
224	if (pnewmode == pcurrentmode)
225		return 1;	// already in the desired mode
226
227// initialize the new mode
228	poldmode = pcurrentmode;
229	pcurrentmode = pnewmode;
230
231	vid.width = pcurrentmode->width;
232	vid.height = pcurrentmode->height;
233	vid.aspect = pcurrentmode->aspect;
234	vid.rowbytes = pcurrentmode->rowbytes;
235
236	stat = (*pcurrentmode->setmode) (&vid, pcurrentmode);
237
238	if (stat < 1)
239	{
240		if (stat == 0)
241		{
242		// real, hard failure that requires resetting the mode
243			if (!VID_SetMode (vid_modenum, palette))	// restore prior mode
244				Sys_Error ("VID_SetMode: Unable to set any mode, probably "
245						   "because there's not enough memory available");
246			Con_Printf ("Failed to set mode %d\n", modenum);
247			return 0;
248		}
249		else if (stat == -1)
250		{
251		// not enough memory; just put things back the way they were
252			pcurrentmode = poldmode;
253			vid.width = pcurrentmode->width;
254			vid.height = pcurrentmode->height;
255			vid.aspect = pcurrentmode->aspect;
256			vid.rowbytes = pcurrentmode->rowbytes;
257			return 0;
258		}
259		else
260		{
261			Sys_Error ("VID_SetMode: invalid setmode return code %d");
262		}
263	}
264
265	(*pcurrentmode->setpalette) (&vid, pcurrentmode, palette);
266
267	vid_modenum = modenum;
268	Cvar_SetValue ("vid_mode", (float)vid_modenum);
269
270	nomodecheck = true;
271	Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL));
272	nomodecheck = false;
273
274	vid.recalc_refdef = 1;
275
276	return 1;
277}
278
279
280/*
281================
282VID_SetPalette
283================
284*/
285void    VID_SetPalette (unsigned char *palette)
286{
287	if (palette != vid_current_palette)
288		Q_memcpy(vid_current_palette, palette, 768);
289	(*pcurrentmode->setpalette)(&vid, pcurrentmode, vid_current_palette);
290}
291
292
293/*
294================
295VID_ShiftPalette
296================
297*/
298void    VID_ShiftPalette (unsigned char *palette)
299{
300
301	VID_SetPalette (palette);
302}
303
304
305/*
306================
307VID_Shutdown
308================
309*/
310void VID_Shutdown (void)
311{
312
313	regs.h.ah = 0;
314	regs.h.al = 0x3;
315	dos_int86(0x10);
316
317	vid_testingmode = 0;
318}
319
320
321/*
322================
323VID_Update
324================
325*/
326void    VID_Update (vrect_t *rects)
327{
328	if (firstupdate && _vid_default_mode.value)
329	{
330		if(_vid_default_mode.value >= numvidmodes)
331			Cvar_SetValue ("_vid_default_mode", 0);
332
333		firstupdate = 0;
334		Cvar_SetValue ("vid_mode", _vid_default_mode.value);
335	}
336
337	(*pcurrentmode->swapbuffers)(&vid, pcurrentmode, rects);
338
339	if (!nomodecheck)
340	{
341		if (vid_testingmode)
342		{
343			if (realtime >= vid_testendtime)
344			{
345				VID_SetMode (vid_realmode, vid_current_palette);
346				vid_testingmode = 0;
347			}
348		}
349		else
350		{
351			if (vid_mode.value != vid_realmode)
352			{
353				VID_SetMode ((int)vid_mode.value, vid_current_palette);
354				Cvar_SetValue ("vid_mode", (float)vid_modenum);
355									// so if mode set fails, we don't keep on
356									//  trying to set that mode
357				vid_realmode = vid_modenum;
358			}
359		}
360	}
361}
362
363
364/*
365=================
366VID_NumModes_f
367=================
368*/
369void VID_NumModes_f (void)
370{
371	int		nummodes;
372
373	nummodes = VID_NumModes ();
374	if (nummodes == 1)
375		Con_Printf ("%d video mode is available\n", VID_NumModes ());
376	else
377		Con_Printf ("%d video modes are available\n", VID_NumModes ());
378}
379
380
381/*
382=================
383VID_DescribeCurrentMode_f
384=================
385*/
386void VID_DescribeCurrentMode_f (void)
387{
388	Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL));
389}
390
391
392/*
393=================
394VID_DescribeMode_f
395=================
396*/
397void VID_DescribeMode_f (void)
398{
399	int		modenum;
400
401	modenum = Q_atoi (Cmd_Argv(1));
402
403	Con_Printf ("%s\n", VID_ModeInfo (modenum, NULL));
404}
405
406
407/*
408=================
409VID_DescribeModes_f
410=================
411*/
412void VID_DescribeModes_f (void)
413{
414	int			i, nummodes;
415	char		*pinfo, *pheader;
416	vmode_t		*pv;
417	qboolean	na;
418
419	na = false;
420
421	nummodes = VID_NumModes ();
422	for (i=0 ; i<nummodes ; i++)
423	{
424		pv = VID_GetModePtr (i);
425		pinfo = VID_ModeInfo (i, &pheader);
426		if (pheader)
427			Con_Printf ("\n%s\n", pheader);
428
429		if (VGA_CheckAdequateMem (pv->width, pv->height, pv->rowbytes,
430			(pv->numpages == 1) || vid_nopageflip.value))
431		{
432			Con_Printf ("%2d: %s\n", i, pinfo);
433		}
434		else
435		{
436			Con_Printf ("**: %s\n", pinfo);
437			na = true;
438		}
439	}
440
441	if (na)
442	{
443		Con_Printf ("\n[**: not enough system RAM for mode]\n");
444	}
445}
446
447
448/*
449=================
450VID_GetModeDescription
451=================
452*/
453char *VID_GetModeDescription (int mode)
454{
455	char		*pinfo, *pheader;
456	vmode_t		*pv;
457
458	pv = VID_GetModePtr (mode);
459	pinfo = VID_ModeInfo (mode, &pheader);
460
461	if (VGA_CheckAdequateMem (pv->width, pv->height, pv->rowbytes,
462		(pv->numpages == 1) || vid_nopageflip.value))
463	{
464		return pinfo;
465	}
466	else
467	{
468		return NULL;
469	}
470}
471
472
473/*
474=================
475VID_TestMode_f
476=================
477*/
478void VID_TestMode_f (void)
479{
480	int		modenum;
481	double	testduration;
482
483	if (!vid_testingmode)
484	{
485		modenum = Q_atoi (Cmd_Argv(1));
486
487		if (VID_SetMode (modenum, vid_current_palette))
488		{
489			vid_testingmode = 1;
490			testduration = Q_atof (Cmd_Argv(2));
491			if (testduration == 0)
492				testduration = 5.0;
493			vid_testendtime = realtime + testduration;
494		}
495	}
496}
497
498
499/*
500================
501D_BeginDirectRect
502================
503*/
504void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
505{
506
507	if (!vid.direct || !pcurrentmode)
508		return;
509
510	if ((width > 24) || (height > 24) || (width < 1) || (height < 1))
511		return;
512
513	if (width & 0x03)
514		return;
515
516	(*pcurrentmode->begindirectrect) (&vid, pcurrentmode, x, y, pbitmap, width,
517									  height);
518}
519
520
521/*
522================
523D_EndDirectRect
524================
525*/
526void D_EndDirectRect (int x, int y, int width, int height)
527{
528
529	if (!vid.direct || !pcurrentmode)
530		return;
531
532	if ((width > 24) || (height > 24) || (width < 1) || (height < 1))
533		return;
534
535	if ((width & 0x03) || (height & 0x03))
536		return;
537
538	(*pcurrentmode->enddirectrect) (&vid, pcurrentmode, x, y, width, height);
539}
540
541
542//===========================================================================
543
544extern void M_Menu_Options_f (void);
545extern void M_Print (int cx, int cy, char *str);
546extern void M_PrintWhite (int cx, int cy, char *str);
547extern void M_DrawCharacter (int cx, int line, int num);
548extern void M_DrawTransPic (int x, int y, qpic_t *pic);
549extern void M_DrawPic (int x, int y, qpic_t *pic);
550
551static int	vid_line, vid_wmodes, vid_column_size;
552
553typedef struct
554{
555	int		modenum;
556	char	*desc;
557	int		iscur;
558} modedesc_t;
559
560#define MAX_COLUMN_SIZE	11
561
562#define MAX_MODEDESCS	(MAX_COLUMN_SIZE*3)
563
564static modedesc_t	modedescs[MAX_MODEDESCS];
565
566/*
567================
568VID_MenuDraw
569================
570*/
571void VID_MenuDraw (void)
572{
573	qpic_t		*p;
574	char		*ptr;
575	int			nummodes, i, j, column, row, dup;
576	char		temp[100];
577
578	vid_wmodes = 0;
579	nummodes = VID_NumModes ();
580
581	p = Draw_CachePic ("gfx/vidmodes.lmp");
582	M_DrawPic ( (320-p->width)/2, 4, p);
583
584	for (i=0 ; i<nummodes ; i++)
585	{
586		if (vid_wmodes < MAX_MODEDESCS)
587		{
588			if (i != 1)
589			{
590				ptr = VID_GetModeDescription (i);
591
592				if (ptr)
593				{
594					dup = 0;
595
596					for (j=0 ; j<vid_wmodes ; j++)
597					{
598						if (!strcmp (modedescs[j].desc, ptr))
599						{
600							if (modedescs[j].modenum != 0)
601							{
602								modedescs[j].modenum = i;
603								dup = 1;
604
605								if (i == vid_modenum)
606									modedescs[j].iscur = 1;
607							}
608							else
609							{
610								dup = 1;
611							}
612
613							break;
614						}
615					}
616
617					if (!dup)
618					{
619						modedescs[vid_wmodes].modenum = i;
620						modedescs[vid_wmodes].desc = ptr;
621						modedescs[vid_wmodes].iscur = 0;
622
623						if (i == vid_modenum)
624							modedescs[vid_wmodes].iscur = 1;
625
626						vid_wmodes++;
627					}
628				}
629			}
630		}
631	}
632
633	vid_column_size = (vid_wmodes + 2) / 3;
634
635	column = 16;
636	row = 36;
637
638	for (i=0 ; i<vid_wmodes ; i++)
639	{
640		if (modedescs[i].iscur)
641			M_PrintWhite (column, row, modedescs[i].desc);
642		else
643			M_Print (column, row, modedescs[i].desc);
644
645		row += 8;
646
647		if ((i % vid_column_size) == (vid_column_size - 1))
648		{
649			column += 13*8;
650			row = 36;
651		}
652	}
653
654// line cursor
655	if (vid_testingmode)
656	{
657		sprintf (temp, "TESTING %s",
658				modedescs[vid_line].desc);
659		M_Print (13*8, 36 + MAX_COLUMN_SIZE * 8 + 8*4, temp);
660		M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6,
661				"Please wait 5 seconds...");
662	}
663	else
664	{
665		M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8,
666				"Press Enter to set mode");
667		M_Print (6*8, 36 + MAX_COLUMN_SIZE * 8 + 8*3,
668				"T to test mode for 5 seconds");
669		ptr = VID_GetModeDescription (vid_modenum);
670		sprintf (temp, "D to make %s the default", ptr);
671		M_Print (6*8, 36 + MAX_COLUMN_SIZE * 8 + 8*5, temp);
672		ptr = VID_GetModeDescription ((int)_vid_default_mode.value);
673
674		if (ptr)
675		{
676			sprintf (temp, "Current default is %s", ptr);
677			M_Print (7*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, temp);
678		}
679
680		M_Print (15*8, 36 + MAX_COLUMN_SIZE * 8 + 8*8,
681				"Esc to exit");
682
683		row = 36 + (vid_line % vid_column_size) * 8;
684		column = 8 + (vid_line / vid_column_size) * 13*8;
685
686		M_DrawCharacter (column, row, 12+((int)(realtime*4)&1));
687	}
688}
689
690
691/*
692================
693VID_MenuKey
694================
695*/
696void VID_MenuKey (int key)
697{
698	if (vid_testingmode)
699		return;
700
701	switch (key)
702	{
703	case K_ESCAPE:
704		S_LocalSound ("misc/menu1.wav");
705		M_Menu_Options_f ();
706		break;
707
708	case K_UPARROW:
709		S_LocalSound ("misc/menu1.wav");
710		vid_line--;
711
712		if (vid_line < 0)
713			vid_line = vid_wmodes - 1;
714		break;
715
716	case K_DOWNARROW:
717		S_LocalSound ("misc/menu1.wav");
718		vid_line++;
719
720		if (vid_line >= vid_wmodes)
721			vid_line = 0;
722		break;
723
724	case K_LEFTARROW:
725		S_LocalSound ("misc/menu1.wav");
726		vid_line -= vid_column_size;
727
728		if (vid_line < 0)
729		{
730			vid_line += ((vid_wmodes + (vid_column_size - 1)) /
731					vid_column_size) * vid_column_size;
732
733			while (vid_line >= vid_wmodes)
734				vid_line -= vid_column_size;
735		}
736		break;
737
738	case K_RIGHTARROW:
739		S_LocalSound ("misc/menu1.wav");
740		vid_line += vid_column_size;
741
742		if (vid_line >= vid_wmodes)
743		{
744			vid_line -= ((vid_wmodes + (vid_column_size - 1)) /
745					vid_column_size) * vid_column_size;
746
747			while (vid_line < 0)
748				vid_line += vid_column_size;
749		}
750		break;
751
752	case K_ENTER:
753		S_LocalSound ("misc/menu1.wav");
754		VID_SetMode (modedescs[vid_line].modenum, vid_current_palette);
755		break;
756
757	case 'T':
758	case 't':
759		S_LocalSound ("misc/menu1.wav");
760		if (VID_SetMode (modedescs[vid_line].modenum, vid_current_palette))
761		{
762			vid_testingmode = 1;
763			vid_testendtime = realtime + 5.0;
764		}
765		break;
766
767	case 'D':
768	case 'd':
769		S_LocalSound ("misc/menu1.wav");
770		firstupdate = 0;
771		Cvar_SetValue ("_vid_default_mode", vid_modenum);
772		break;
773
774	default:
775		break;
776	}
777}
778
779