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