/* vid_win.c @description@ Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "winquake.h" #include "d_local.h" #include "resource.h" #define MAX_MODE_LIST 30 #define VID_ROW_SIZE 3 qboolean dibonly; extern int Minimized; HWND mainwindow; HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); int DIBWidth, DIBHeight; qboolean DDActive; RECT WindowRect; DWORD WindowStyle, ExWindowStyle; int window_center_x, window_center_y, window_x, window_y, window_width, window_height; RECT window_rect; static DEVMODE gdevmode; static qboolean startwindowed = 0, windowed_mode_set; static int firstupdate = 1; static qboolean vid_initialized = false, vid_palettized; static int lockcount; static int vid_fulldib_on_focus_mode; static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; static int vid_stretched, windowed_mouse; static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic; static HICON hIcon; viddef_t vid; // global video state #define MODE_WINDOWED 0 #define MODE_SETTABLE_WINDOW 2 #define NO_MODE (MODE_WINDOWED - 1) #define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) // Note that 0 is MODE_WINDOWED cvar_t *vid_mode; // Note that 0 is MODE_WINDOWED cvar_t *_vid_default_mode; // Note that 3 is MODE_FULLSCREEN_DEFAULT cvar_t *_vid_default_mode_win; cvar_t *vid_wait; cvar_t *vid_nopageflip; cvar_t *_vid_wait_override; cvar_t *vid_config_x; cvar_t *vid_config_y; cvar_t *vid_stretch_by_2; cvar_t *_windowed_mouse; cvar_t *vid_fullscreen_mode; cvar_t *vid_windowed_mode; cvar_t *block_switch; cvar_t *vid_window_x; cvar_t *vid_window_y; typedef struct { int width; int height; } lmode_t; lmode_t lowresmodes[] = { {320, 200}, {320, 240}, {400, 300}, {512, 384}, }; int vid_modenum = NO_MODE; int vid_testingmode, vid_realmode; double vid_testendtime; int vid_default = MODE_WINDOWED; static int windowed_default; modestate_t modestate = MS_UNINIT; static byte *vid_surfcache; static int vid_surfcachesize; static int VID_highhunkmark; unsigned char vid_curpal[256 * 3]; unsigned short d_8to16table[256]; unsigned d_8to24table[256]; int driver = grDETECT, mode; bool useWinDirect = true, useDirectDraw = true; MGLDC *mgldc = NULL, *memdc = NULL, *dibdc = NULL, *windc = NULL; typedef struct { modestate_t type; int width; int height; int modenum; int mode13; int stretched; int dib; int fullscreen; int bpp; int halfscreen; char modedesc[13]; } vmode_t; static vmode_t modelist[MAX_MODE_LIST]; static int nummodes; static vmode_t *pcurrentmode; int aPage; // Current active display page int vPage; // Current visible display page int waitVRT = true; // True to wait for retrace on flip static vmode_t badmode; static byte backingbuf[48 * 24]; void VID_MenuDraw (void); void VID_MenuKey (int key); LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void AppActivate (BOOL fActive, BOOL minimize); void VID_InitCvars (void) { } /* ================ VID_RememberWindowPos ================ */ void VID_RememberWindowPos (void) { RECT rect; if (GetWindowRect (mainwindow, &rect)) { if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && (rect.top < GetSystemMetrics (SM_CYSCREEN)) && (rect.right > 0) && (rect.bottom > 0)) { Cvar_SetValue (vid_window_x, (float) rect.left); Cvar_SetValue (vid_window_y, (float) rect.top); } } } /* ================ VID_CheckWindowXY ================ */ void VID_CheckWindowXY (void) { if ((vid_window_x->int_val > (GetSystemMetrics (SM_CXSCREEN) - 160)) || (vid_window_y->int_val > (GetSystemMetrics (SM_CYSCREEN) - 120)) || (vid_window_x->int_val < 0) || (vid_window_y->int_val < 0)) { Cvar_SetValue (vid_window_x, 0.0); Cvar_SetValue (vid_window_y, 0.0); } } /* ================ VID_UpdateWindowStatus ================ */ void VID_UpdateWindowStatus (void) { window_rect.left = window_x; window_rect.top = window_y; window_rect.right = window_x + window_width; window_rect.bottom = window_y + window_height; window_center_x = (window_rect.left + window_rect.right) / 2; window_center_y = (window_rect.top + window_rect.bottom) / 2; IN_UpdateClipCursor (); } /* ================ ClearAllStates ================ */ void ClearAllStates (void) { int i; // send an up event for each key, to make sure the server clears them all for (i = 0; i < 256; i++) { Key_Event (i, false); } Key_ClearStates (); IN_ClearStates (); } /* ================ VID_CheckAdequateMem ================ */ qboolean VID_CheckAdequateMem (int width, int height) { int tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tbuffersize += D_SurfaceCacheForRes (width, height); // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + 0x10000 * 3) < minimum_memory) { return false; // not enough memory for mode } return true; } /* ================ VID_AllocBuffers ================ */ qboolean VID_AllocBuffers (int width, int height) { int tsize, tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tsize = D_SurfaceCacheForRes (width, height); tbuffersize += tsize; // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + 0x10000 * 3) < minimum_memory) { Con_SafePrintf ("Not enough memory for video mode\n"); return false; // not enough memory for mode } vid_surfcachesize = tsize; if (d_pzbuffer) { D_FlushCaches (); Hunk_FreeToHighMark (VID_highhunkmark); d_pzbuffer = NULL; } VID_highhunkmark = Hunk_HighMark (); d_pzbuffer = Hunk_HighAllocName (tbuffersize, "video"); vid_surfcache = (byte *) d_pzbuffer + width * height * sizeof (*d_pzbuffer); return true; } void initFatalError (void) { MGL_exit (); MGL_fatalError (MGL_errorMsg (MGL_result ())); exit (EXIT_FAILURE); } int VID_Suspend (MGLDC * dc, m_int flags) { if (flags & MGL_DEACTIVATE) { // FIXME: this doesn't currently work on NT if (block_switch->int_val && !WinNT) { return MGL_NO_DEACTIVATE; } S_BlockSound (); S_ClearBuffer (); IN_RestoreOriginalMouseState (); CDAudio_Pause (); // keep WM_PAINT from trying to redraw in_mode_set = true; block_drawing = true; // so we don't try to draw while // switched away return MGL_NO_SUSPEND_APP; } else if (flags & MGL_REACTIVATE) { IN_SetQuakeMouseState (); // fix the leftover Alt from any Alt-Tab or the like that switched us // // // away ClearAllStates (); CDAudio_Resume (); S_UnblockSound (); in_mode_set = false; vid.recalc_refdef = 1; block_drawing = false; return MGL_NO_SUSPEND_APP; } } void registerAllDispDrivers (void) { /* Event though these driver require WinDirect, we register * them so that they will still be available even if DirectDraw * is present and the user has disable the high performance * WinDirect modes. */ MGL_registerDriver (MGL_VGA8NAME, VGA8_driver); // MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); /* Register display drivers */ if (useWinDirect) { //we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); MGL_registerDriver (MGL_LINEAR8NAME, LINEAR8_driver); if (!COM_CheckParm ("-novbeaf")) MGL_registerDriver (MGL_ACCEL8NAME, ACCEL8_driver); } if (useDirectDraw) { MGL_registerDriver (MGL_DDRAW8NAME, DDRAW8_driver); } } void registerAllMemDrivers (void) { /* Register memory context drivers */ MGL_registerDriver (MGL_PACKED8NAME, PACKED8_driver); } void VID_InitMGLFull (HINSTANCE hInstance) { int i, xRes, yRes, bits, vMode, lowres, curmode, temp; int lowstretchedres, stretchedmode, lowstretched; uchar *m; // FIXME: NT is checked for because MGL currently has a bug that causes it // to try to use WinDirect modes even on NT if (COM_CheckParm ("-nowindirect") || COM_CheckParm ("-nowd") || COM_CheckParm ("-novesa") || WinNT) { useWinDirect = false; } if (COM_CheckParm ("-nodirectdraw") || COM_CheckParm ("-noddraw") || COM_CheckParm ("-nodd")) useDirectDraw = false; // Initialise the MGL MGL_unregisterAllDrivers (); registerAllDispDrivers (); registerAllMemDrivers (); MGL_detectGraph (&driver, &mode); m = MGL_availableModes (); if (m[0] != 0xFF) { lowres = lowstretchedres = 99999; lowstretched = 0; curmode = 0; // find the lowest-res mode, or a mode we can stretch up to and get // lowest-res that way for (i = 0; m[i] != 0xFF; i++) { MGL_modeResolution (m[i], &xRes, &yRes, &bits); if ((bits == 8) && (xRes <= MAXWIDTH) && (yRes <= MAXHEIGHT) && (curmode < MAX_MODE_LIST)) { if (m[i] == grVGA_320x200x256) is_mode0x13 = true; if (!COM_CheckParm ("-noforcevga")) { if (m[i] == grVGA_320x200x256) { mode = i; break; } } if (xRes < lowres) { lowres = xRes; mode = i; } if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) { lowstretchedres = xRes >> 1; stretchedmode = i; } } curmode++; } // if there's a mode we can stretch by 2 up to, thereby effectively // getting // a lower-res mode than the lowest-res real but still at least // 320x200, that // will be our default mode if (lowstretchedres < lowres) { mode = stretchedmode; lowres = lowstretchedres; lowstretched = 1; } // build the mode list, leaving room for the low-res stretched mode, // if any nummodes++; // leave room for default mode for (i = 0; m[i] != 0xFF; i++) { MGL_modeResolution (m[i], &xRes, &yRes, &bits); if ((bits == 8) && (xRes <= MAXWIDTH) && (yRes <= MAXHEIGHT) && (nummodes < MAX_MODE_LIST)) { if (i == mode) { if (lowstretched) { stretchedmode = nummodes; curmode = nummodes++; } else { curmode = MODE_FULLSCREEN_DEFAULT; } } else { curmode = nummodes++; } modelist[curmode].type = MS_FULLSCREEN; modelist[curmode].width = xRes; modelist[curmode].height = yRes; snprintf (modelist[curmode].modedesc, sizeof (modelist[curmode].modedesc), "%dx%d", xRes, yRes); if (m[i] == grVGA_320x200x256) modelist[curmode].mode13 = 1; else modelist[curmode].mode13 = 0; modelist[curmode].modenum = m[i]; modelist[curmode].stretched = 0; modelist[curmode].dib = 0; modelist[curmode].fullscreen = 1; modelist[curmode].halfscreen = 0; modelist[curmode].bpp = 8; } } if (lowstretched) { modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; snprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, sizeof (modelist[MODE_FULLSCREEN_DEFAULT].modedesc), "%dx%d", modelist[MODE_FULLSCREEN_DEFAULT].width, modelist[MODE_FULLSCREEN_DEFAULT].height); } vid_default = MODE_FULLSCREEN_DEFAULT; temp = m[0]; if (!MGL_init (&driver, &temp, "")) { initFatalError (); } } MGL_setSuspendAppCallback (VID_Suspend); } MGLDC * createDisplayDC (int forcemem) /**************************************************************************** * * Function: createDisplayDC * Returns: Pointer to the MGL device context to use for the application * * Description: Initialises the MGL and creates an appropriate display * device context to be used by the GUI. This creates and * apropriate device context depending on the system being * compile for, and should be the only place where system * specific code is required. * ****************************************************************************/ { MGLDC *dc; pixel_format_t pf; int npages; // Start the specified video mode if (!MGL_changeDisplayMode (mode)) initFatalError (); npages = MGL_availablePages (mode); if (npages > 3) npages = 3; if (!COM_CheckParm ("-notriplebuf")) { if (npages > 2) { npages = 2; } } if ((dc = MGL_createDisplayDC (npages)) == NULL) return NULL; if (!forcemem && (MGL_surfaceAccessType (dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0)) { MGL_makeCurrentDC (dc); memdc = NULL; } else { // Set up for blitting from a memory buffer memdc = MGL_createMemoryDC (MGL_sizex (dc) + 1, MGL_sizey (dc) + 1, 8, &pf); MGL_makeCurrentDC (memdc); } // Enable page flipping even for even for blitted surfaces if (forcemem) { vid.numpages = 1; } else { vid.numpages = dc->mi.maxPage + 1; if (vid.numpages > 1) { // Set up for page flipping MGL_setActivePage (dc, aPage = 1); MGL_setVisualPage (dc, vPage = 0, false); } if (vid.numpages > 3) vid.numpages = 3; } if (vid.numpages == 2) waitVRT = true; else waitVRT = false; return dc; } void VID_InitMGLDIB (HINSTANCE hInstance) { WNDCLASS wc; HDC hdc; int i; hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); /* Register the frame class */ wc.style = 0; wc.lpfnWndProc = (WNDPROC) MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = 0; wc.hCursor = LoadCursor (NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = 0; wc.lpszClassName = "WinQuake"; if (!RegisterClass (&wc)) Sys_Error ("Couldn't register window class"); /* Find the size for the DIB window */ /* Initialise the MGL for windowed operation */ MGL_setAppInstance (hInstance); registerAllMemDrivers (); MGL_initWindowed (""); modelist[0].type = MS_WINDOWED; modelist[0].width = 320; modelist[0].height = 240; strcpy (modelist[0].modedesc, "320x240"); modelist[0].mode13 = 0; modelist[0].modenum = MODE_WINDOWED; modelist[0].stretched = 0; modelist[0].dib = 1; modelist[0].fullscreen = 0; modelist[0].halfscreen = 0; modelist[0].bpp = 8; modelist[1].type = MS_WINDOWED; modelist[1].width = 640; modelist[1].height = 480; strcpy (modelist[1].modedesc, "640x480"); modelist[1].mode13 = 0; modelist[1].modenum = MODE_WINDOWED + 1; modelist[1].stretched = 1; modelist[1].dib = 1; modelist[1].fullscreen = 0; modelist[1].halfscreen = 0; modelist[1].bpp = 8; modelist[2].type = MS_WINDOWED; modelist[2].width = 800; modelist[2].height = 600; strcpy (modelist[2].modedesc, "800x600"); modelist[2].mode13 = 0; modelist[2].modenum = MODE_WINDOWED + 2; modelist[2].stretched = 1; modelist[2].dib = 1; modelist[2].fullscreen = 0; modelist[2].halfscreen = 0; modelist[2].bpp = 8; // automatically stretch the default mode up if > 640x480 desktop resolution hdc = GetDC (NULL); if ((GetDeviceCaps (hdc, HORZRES) > 640) && !COM_CheckParm ("-noautostretch")) { vid_default = MODE_WINDOWED + 1; } else { vid_default = MODE_WINDOWED; } windowed_default = vid_default; ReleaseDC (NULL, hdc); nummodes = 3; // reserve space for windowed mode DDActive = 0; } /* ================= VID_InitFullDIB ================= */ void VID_InitFullDIB (HINSTANCE hInstance) { DEVMODE devmode; int i, j, modenum, cmodes, existingmode, originalnummodes, lowestres; int numlowresmodes, bpp, done; int cstretch, istretch, mstretch; BOOL stat; // enumerate 8 bpp modes originalnummodes = nummodes; modenum = 0; lowestres = 99999; do { stat = EnumDisplaySettings (NULL, modenum, &devmode); if ((devmode.dmBitsPerPel == 8) && (devmode.dmPelsWidth <= MAXWIDTH) && (devmode.dmPelsHeight <= MAXHEIGHT) && (nummodes < MAX_MODE_LIST)) { devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { modelist[nummodes].type = MS_FULLDIB; modelist[nummodes].width = devmode.dmPelsWidth; modelist[nummodes].height = devmode.dmPelsHeight; modelist[nummodes].modenum = 0; modelist[nummodes].mode13 = 0; modelist[nummodes].stretched = 0; modelist[nummodes].halfscreen = 0; modelist[nummodes].dib = 1; modelist[nummodes].fullscreen = 1; modelist[nummodes].bpp = devmode.dmBitsPerPel; snprintf (modelist[nummodes].modedesc, sizeof (modelist[nummodes].modedesc), "%dx%d", devmode.dmPelsWidth, devmode.dmPelsHeight); // if the width is more than twice the height, reduce it by // half because this // is probably a dual-screen monitor if (!COM_CheckParm ("-noadjustaspect")) { if (modelist[nummodes].width > (modelist[nummodes].height << 1)) { modelist[nummodes].width >>= 1; modelist[nummodes].halfscreen = 1; snprintf (modelist[nummodes].modedesc, sizeof (modelist[nummodes].modedesc), "%dx%d", modelist[nummodes].width, modelist[nummodes].height); } } for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { if ((modelist[nummodes].width == modelist[i].width) && (modelist[nummodes].height == modelist[i].height)) { existingmode = 1; break; } } if (!existingmode) { if (modelist[nummodes].width < lowestres) lowestres = modelist[nummodes].width; nummodes++; } } } modenum++; } while (stat); // see if any of them were actually settable; if so, this is our mode list, // else enumerate all modes; our mode list is whichever ones are settable // with > 8 bpp if (nummodes == originalnummodes) { modenum = 0; lowestres = 99999; Con_SafePrintf ("No 8-bpp fullscreen DIB modes found\n"); do { stat = EnumDisplaySettings (NULL, modenum, &devmode); if ((((devmode.dmPelsWidth <= MAXWIDTH) && (devmode.dmPelsHeight <= MAXHEIGHT)) || (!COM_CheckParm ("-noadjustaspect") && (devmode.dmPelsWidth <= (MAXWIDTH * 2)) && (devmode.dmPelsWidth > (devmode.dmPelsHeight * 2)))) && (nummodes < MAX_MODE_LIST) && (devmode.dmBitsPerPel > 8)) { devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { modelist[nummodes].type = MS_FULLDIB; modelist[nummodes].width = devmode.dmPelsWidth; modelist[nummodes].height = devmode.dmPelsHeight; modelist[nummodes].modenum = 0; modelist[nummodes].mode13 = 0; modelist[nummodes].stretched = 0; modelist[nummodes].halfscreen = 0; modelist[nummodes].dib = 1; modelist[nummodes].fullscreen = 1; modelist[nummodes].bpp = devmode.dmBitsPerPel; snprintf (modelist[nummodes].modedesc, sizeof (modelist[nummodes].modedesc), "%dx%d", devmode.dmPelsWidth, devmode.dmPelsHeight); // if the width is more than twice the height, reduce it // by half because this // is probably a dual-screen monitor if (!COM_CheckParm ("-noadjustaspect")) { if (modelist[nummodes].width > (modelist[nummodes].height * 2)) { modelist[nummodes].width >>= 1; modelist[nummodes].halfscreen = 1; snprintf (modelist[nummodes].modedesc, sizeof (modelist[nummodes].modedesc), "%dx%d", modelist[nummodes].width, modelist[nummodes].height); } } for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { if ((modelist[nummodes].width == modelist[i].width) && (modelist[nummodes].height == modelist[i].height)) { // pick the lowest available bpp if (modelist[nummodes].bpp < modelist[i].bpp) modelist[i] = modelist[nummodes]; existingmode = 1; break; } } if (!existingmode) { if (modelist[nummodes].width < lowestres) lowestres = modelist[nummodes].width; nummodes++; } } } modenum++; } while (stat); } // see if there are any low-res modes that aren't being reported numlowresmodes = sizeof (lowresmodes) / sizeof (lowresmodes[0]); bpp = 8; done = 0; // first make sure the driver doesn't just answer yes to all tests devmode.dmBitsPerPel = 8; devmode.dmPelsWidth = 42; devmode.dmPelsHeight = 37; devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { done = 1; } while (!done) { for (j = 0; (j < numlowresmodes) && (nummodes < MAX_MODE_LIST); j++) { devmode.dmBitsPerPel = bpp; devmode.dmPelsWidth = lowresmodes[j].width; devmode.dmPelsHeight = lowresmodes[j].height; devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { modelist[nummodes].type = MS_FULLDIB; modelist[nummodes].width = devmode.dmPelsWidth; modelist[nummodes].height = devmode.dmPelsHeight; modelist[nummodes].modenum = 0; modelist[nummodes].mode13 = 0; modelist[nummodes].stretched = 0; modelist[nummodes].halfscreen = 0; modelist[nummodes].dib = 1; modelist[nummodes].fullscreen = 1; modelist[nummodes].bpp = devmode.dmBitsPerPel; snprintf (modelist[nummodes].modedesc, sizeof (modelist[nummodes].modedesc), "%dx%d", devmode.dmPelsWidth, devmode.dmPelsHeight); // we only want the lowest-bpp version of each mode for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { if ((modelist[nummodes].width == modelist[i].width) && (modelist[nummodes].height == modelist[i].height) && (modelist[nummodes].bpp >= modelist[i].bpp)) { existingmode = 1; break; } } if (!existingmode) { if (modelist[nummodes].width < lowestres) lowestres = modelist[nummodes].width; nummodes++; } } } switch (bpp) { case 8: bpp = 16; break; case 16: bpp = 32; break; case 32: done = 1; break; } } // now add the lowest stretch-by-2 pseudo-modes between 320-wide // (inclusive) and lowest real res (not inclusive) // don't bother if we have a real VGA mode 0x13 mode if (!is_mode0x13) { for (i = originalnummodes, cstretch = 0; i < nummodes; i++) { if (((modelist[i].width >> 1) < lowestres) && ((modelist[i].width >> 1) >= 320)) { lowestres = modelist[i].width >> 1; cstretch = 1; mstretch = i; } } if ((nummodes + cstretch) > MAX_MODE_LIST) cstretch = MAX_MODE_LIST - nummodes; if (cstretch > 0) { for (i = (nummodes - 1); i >= originalnummodes; i--) modelist[i + cstretch] = modelist[i]; nummodes += cstretch; istretch = originalnummodes; modelist[istretch] = modelist[mstretch]; modelist[istretch].width >>= 1; modelist[istretch].height >>= 1; modelist[istretch].stretched = 1; snprintf (modelist[istretch].modedesc, sizeof (modelist[istretch].modedesc), "%dx%d", modelist[istretch].width, modelist[istretch].height); } } if (nummodes != originalnummodes) vid_default = MODE_FULLSCREEN_DEFAULT; else Con_SafePrintf ("No fullscreen DIB modes found\n"); } /* ================= VID_NumModes ================= */ int VID_NumModes (void) { return nummodes; } /* ================= VID_GetModePtr ================= */ vmode_t * VID_GetModePtr (int modenum) { if ((modenum >= 0) && (modenum < nummodes)) return &modelist[modenum]; else return &badmode; } /* ================= VID_CheckModedescFixup ================= */ void VID_CheckModedescFixup (int mode) { int x, y, stretch; if (mode == MODE_SETTABLE_WINDOW) { modelist[mode].stretched = vid_stretch_by_2->int_val; stretch = modelist[mode].stretched; if (vid_config_x->int_val < (320 << stretch)) Cvar_SetValue (vid_config_x, 320 << stretch); if (vid_config_y->int_val < (200 << stretch)) Cvar_SetValue (vid_config_y, 200 << stretch); x = vid_config_x->int_val; y = vid_config_y->int_val; snprintf (modelist[mode].modedesc, sizeof (modelist[mode].modedesc), "%dx%d", x, y); modelist[mode].width = x; modelist[mode].height = y; } } /* ================= VID_GetModeDescriptionMemCheck ================= */ char * VID_GetModeDescriptionMemCheck (int mode) { char *pinfo; vmode_t *pv; if ((mode < 0) || (mode >= nummodes)) return NULL; VID_CheckModedescFixup (mode); pv = VID_GetModePtr (mode); pinfo = pv->modedesc; if (VID_CheckAdequateMem (pv->width, pv->height)) { return pinfo; } else { return NULL; } } /* ================= VID_GetModeDescription ================= */ char * VID_GetModeDescription (int mode) { char *pinfo; vmode_t *pv; if ((mode < 0) || (mode >= nummodes)) return NULL; VID_CheckModedescFixup (mode); pv = VID_GetModePtr (mode); pinfo = pv->modedesc; return pinfo; } /* ================= VID_GetModeDescription2 Tacks on "windowed" or "fullscreen" ================= */ char * VID_GetModeDescription2 (int mode) { static char pinfo[40]; vmode_t *pv; if ((mode < 0) || (mode >= nummodes)) return NULL; VID_CheckModedescFixup (mode); pv = VID_GetModePtr (mode); if (modelist[mode].type == MS_FULLSCREEN) { snprintf (pinfo, sizeof (pinfo), "%s fullscreen", pv->modedesc); } else if (modelist[mode].type == MS_FULLDIB) { snprintf (pinfo, sizeof (pinfo), "%s fullscreen", pv->modedesc); } else { snprintf (pinfo, sizeof (pinfo), "%s windowed", pv->modedesc); } return pinfo; } // KJB: Added this to return the mode driver name in description for console char * VID_GetExtModeDescription (int mode) { static char pinfo[40]; vmode_t *pv; if ((mode < 0) || (mode >= nummodes)) return NULL; VID_CheckModedescFixup (mode); pv = VID_GetModePtr (mode); if (modelist[mode].type == MS_FULLSCREEN) { snprintf (pinfo, sizeof (pinfo), "%s fullscreen %s", pv->modedesc, MGL_modeDriverName (pv->modenum)); } else if (modelist[mode].type == MS_FULLDIB) { snprintf (pinfo, sizeof (pinfo), "%s fullscreen DIB", pv->modedesc); } else { snprintf (pinfo, sizeof (pinfo), "%s windowed", pv->modedesc); } return pinfo; } void DestroyDIBWindow (void) { if (modestate == MS_WINDOWED) { // destroy the associated MGL DC's; the window gets reused if (windc) MGL_destroyDC (windc); if (dibdc) MGL_destroyDC (dibdc); windc = dibdc = NULL; } } void DestroyFullscreenWindow (void) { if (modestate == MS_FULLSCREEN) { // destroy the existing fullscreen mode and DC's if (mgldc) MGL_destroyDC (mgldc); if (memdc) MGL_destroyDC (memdc); mgldc = memdc = NULL; } } void DestroyFullDIBWindow (void) { if (modestate == MS_FULLDIB) { ChangeDisplaySettings (NULL, CDS_FULLSCREEN); // Destroy the fullscreen DIB window and associated MGL DC's if (windc) MGL_destroyDC (windc); if (dibdc) MGL_destroyDC (dibdc); windc = dibdc = NULL; } } qboolean VID_SetWindowedMode (int modenum) { HDC hdc; pixel_format_t pf; qboolean stretched; int lastmodestate; LONG wlong; if (!windowed_mode_set) { if (COM_CheckParm ("-resetwinpos")) { Cvar_SetValue (vid_window_x, 0.0); Cvar_SetValue (vid_window_y, 0.0); } windowed_mode_set; } VID_CheckModedescFixup (modenum); DDActive = 0; lastmodestate = modestate; DestroyFullscreenWindow (); DestroyFullDIBWindow (); if (windc) MGL_destroyDC (windc); if (dibdc) MGL_destroyDC (dibdc); windc = dibdc = NULL; // KJB: Signal to the MGL that we are going back to windowed mode if (!MGL_changeDisplayMode (grWINDOWED)) initFatalError (); WindowRect.top = WindowRect.left = 0; WindowRect.right = modelist[modenum].width; WindowRect.bottom = modelist[modenum].height; stretched = modelist[modenum].stretched; DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; if (stretched) { DIBWidth >>= 1; DIBHeight >>= 1; } WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; ExWindowStyle = 0; AdjustWindowRectEx (&WindowRect, WindowStyle, FALSE, 0); // the first time we're called to set the mode, create the window we'll use // for the rest of the session if (!vid_mode_set) { mainwindow = CreateWindowEx (ExWindowStyle, "WinQuake", "WinQuake", WindowStyle, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, NULL, NULL, global_hInstance, NULL); if (!mainwindow) Sys_Error ("Couldn't create DIB window"); // tell MGL to use this window for fullscreen modes MGL_registerFullScreenWindow (mainwindow); vid_mode_set = true; } else { SetWindowLong (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); SetWindowLong (mainwindow, GWL_EXSTYLE, ExWindowStyle); } if (!SetWindowPos (mainwindow, NULL, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_HIDEWINDOW)) { Sys_Error ("Couldn't resize DIB window"); } if (hide_window) return true; // position and show the DIB window VID_CheckWindowXY (); SetWindowPos (mainwindow, NULL, vid_window_x->int_val, vid_window_y->int_val, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); if (force_minimized) ShowWindow (mainwindow, SW_MINIMIZE); else ShowWindow (mainwindow, SW_SHOWDEFAULT); UpdateWindow (mainwindow); modestate = MS_WINDOWED; vid_fulldib_on_focus_mode = 0; // because we have set the background brush for the window to NULL // (to avoid flickering when re-sizing the window on the desktop), // we clear the window to black when created, otherwise it will be // empty while Quake starts up. hdc = GetDC (mainwindow); PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); ReleaseDC (mainwindow, hdc); /* Create the MGL window DC and the MGL memory DC */ if ((windc = MGL_createWindowedDC (mainwindow)) == NULL) MGL_fatalError ("Unable to create Windowed DC!"); if ((dibdc = MGL_createMemoryDC (DIBWidth, DIBHeight, 8, &pf)) == NULL) MGL_fatalError ("Unable to create Memory DC!"); MGL_makeCurrentDC (dibdc); vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; vid.numpages = 1; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.height = vid.conheight = DIBHeight; vid.width = vid.conwidth = DIBWidth; vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); vid_stretched = stretched; SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); return true; } qboolean VID_SetFullscreenMode (int modenum) { DDActive = 1; DestroyDIBWindow (); DestroyFullDIBWindow (); mode = modelist[modenum].modenum; // Destroy old DC's, resetting back to fullscreen mode if (mgldc) MGL_destroyDC (mgldc); if (memdc) MGL_destroyDC (memdc); mgldc = memdc = NULL; if ((mgldc = createDisplayDC (modelist[modenum].stretched || vid_nopageflip->int_val)) == NULL) { return false; } modestate = MS_FULLSCREEN; vid_fulldib_on_focus_mode = 0; vid.buffer = vid.conbuffer = vid.direct = NULL; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; DIBHeight = vid.height = vid.conheight = modelist[modenum].height; DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); vid_stretched = modelist[modenum].stretched; // needed because we're not getting WM_MOVE messages fullscreen on NT window_x = 0; window_y = 0; // set the large icon, so the Quake icon will show up in the taskbar SendMessage (mainwindow, WM_SETICON, (WPARAM) 1, (LPARAM) hIcon); SendMessage (mainwindow, WM_SETICON, (WPARAM) 0, (LPARAM) hIcon); // shouldn't be needed, but Kendall needs to let us get the activation // message for this not to be needed on NT AppActivate (true, false); return true; } qboolean VID_SetFullDIBMode (int modenum) { HDC hdc; pixel_format_t pf; int lastmodestate; DDActive = 0; DestroyFullscreenWindow (); DestroyDIBWindow (); if (windc) MGL_destroyDC (windc); if (dibdc) MGL_destroyDC (dibdc); windc = dibdc = NULL; // KJB: Signal to the MGL that we are going back to windowed mode if (!MGL_changeDisplayMode (grWINDOWED)) initFatalError (); gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; gdevmode.dmBitsPerPel = modelist[modenum].bpp; gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum]. stretched << modelist[modenum].halfscreen; gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched; gdevmode.dmSize = sizeof (gdevmode); if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) Sys_Error ("Couldn't set fullscreen DIB mode"); lastmodestate = modestate; modestate = MS_FULLDIB; vid_fulldib_on_focus_mode = modenum; WindowRect.top = WindowRect.left = 0; hdc = GetDC (NULL); WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; ReleaseDC (NULL, hdc); DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; ExWindowStyle = 0; AdjustWindowRectEx (&WindowRect, WindowStyle, FALSE, 0); SetWindowLong (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); SetWindowLong (mainwindow, GWL_EXSTYLE, ExWindowStyle); if (!SetWindowPos (mainwindow, NULL, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, SWP_NOCOPYBITS | SWP_NOZORDER)) { Sys_Error ("Couldn't resize DIB window"); } // position and show the DIB window SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); ShowWindow (mainwindow, SW_SHOWDEFAULT); UpdateWindow (mainwindow); // Because we have set the background brush for the window to NULL // (to avoid flickering when re-sizing the window on the desktop), we // clear the window to black when created, otherwise it will be // empty while Quake starts up. hdc = GetDC (mainwindow); PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); ReleaseDC (mainwindow, hdc); /* Create the MGL window DC and the MGL memory DC */ if ((windc = MGL_createWindowedDC (mainwindow)) == NULL) MGL_fatalError ("Unable to create Fullscreen DIB DC!"); if ((dibdc = MGL_createMemoryDC (DIBWidth, DIBHeight, 8, &pf)) == NULL) MGL_fatalError ("Unable to create Memory DC!"); MGL_makeCurrentDC (dibdc); vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; vid.numpages = 1; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.height = vid.conheight = DIBHeight; vid.width = vid.conwidth = DIBWidth; vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); vid_stretched = modelist[modenum].stretched; // needed because we're not getting WM_MOVE messages fullscreen on NT window_x = 0; window_y = 0; return true; } void VID_RestoreOldMode (int original_mode) { static qboolean inerror = false; if (inerror) return; in_mode_set = false; inerror = true; // make sure mode set happens (video mode changes) vid_modenum = original_mode - 1; if (!VID_SetMode (original_mode, vid_curpal)) { vid_modenum = MODE_WINDOWED - 1; if (!VID_SetMode (windowed_default, vid_curpal)) Sys_Error ("Can't set any video mode"); } inerror = false; } void VID_SetDefaultMode (void) { if (vid_initialized) VID_SetMode (0, vid_curpal); IN_DeactivateMouse (); } int VID_SetMode (int modenum, unsigned char *palette) { int original_mode, temp, dummy; qboolean stat; MSG msg; HDC hdc; while ((modenum >= nummodes) || (modenum < 0)) { if (vid_modenum == NO_MODE) { if (modenum == vid_default) { modenum = windowed_default; } else { modenum = vid_default; } Cvar_SetValue (vid_mode, (float) modenum); } else { Cvar_SetValue (vid_mode, (float) vid_modenum); return 0; } } if (!force_mode_set && (modenum == vid_modenum)) return true; // so Con_Printfs don't mess us up by forcing vid and snd updates temp = scr_disabled_for_loading; scr_disabled_for_loading = true; in_mode_set = true; CDAudio_Pause (); S_ClearBuffer (); if (vid_modenum == NO_MODE) original_mode = windowed_default; else original_mode = vid_modenum; // Set either the fullscreen or windowed mode if (modelist[modenum].type == MS_WINDOWED) { if (_windowed_mouse->int_val) { stat = VID_SetWindowedMode (modenum); IN_ActivateMouse (); IN_HideMouse (); } else { IN_DeactivateMouse (); IN_ShowMouse (); stat = VID_SetWindowedMode (modenum); } } else if (modelist[modenum].type == MS_FULLDIB) { stat = VID_SetFullDIBMode (modenum); IN_ActivateMouse (); IN_HideMouse (); } else { stat = VID_SetFullscreenMode (modenum); IN_ActivateMouse (); IN_HideMouse (); } window_width = vid.width << vid_stretched; window_height = vid.height << vid_stretched; VID_UpdateWindowStatus (); CDAudio_Resume (); scr_disabled_for_loading = temp; if (!stat) { VID_RestoreOldMode (original_mode); return false; } if (hide_window) return true; // now we try to make sure we get the focus on the mode switch, because // sometimes in some systems we don't. We grab the foreground, then // finish setting up, pump all our messages, and sleep for a little while // to let messages finish bouncing around the system, then we put // ourselves at the top of the z order, then grab the foreground again, // Who knows if it helps, but it probably doesn't hurt if (!force_minimized) SetForegroundWindow (mainwindow); hdc = GetDC (NULL); if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) vid_palettized = true; else vid_palettized = false; VID_SetPalette (palette); ReleaseDC (NULL, hdc); vid_modenum = modenum; Cvar_SetValue (vid_mode, (float) vid_modenum); if (!VID_AllocBuffers (vid.width, vid.height)) { // couldn't get memory for this mode; try to fall back to previous // mode VID_RestoreOldMode (original_mode); return false; } D_InitCaches (vid_surfcache, vid_surfcachesize); while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage (&msg); DispatchMessage (&msg); } Sleep (100); if (!force_minimized) { SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS); SetForegroundWindow (mainwindow); } // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); if (!msg_suppress_1) Con_SafePrintf ("%s\n", VID_GetModeDescription (vid_modenum)); VID_SetPalette (palette); in_mode_set = false; vid.recalc_refdef = 1; return true; } void VID_LockBuffer (void) { if (dibdc) return; lockcount++; if (lockcount > 1) return; MGL_beginDirectAccess (); if (memdc) { // Update surface pointer for linear access modes vid.buffer = vid.conbuffer = vid.direct = memdc->surface; vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; } else if (mgldc) { // Update surface pointer for linear access modes vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; } if (r_dowarp) d_viewbuffer = r_warpbuffer; else d_viewbuffer = (void *) (byte *) vid.buffer; if (r_dowarp) screenwidth = WARP_WIDTH; else screenwidth = vid.rowbytes; } void VID_UnlockBuffer (void) { if (dibdc) return; lockcount--; if (lockcount > 0) return; if (lockcount < 0) Sys_Error ("Unbalanced unlock"); MGL_endDirectAccess (); // to turn up any unlocked accesses vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; } int VID_ForceUnlockedAndReturnState (void) { int lk; if (!lockcount) return 0; lk = lockcount; if (dibdc) { lockcount = 0; } else { lockcount = 1; VID_UnlockBuffer (); } return lk; } void VID_ForceLockState (int lk) { if (!dibdc && lk) { lockcount = 0; VID_LockBuffer (); } lockcount = lk; } void VID_SetPalette (unsigned char *palette) { INT i; palette_t pal[256]; HDC hdc; if (!Minimized) { palette_changed = true; // make sure we have the static colors if we're the active app hdc = GetDC (NULL); if (vid_palettized && ActiveApp) { if (GetSystemPaletteUse (hdc) == SYSPAL_STATIC) { // switch to SYSPAL_NOSTATIC and remap the colors SetSystemPaletteUse (hdc, SYSPAL_NOSTATIC); syscolchg = true; pal_is_nostatic = true; } } ReleaseDC (NULL, hdc); // Translate the palette values to an MGL palette array and // set the values. for (i = 0; i < 256; i++) { pal[i].red = palette[i * 3]; pal[i].green = palette[i * 3 + 1]; pal[i].blue = palette[i * 3 + 2]; } if (DDActive) { if (!mgldc) return; MGL_setPalette (mgldc, pal, 256, 0); MGL_realizePalette (mgldc, 256, 0, false); if (memdc) MGL_setPalette (memdc, pal, 256, 0); } else { if (!windc) return; MGL_setPalette (windc, pal, 256, 0); MGL_realizePalette (windc, 256, 0, false); if (dibdc) { MGL_setPalette (dibdc, pal, 256, 0); MGL_realizePalette (dibdc, 256, 0, false); } } } memcpy (vid_curpal, palette, sizeof (vid_curpal)); if (syscolchg) { PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM) 0, (LPARAM) 0); syscolchg = false; } } void VID_ShiftPalette (unsigned char *palette) { VID_SetPalette (palette); } /* ================= VID_DescribeCurrentMode_f ================= */ void VID_DescribeCurrentMode_f (void) { Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); } /* ================= VID_NumModes_f ================= */ void VID_NumModes_f (void) { if (nummodes == 1) Con_Printf ("%d video mode is available\n", nummodes); else Con_Printf ("%d video modes are available\n", nummodes); } /* ================= VID_DescribeMode_f ================= */ void VID_DescribeMode_f (void) { int modenum; modenum = Q_atoi (Cmd_Argv (1)); Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); } /* ================= VID_DescribeModes_f ================= */ void VID_DescribeModes_f (void) { int i, lnummodes; char *pinfo; qboolean na; vmode_t *pv; na = false; lnummodes = VID_NumModes (); for (i = 0; i < lnummodes; i++) { pv = VID_GetModePtr (i); pinfo = VID_GetExtModeDescription (i); if (VID_CheckAdequateMem (pv->width, pv->height)) { Con_Printf ("%2d: %s\n", i, pinfo); } else { Con_Printf ("**: %s\n", pinfo); na = true; } } if (na) { Con_Printf ("\n[**: not enough system RAM for mode]\n"); } } /* ================= VID_TestMode_f ================= */ void VID_TestMode_f (void) { int modenum; double testduration; if (!vid_testingmode) { modenum = Q_atoi (Cmd_Argv (1)); if (VID_SetMode (modenum, vid_curpal)) { vid_testingmode = 1; testduration = Q_atof (Cmd_Argv (2)); if (testduration == 0) testduration = 5.0; vid_testendtime = realtime + testduration; } } } /* ================= VID_Windowed_f ================= */ void VID_Windowed_f (void) { VID_SetMode (vid_windowed_mode->int_val, vid_curpal); } /* ================= VID_Fullscreen_f ================= */ void VID_Fullscreen_f (void) { VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); } /* ================= VID_Minimize_f ================= */ void VID_Minimize_f (void) { // we only support minimizing windows; if you're fullscreen, // switch to windowed first if (modestate == MS_WINDOWED) ShowWindow (mainwindow, SW_MINIMIZE); } /* ================= VID_ForceMode_f ================= */ void VID_ForceMode_f (void) { int modenum; double testduration; if (!vid_testingmode) { modenum = Q_atoi (Cmd_Argv (1)); force_mode_set = 1; VID_SetMode (modenum, vid_curpal); force_mode_set = 0; } } void VID_Init (unsigned char *palette) { int i, bestmatch, bestmatchmetric, t, dr, dg, db; int basenummodes; byte *ptmp; vid_mode = Cvar_Get ("vid_mode", "0", CVAR_NONE, 0, "None"); vid_wait = Cvar_Get ("vid_wait", "0", CVAR_NONE, 0, "None"); vid_nopageflip = Cvar_Get ("vid_nopageflip", "0", CVAR_ARCHIVE, 0, "None"); _vid_wait_override = Cvar_Get ("_vid_wait_override", "0", CVAR_ARCHIVE, 0, "None"); _vid_default_mode = Cvar_Get ("_vid_default_mode", "0", CVAR_ARCHIVE, 0, "None"); _vid_default_mode_win = Cvar_Get ("_vid_default_mode_win", "3", CVAR_ARCHIVE, 0, "None"); vid_config_x = Cvar_Get ("vid_config_x", "800", CVAR_ARCHIVE, 0, "None"); vid_config_y = Cvar_Get ("vid_config_y", "600", CVAR_ARCHIVE, 0, "None"); vid_stretch_by_2 = Cvar_Get ("vid_stretch_by_2", "1", CVAR_ARCHIVE, 0, "None"); _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, 0, "None"); vid_fullscreen_mode = Cvar_Get ("vid_fullscreen_mode", "3", CVAR_ARCHIVE, 0, "None"); vid_windowed_mode = Cvar_Get ("vid_windowed_mode", "0", CVAR_ARCHIVE, 0, "None"); block_switch = Cvar_Get ("block_switch", "0", CVAR_ARCHIVE, 0, "None"); vid_window_x = Cvar_Get ("vid_window_x", "0", CVAR_ARCHIVE, 0, "None"); vid_window_y = Cvar_Get ("vid_window_y", "0", CVAR_ARCHIVE, 0, "None"); Cmd_AddCommand ("vid_testmode", VID_TestMode_f, "No Description"); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, "No Description"); Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f, "No Description"); Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, "No Description"); Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, "No Description"); Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f, "No Description"); Cmd_AddCommand ("vid_windowed", VID_Windowed_f, "No Description"); Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f, "No Description"); Cmd_AddCommand ("vid_minimize", VID_Minimize_f, "No Description"); if (COM_CheckParm ("-dibonly")) dibonly = true; VID_InitMGLDIB (global_hInstance); basenummodes = nummodes; if (!dibonly) VID_InitMGLFull (global_hInstance); // if there are no non-windowed modes, or only windowed and mode 0x13, then use // fullscreen DIBs as well if (((nummodes == basenummodes) || ((nummodes == (basenummodes + 1)) && is_mode0x13)) && !COM_CheckParm ("-nofulldib")) { VID_InitFullDIB (global_hInstance); } vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); vid_testingmode = 0; // GDI doesn't let us remap palette index 0, so we'll remap color // mappings from that black to another one bestmatchmetric = 256 * 256 * 3; for (i = 1; i < 256; i++) { dr = palette[0] - palette[i * 3]; dg = palette[1] - palette[i * 3 + 1]; db = palette[2] - palette[i * 3 + 2]; t = (dr * dr) + (dg * dg) + (db * db); if (t < bestmatchmetric) { bestmatchmetric = t; bestmatch = i; if (t == 0) break; } } for (i = 0, ptmp = vid.colormap; i < (1 << (VID_CBITS + 8)); i++, ptmp++) { if (*ptmp == 0) *ptmp = bestmatch; } if (COM_CheckParm ("-startwindowed")) { startwindowed = 1; vid_default = windowed_default; } if (hwnd_dialog) DestroyWindow (hwnd_dialog); // sound initialization has to go here, preceded by a windowed mode set, // so there's a window for DirectSound to work with but we're not yet // fullscreen so the "hardware already in use" dialog is visible if it // gets displayed // keep the window minimized until we're ready for the first real mode set hide_window = true; VID_SetMode (MODE_WINDOWED, palette); hide_window = false; S_Init (); vid_initialized = true; force_mode_set = true; VID_SetMode (vid_default, palette); force_mode_set = false; vid_realmode = vid_modenum; VID_SetPalette (palette); vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; strcpy (badmode.modedesc, "Bad mode"); } void VID_Shutdown (void) { HDC hdc; int dummy; if (vid_initialized) { if (modestate == MS_FULLDIB) ChangeDisplaySettings (NULL, CDS_FULLSCREEN); PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM) mainwindow, (LPARAM) 0); PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM) 0, (LPARAM) 0); AppActivate (false, false); DestroyDIBWindow (); DestroyFullscreenWindow (); DestroyFullDIBWindow (); if (hwnd_dialog) DestroyWindow (hwnd_dialog); if (mainwindow) DestroyWindow (mainwindow); MGL_exit (); vid_testingmode = 0; vid_initialized = 0; } } /* ================ FlipScreen ================ */ void FlipScreen (vrect_t *rects) { HRESULT ddrval; // Flip the surfaces if (DDActive) { if (mgldc) { if (memdc) { while (rects) { if (vid_stretched) { MGL_stretchBltCoord (mgldc, memdc, rects->x, rects->y, rects->x + rects->width, rects->y + rects->height, rects->x << 1, rects->y << 1, (rects->x + rects->width) << 1, (rects->y + rects->height) << 1); } else { MGL_bitBltCoord (mgldc, memdc, rects->x, rects->y, (rects->x + rects->width), (rects->y + rects->height), rects->x, rects->y, MGL_REPLACE_MODE); } rects = rects->pnext; } } if (vid.numpages > 1) { // We have a flipping surface, so do a hard page flip aPage = (aPage + 1) % vid.numpages; vPage = (vPage + 1) % vid.numpages; MGL_setActivePage (mgldc, aPage); MGL_setVisualPage (mgldc, vPage, waitVRT); } } } else { HDC hdcScreen; hdcScreen = GetDC (mainwindow); if (windc && dibdc) { MGL_setWinDC (windc, hdcScreen); while (rects) { if (vid_stretched) { MGL_stretchBltCoord (windc, dibdc, rects->x, rects->y, rects->x + rects->width, rects->y + rects->height, rects->x << 1, rects->y << 1, (rects->x + rects->width) << 1, (rects->y + rects->height) << 1); } else { MGL_bitBltCoord (windc, dibdc, rects->x, rects->y, rects->x + rects->width, rects->y + rects->height, rects->x, rects->y, MGL_REPLACE_MODE); } rects = rects->pnext; } } ReleaseDC (mainwindow, hdcScreen); } } void VID_Update (vrect_t *rects) { vrect_t rect; RECT trect; if (!vid_palettized && palette_changed) { palette_changed = false; rect.x = 0; rect.y = 0; rect.width = vid.width; rect.height = vid.height; rect.pnext = NULL; rects = ▭ } if (firstupdate) { if (modestate == MS_WINDOWED) { GetWindowRect (mainwindow, &trect); if ((trect.left != vid_window_x->int_val) || (trect.top != vid_window_y->int_val)) { if (COM_CheckParm ("-resetwinpos")) { Cvar_SetValue (vid_window_x, 0.0); Cvar_SetValue (vid_window_y, 0.0); } VID_CheckWindowXY (); SetWindowPos (mainwindow, NULL, vid_window_x->int_val, vid_window_y->int_val, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); } } if ((_vid_default_mode_win->int_val != vid_default) && (!startwindowed || (_vid_default_mode_win->int_val < MODE_FULLSCREEN_DEFAULT))) { firstupdate = 0; if (COM_CheckParm ("-resetwinpos")) { Cvar_SetValue (vid_window_x, 0.0); Cvar_SetValue (vid_window_y, 0.0); } if ((_vid_default_mode_win->int_val < 0) || (_vid_default_mode_win->int_val >= nummodes)) { Cvar_SetValue (_vid_default_mode_win, windowed_default); } Cvar_SetValue (vid_mode, _vid_default_mode_win->int_val); } } // We've drawn the frame; copy it to the screen FlipScreen (rects); if (vid_testingmode) { if (realtime >= vid_testendtime) { VID_SetMode (vid_realmode, vid_curpal); vid_testingmode = 0; } } else { if (vid_mode->int_val != vid_realmode) { VID_SetMode (vid_mode->int_val, vid_curpal); Cvar_SetValue (vid_mode, (float) vid_modenum); // so if mode set fails, we don't keep on // trying to set that mode vid_realmode = vid_modenum; } } // handle the mouse state when windowed if that's changed if (modestate == MS_WINDOWED) { if (_windowed_mouse->int_val != windowed_mouse) { if (_windowed_mouse->int_val) { IN_ActivateMouse (); IN_HideMouse (); } else { IN_DeactivateMouse (); IN_ShowMouse (); } windowed_mouse = _windowed_mouse->int_val; } } } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) { int i, j, reps, repshift; vrect_t rect; if (!vid_initialized) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } if (vid.numpages == 1) { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (&backingbuf[(i + j) * 24], vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, width); memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, &pbitmap[(i >> repshift) * width], width); } } VID_UnlockBuffer (); rect.x = x; rect.y = y; rect.width = width; rect.height = height << repshift; rect.pnext = NULL; FlipScreen (&rect); } else { // unlock if locked if (lockcount > 0) MGL_endDirectAccess (); // set the active page to the displayed page MGL_setActivePage (mgldc, vPage); // lock the screen MGL_beginDirectAccess (); // save from and draw to screen for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (&backingbuf[(i + j) * 24], (byte *) mgldc->surface + x + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, width); memcpy ((byte *) mgldc->surface + x + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, &pbitmap[(i >> repshift) * width], width); } } // unlock the screen MGL_endDirectAccess (); // restore the original active page MGL_setActivePage (mgldc, aPage); // relock the screen if it was locked if (lockcount > 0) MGL_beginDirectAccess (); } } /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { int i, j, reps, repshift; vrect_t rect; if (!vid_initialized) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } if (vid.numpages == 1) { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, &backingbuf[(i + j) * 24], width); } } VID_UnlockBuffer (); rect.x = x; rect.y = y; rect.width = width; rect.height = height << repshift; rect.pnext = NULL; FlipScreen (&rect); } else { // unlock if locked if (lockcount > 0) MGL_endDirectAccess (); // set the active page to the displayed page MGL_setActivePage (mgldc, vPage); // lock the screen MGL_beginDirectAccess (); // restore to the screen for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy ((byte *) mgldc->surface + x + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, &backingbuf[(i + j) * 24], width); } } // unlock the screen MGL_endDirectAccess (); // restore the original active page MGL_setActivePage (mgldc, aPage); // relock the screen if it was locked if (lockcount > 0) MGL_beginDirectAccess (); } } //========================================================================== byte scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT, '*', K_ALT, ' ', 0, K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0, K_HOME, K_UPARROW, K_PGUP, '-', K_LEFTARROW, '5', K_RIGHTARROW, '+', K_END, // 4 K_DOWNARROW, K_PGDN, K_INS, K_DEL, 0, 0, 0, K_F11, K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 7 }; /* ======= MapKey Map from windows to quake keynums ======= */ int MapKey (int key) { key = (key >> 16) & 255; if (key > 127) return 0; return scantokey[key]; } void AppActivate (BOOL fActive, BOOL minimize) /**************************************************************************** * * Function: AppActivate * Parameters: fActive - True if app is activating * * Description: If the application is activating, then swap the system * into SYSPAL_NOSTATIC mode so that our palettes will display * correctly. * ****************************************************************************/ { HDC hdc; int i, t; static BOOL sound_active; ActiveApp = fActive; // messy, but it seems to work if (vid_fulldib_on_focus_mode) { Minimized = minimize; if (Minimized) ActiveApp = false; } MGL_appActivate (windc, ActiveApp); if (vid_initialized) { // yield the palette if we're losing the focus hdc = GetDC (NULL); if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { if (ActiveApp) { if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) { if (GetSystemPaletteUse (hdc) == SYSPAL_STATIC) { // switch to SYSPAL_NOSTATIC and remap the colors SetSystemPaletteUse (hdc, SYSPAL_NOSTATIC); syscolchg = true; pal_is_nostatic = true; } } } else if (pal_is_nostatic) { if (GetSystemPaletteUse (hdc) == SYSPAL_NOSTATIC) { // switch back to SYSPAL_STATIC and the old mapping SetSystemPaletteUse (hdc, SYSPAL_STATIC); syscolchg = true; } pal_is_nostatic = false; } } if (!Minimized) VID_SetPalette (vid_curpal); scr_fullupdate = 0; ReleaseDC (NULL, hdc); } // enable/disable sound on focus gain/loss if (!ActiveApp && sound_active) { S_BlockSound (); S_ClearBuffer (); sound_active = false; } else if (ActiveApp && !sound_active) { S_UnblockSound (); S_ClearBuffer (); sound_active = true; } // minimize/restore fulldib windows/mouse-capture normal windows on demand if (!in_mode_set) { if (ActiveApp) { if (vid_fulldib_on_focus_mode) { if (vid_initialized) { msg_suppress_1 = true; // don't want to see normal mode // set message VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); msg_suppress_1 = false; t = in_mode_set; in_mode_set = true; AppActivate (true, false); in_mode_set = t; } IN_ActivateMouse (); IN_HideMouse (); } else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) { IN_ActivateMouse (); IN_HideMouse (); } } if (!ActiveApp) { if (modestate == MS_FULLDIB) { if (vid_initialized) { force_minimized = true; i = vid_fulldib_on_focus_mode; msg_suppress_1 = true; // don't want to see normal mode // set message VID_SetMode (windowed_default, vid_curpal); msg_suppress_1 = false; vid_fulldib_on_focus_mode = i; force_minimized = false; // we never seem to get WM_ACTIVATE inactive from this // mode set, so we'll // do it manually t = in_mode_set; in_mode_set = true; AppActivate (false, true); in_mode_set = t; } IN_DeactivateMouse (); IN_ShowMouse (); } else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) { IN_DeactivateMouse (); IN_ShowMouse (); } } } } /* ================ VID_HandlePause ================ */ void VID_HandlePause (qboolean pause) { if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) { if (pause) { IN_DeactivateMouse (); IN_ShowMouse (); } else { IN_ActivateMouse (); IN_HideMouse (); } } } /* =================================================================== MAIN WINDOW =================================================================== */ LONG CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); /* main window procedure */ LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG lRet = 0; int fwKeys, xPos, yPos, fActive, fMinimized, temp; HDC hdc; PAINTSTRUCT ps; static int recursiveflag; switch (uMsg) { case WM_CREATE: break; case WM_SYSCOMMAND: // Check for maximize being hit switch (wParam & ~0x0F) { case SC_MAXIMIZE: // if minimized, bring up as a window before going fullscreen, // so MGL will have the right state to restore if (Minimized) { force_mode_set = true; VID_SetMode (vid_modenum, vid_curpal); force_mode_set = false; } VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); break; case SC_SCREENSAVE: case SC_MONITORPOWER: if (modestate != MS_WINDOWED) { // don't call DefWindowProc() because we don't want to start // the screen saver fullscreen break; } // fall through windowed and allow the screen saver to start default: if (!in_mode_set) { S_BlockSound (); S_ClearBuffer (); } lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); if (!in_mode_set) { S_UnblockSound (); } } break; case WM_MOVE: window_x = (int) LOWORD (lParam); window_y = (int) HIWORD (lParam); VID_UpdateWindowStatus (); if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) VID_RememberWindowPos (); break; case WM_SIZE: Minimized = false; if (!(wParam & SIZE_RESTORED)) { if (wParam & SIZE_MINIMIZED) Minimized = true; } break; case WM_SYSCHAR: // keep Alt-Space from happening break; case WM_ACTIVATE: fActive = LOWORD (wParam); fMinimized = (BOOL) HIWORD (wParam); AppActivate (!(fActive == WA_INACTIVE), fMinimized); // fix the leftover Alt from any Alt-Tab or the like that switched us // // // away ClearAllStates (); if (!in_mode_set) { if (windc) MGL_activatePalette (windc, true); VID_SetPalette (vid_curpal); } break; case WM_PAINT: hdc = BeginPaint (hWnd, &ps); if (!in_mode_set && host_initialized) SCR_UpdateWholeScreen (); EndPaint (hWnd, &ps); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (!in_mode_set) Key_Event (MapKey (lParam), true); break; case WM_KEYUP: case WM_SYSKEYUP: if (!in_mode_set) Key_Event (MapKey (lParam), false); break; // this is complicated because Win32 seems to pack multiple mouse // events into // one update sometimes, so we always check all states and look for // events case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MOUSEMOVE: if (!in_mode_set) { temp = 0; if (wParam & MK_LBUTTON) temp |= 1; if (wParam & MK_RBUTTON) temp |= 2; if (wParam & MK_MBUTTON) temp |= 4; IN_MouseEvent (temp); } break; // JACK: This is the mouse wheel with the Intellimouse // Its delta is either positive or neg, and we generate the proper // Event. case WM_MOUSEWHEEL: if ((short) HIWORD (wParam) > 0) { Key_Event (K_MWHEELUP, true); Key_Event (K_MWHEELUP, false); } else { Key_Event (K_MWHEELDOWN, true); Key_Event (K_MWHEELDOWN, false); } break; // KJB: Added these new palette functions case WM_PALETTECHANGED: if ((HWND) wParam == hWnd) break; /* Fall through to WM_QUERYNEWPALETTE */ case WM_QUERYNEWPALETTE: hdc = GetDC (NULL); if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) vid_palettized = true; else vid_palettized = false; ReleaseDC (NULL, hdc); scr_fullupdate = 0; if (vid_initialized && !in_mode_set && windc && MGL_activatePalette (windc, false) && !Minimized) { VID_SetPalette (vid_curpal); InvalidateRect (mainwindow, NULL, false); // specifically required if WM_QUERYNEWPALETTE realizes a new // palette lRet = TRUE; } break; case WM_DISPLAYCHANGE: if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) { force_mode_set = true; VID_SetMode (vid_modenum, vid_curpal); force_mode_set = false; } break; case WM_CLOSE: // this causes Close in the right-click task bar menu not to work, // but right // now bad things happen if Close is handled in that case (garbage // and a // crash on Win95) if (!in_mode_set) { if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { Sys_Quit (); } } break; case MM_MCINOTIFY: lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); break; default: /* pass all unhandled messages to DefWindowProc */ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); break; } /* return 0 if handled message, 1 if not */ return lRet; } extern void M_Menu_Options_f (void); extern void M_Print (int cx, int cy, char *str); extern void M_PrintWhite (int cx, int cy, char *str); extern void M_DrawCharacter (int cx, int line, int num); extern void M_DrawTransPic (int x, int y, qpic_t *pic); extern void M_DrawPic (int x, int y, qpic_t *pic); static int vid_line, vid_wmodes; typedef struct { int modenum; char *desc; int iscur; int ismode13; int width; } modedesc_t; #define MAX_COLUMN_SIZE 5 #define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) #define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) static modedesc_t modedescs[MAX_MODEDESCS]; /* ================ VID_MenuDraw ================ */ void VID_MenuDraw (void) { qpic_t *p; char *ptr; int lnummodes, i, j, k, column, row, dup, dupmode; char temp[100]; vmode_t *pv; modedesc_t tmodedesc; p = Draw_CachePic ("gfx/vidmodes.lmp"); M_DrawPic ((320 - p->width) / 2, 4, p); for (i = 0; i < 3; i++) { ptr = VID_GetModeDescriptionMemCheck (i); modedescs[i].modenum = modelist[i].modenum; modedescs[i].desc = ptr; modedescs[i].ismode13 = 0; modedescs[i].iscur = 0; if (vid_modenum == i) modedescs[i].iscur = 1; } vid_wmodes = 3; lnummodes = VID_NumModes (); for (i = 3; i < lnummodes; i++) { ptr = VID_GetModeDescriptionMemCheck (i); pv = VID_GetModePtr (i); // we only have room for 15 fullscreen modes, so don't allow // 360-wide modes, because if there are 5 320-wide modes and // 5 360-wide modes, we'll run out of space if (ptr && ((pv->width != 360) || COM_CheckParm ("-allow360"))) { dup = 0; for (j = 3; j < vid_wmodes; j++) { if (!strcmp (modedescs[j].desc, ptr)) { dup = 1; dupmode = j; break; } } if (dup || (vid_wmodes < MAX_MODEDESCS)) { if (!dup || !modedescs[dupmode].ismode13 || COM_CheckParm ("-noforcevga")) { if (dup) { k = dupmode; } else { k = vid_wmodes; } modedescs[k].modenum = i; modedescs[k].desc = ptr; modedescs[k].ismode13 = pv->mode13; modedescs[k].iscur = 0; modedescs[k].width = pv->width; if (i == vid_modenum) modedescs[k].iscur = 1; if (!dup) vid_wmodes++; } } } } // sort the modes on width (to handle picking up oddball dibonly modes // after all the others) for (i = 3; i < (vid_wmodes - 1); i++) { for (j = (i + 1); j < vid_wmodes; j++) { if (modedescs[i].width > modedescs[j].width) { tmodedesc = modedescs[i]; modedescs[i] = modedescs[j]; modedescs[j] = tmodedesc; } } } M_Print (13 * 8, 36, "Windowed Modes"); column = 16; row = 36 + 2 * 8; for (i = 0; i < 3; i++) { if (modedescs[i].iscur) M_PrintWhite (column, row, modedescs[i].desc); else M_Print (column, row, modedescs[i].desc); column += 13 * 8; } if (vid_wmodes > 3) { M_Print (12 * 8, 36 + 4 * 8, "Fullscreen Modes"); column = 16; row = 36 + 6 * 8; for (i = 3; i < vid_wmodes; i++) { if (modedescs[i].iscur) M_PrintWhite (column, row, modedescs[i].desc); else M_Print (column, row, modedescs[i].desc); column += 13 * 8; if (((i - 3) % VID_ROW_SIZE) == (VID_ROW_SIZE - 1)) { column = 16; row += 8; } } } // line cursor if (vid_testingmode) { snprintf (temp, sizeof (temp), "TESTING %s", modedescs[vid_line].desc); M_Print (13 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 4, temp); M_Print (9 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 6, "Please wait 5 seconds..."); } else { M_Print (9 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8, "Press Enter to set mode"); M_Print (6 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 3, "T to test mode for 5 seconds"); ptr = VID_GetModeDescription2 (vid_modenum); if (ptr) { snprintf (temp, sizeof (temp), "D to set default: %s", ptr); M_Print (2 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 5, temp); } ptr = VID_GetModeDescription2 (_vid_default_mode_win->int_val); if (ptr) { snprintf (temp, sizeof (temp), "Current default: %s", ptr); M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 6, temp); } M_Print (15 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 8, "Esc to exit"); row = 36 + 2 * 8 + (vid_line / VID_ROW_SIZE) * 8; column = 8 + (vid_line % VID_ROW_SIZE) * 13 * 8; if (vid_line >= 3) row += 3 * 8; M_DrawCharacter (column, row, 12 + ((int) (realtime * 4) & 1)); } } /* ================ VID_MenuKey ================ */ void VID_MenuKey (int key) { if (vid_testingmode) return; switch (key) { case K_ESCAPE: S_LocalSound ("misc/menu1.wav"); M_Menu_Options_f (); break; case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + ((vid_line + 2) % VID_ROW_SIZE); if (vid_line >= vid_wmodes) vid_line = vid_wmodes - 1; break; case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + ((vid_line + 4) % VID_ROW_SIZE); if (vid_line >= vid_wmodes) vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); vid_line -= VID_ROW_SIZE; if (vid_line < 0) { vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / VID_ROW_SIZE) * VID_ROW_SIZE; while (vid_line >= vid_wmodes) vid_line -= VID_ROW_SIZE; } break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); vid_line += VID_ROW_SIZE; if (vid_line >= vid_wmodes) { vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / VID_ROW_SIZE) * VID_ROW_SIZE; while (vid_line < 0) vid_line += VID_ROW_SIZE; } break; case K_ENTER: S_LocalSound ("misc/menu1.wav"); VID_SetMode (modedescs[vid_line].modenum, vid_curpal); break; case 'T': case 't': S_LocalSound ("misc/menu1.wav"); // have to set this before setting the mode because WM_PAINT // happens during the mode set and does a VID_Update, which // checks vid_testingmode vid_testingmode = 1; vid_testendtime = realtime + 5.0; if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) { vid_testingmode = 0; } break; case 'D': case 'd': S_LocalSound ("misc/menu1.wav"); firstupdate = 0; Cvar_SetValue (_vid_default_mode_win, vid_modenum); break; default: break; } }