2016-12-18 04:43:04 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
|
|
|
|
Quake III Arena source code 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.
|
|
|
|
|
|
|
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
** WIN_GLIMP.C
|
|
|
|
**
|
|
|
|
** This file contains ALL Win32 specific stuff having to do with the
|
2017-11-03 18:35:35 +00:00
|
|
|
** OpenGL refresh. When a port is being made the following functions
|
2016-12-18 04:43:04 +00:00
|
|
|
** must be implemented by the port:
|
|
|
|
**
|
2019-09-25 03:25:59 +00:00
|
|
|
** Sys_V_EndFrame
|
|
|
|
** Sys_V_Init
|
|
|
|
** Sys_V_Shutdown
|
|
|
|
** Sys_V_IsVSynced
|
2016-12-18 04:43:04 +00:00
|
|
|
**
|
|
|
|
** Note that the GLW_xxx functions are Windows specific GL-subsystem
|
|
|
|
** related functions that are relevant ONLY to win_glimp.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if _MSC_VER
|
|
|
|
#pragma warning (disable: 4996) // deprecated ZOMGOVERRUN! nannywhine
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "../renderer/tr_local.h"
|
2019-09-25 03:25:59 +00:00
|
|
|
#include "../client/client.h"
|
2016-12-18 04:43:04 +00:00
|
|
|
#include "resource.h"
|
|
|
|
#include "win_local.h"
|
2019-09-25 03:25:59 +00:00
|
|
|
#include "glw_win.h"
|
2016-12-18 04:43:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
glwstate_t glw_state;
|
|
|
|
|
|
|
|
|
|
|
|
// responsible for creating the Win32 window and initializing the OpenGL driver.
|
|
|
|
|
2017-11-22 17:26:26 +00:00
|
|
|
static qbool GLW_CreateWindow()
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
|
|
|
static qbool s_classRegistered = qfalse;
|
|
|
|
|
|
|
|
if ( !s_classRegistered )
|
|
|
|
{
|
|
|
|
WNDCLASS wc;
|
|
|
|
memset( &wc, 0, sizeof( wc ) );
|
|
|
|
|
|
|
|
wc.style = CS_OWNDC;
|
|
|
|
wc.lpfnWndProc = MainWndProc;
|
|
|
|
wc.cbClsExtra = 0;
|
|
|
|
wc.cbWndExtra = 0;
|
|
|
|
wc.hInstance = g_wv.hInstance;
|
|
|
|
wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
|
|
|
|
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
|
2017-03-05 21:48:02 +00:00
|
|
|
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
2016-12-18 04:43:04 +00:00
|
|
|
wc.lpszMenuName = 0;
|
|
|
|
wc.lpszClassName = CLIENT_WINDOW_TITLE;
|
|
|
|
|
|
|
|
if ( !RegisterClass( &wc ) )
|
|
|
|
ri.Error( ERR_FATAL, "GLW_CreateWindow: could not register window class" );
|
|
|
|
|
|
|
|
s_classRegistered = qtrue;
|
|
|
|
ri.Printf( PRINT_DEVELOPER, "...registered window class\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// create the HWND if one does not already exist
|
|
|
|
//
|
2022-12-28 19:49:18 +00:00
|
|
|
const qbool createWindow = !g_wv.hWnd;
|
|
|
|
if (createWindow)
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
2017-09-12 01:34:20 +00:00
|
|
|
g_wv.inputInitialized = qfalse;
|
2022-12-28 19:49:18 +00:00
|
|
|
}
|
2017-09-12 01:34:20 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
RECT desiredRect;
|
|
|
|
desiredRect.left = 0;
|
|
|
|
desiredRect.top = 0;
|
|
|
|
desiredRect.right = glInfo.winWidth;
|
|
|
|
desiredRect.bottom = glInfo.winHeight;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
int style = WS_VISIBLE | WS_CLIPCHILDREN;
|
|
|
|
int exstyle = 0;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
if (glInfo.winFullscreen) {
|
|
|
|
style |= WS_POPUP;
|
|
|
|
exstyle |= WS_EX_TOPMOST;
|
|
|
|
} else {
|
|
|
|
style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
|
|
AdjustWindowRect(&desiredRect, style, FALSE);
|
|
|
|
}
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
const int w = desiredRect.right - desiredRect.left;
|
|
|
|
const int h = desiredRect.bottom - desiredRect.top;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
const RECT& monRect = g_wv.monitorRects[g_wv.monitor];
|
2017-03-05 21:48:02 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
int dx = 0;
|
|
|
|
int dy = 0;
|
2017-03-05 21:48:02 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
if (!glInfo.winFullscreen) {
|
|
|
|
dx = ri.Cvar_Get("vid_xpos", "0", 0)->integer;
|
|
|
|
dy = ri.Cvar_Get("vid_ypos", "0", 0)->integer;
|
|
|
|
dx = Com_ClampInt(0, max(0, monRect.right - monRect.left - w), dx);
|
|
|
|
dy = Com_ClampInt(0, max(0, monRect.bottom - monRect.top - h), dy);
|
|
|
|
}
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
const int x = monRect.left + dx;
|
|
|
|
const int y = monRect.top + dy;
|
2017-03-05 21:48:02 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
if (createWindow)
|
|
|
|
{
|
2019-02-20 19:52:34 +00:00
|
|
|
g_wv.duringCreateWindow = qtrue;
|
2022-12-28 19:49:18 +00:00
|
|
|
g_wv.hWnd = CreateWindowEx(exstyle, CLIENT_WINDOW_TITLE, " " CLIENT_WINDOW_TITLE, style,
|
|
|
|
x, y, w, h, NULL, NULL, g_wv.hInstance, NULL);
|
2019-02-20 19:52:34 +00:00
|
|
|
g_wv.duringCreateWindow = qfalse;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
if (!g_wv.hWnd)
|
|
|
|
ri.Error(ERR_FATAL, "GLW_CreateWindow() - Couldn't create window");
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
ShowWindow(g_wv.hWnd, SW_SHOW);
|
|
|
|
UpdateWindow(g_wv.hWnd);
|
|
|
|
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "...created window@%d,%d (%dx%d)\n", x, y, w, h);
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-28 19:49:18 +00:00
|
|
|
const LONG_PTR oldStyle = GetWindowLongPtrA(g_wv.hWnd, GWL_STYLE);
|
|
|
|
const qbool fullScreen = (oldStyle & WS_POPUP) != 0;
|
|
|
|
RECT currentRect;
|
|
|
|
GetClientRect(g_wv.hWnd, ¤tRect);
|
|
|
|
if(currentRect.right - currentRect.left != glInfo.winWidth ||
|
|
|
|
currentRect.bottom - currentRect.top != glInfo.winHeight ||
|
|
|
|
fullScreen != glInfo.winFullscreen)
|
|
|
|
{
|
|
|
|
SetWindowLongPtrA(g_wv.hWnd, GWL_STYLE, style);
|
|
|
|
SetWindowLongPtrA(g_wv.hWnd, GWL_EXSTYLE, exstyle);
|
|
|
|
MoveWindow(g_wv.hWnd, x, y, w, h, TRUE);
|
|
|
|
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "...window already present, window was adjusted\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "...window already present, no change was needed\n");
|
|
|
|
}
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SetForegroundWindow( g_wv.hWnd );
|
|
|
|
SetFocus( g_wv.hWnd );
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
2016-12-30 04:13:59 +00:00
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
static const char* GLW_GetCurrentDisplayDeviceName()
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
2017-06-01 18:48:21 +00:00
|
|
|
static char deviceName[CCHDEVICENAME + 1];
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
const HMONITOR hMonitor = g_wv.hMonitors[g_wv.monitor];
|
|
|
|
if ( hMonitor == NULL )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
MONITORINFOEXA info;
|
|
|
|
ZeroMemory( &info, sizeof(info) );
|
|
|
|
info.cbSize = sizeof(info);
|
|
|
|
if ( GetMonitorInfoA(hMonitor, &info) == 0 )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Q_strncpyz( deviceName, info.szDevice, sizeof(deviceName) );
|
|
|
|
|
|
|
|
return deviceName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GLW_UpdateMonitorRect( const char* deviceName )
|
|
|
|
{
|
|
|
|
if ( deviceName == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
DEVMODEA dm;
|
|
|
|
ZeroMemory( &dm, sizeof(dm) );
|
|
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
if ( EnumDisplaySettingsExA(deviceName, ENUM_CURRENT_SETTINGS, &dm, 0) == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( dm.dmPelsWidth == 0 || dm.dmPelsHeight == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Normally, we should check dm.dmFields for the following flags:
|
|
|
|
// DM_POSITION DM_PELSWIDTH DM_PELSHEIGHT
|
|
|
|
// EnumDisplaySettingsExA doesn't always set up the flags properly.
|
|
|
|
|
|
|
|
RECT& rect = g_wv.monitorRects[g_wv.monitor];
|
|
|
|
rect.left = dm.dmPosition.x;
|
|
|
|
rect.top = dm.dmPosition.y;
|
|
|
|
rect.right = dm.dmPosition.x + dm.dmPelsWidth;
|
|
|
|
rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static qbool GLW_SetDisplaySettings( DEVMODE& dm )
|
|
|
|
{
|
|
|
|
const char* deviceName = GLW_GetCurrentDisplayDeviceName();
|
|
|
|
const int ec = ChangeDisplaySettingsExA( deviceName, &dm, NULL, CDS_FULLSCREEN, NULL );
|
|
|
|
if ( ec == DISP_CHANGE_SUCCESSFUL )
|
|
|
|
{
|
|
|
|
glw_state.cdsDevMode = dm;
|
|
|
|
glw_state.cdsDevModeValid = qtrue;
|
|
|
|
GLW_UpdateMonitorRect( deviceName );
|
2016-12-18 04:43:04 +00:00
|
|
|
return qtrue;
|
2017-06-01 18:48:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
glw_state.cdsDevModeValid = qfalse;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
ri.Printf( PRINT_ALL, "...CDS: %ix%i (C%i) failed: ", (int)dm.dmPelsWidth, (int)dm.dmPelsHeight, (int)dm.dmBitsPerPel );
|
2016-12-18 04:43:04 +00:00
|
|
|
|
|
|
|
#define CDS_ERROR(x) case x: ri.Printf( PRINT_ALL, #x##"\n" ); break;
|
2017-06-01 18:48:21 +00:00
|
|
|
switch (ec) {
|
2016-12-18 04:43:04 +00:00
|
|
|
default:
|
2017-06-01 18:48:21 +00:00
|
|
|
ri.Printf( PRINT_ALL, "unknown error %d\n", ec );
|
2016-12-18 04:43:04 +00:00
|
|
|
break;
|
|
|
|
CDS_ERROR( DISP_CHANGE_RESTART );
|
|
|
|
CDS_ERROR( DISP_CHANGE_BADPARAM );
|
|
|
|
CDS_ERROR( DISP_CHANGE_BADFLAGS );
|
|
|
|
CDS_ERROR( DISP_CHANGE_FAILED );
|
|
|
|
CDS_ERROR( DISP_CHANGE_BADMODE );
|
|
|
|
CDS_ERROR( DISP_CHANGE_NOTUPDATED );
|
|
|
|
}
|
|
|
|
#undef CDS_ERROR
|
|
|
|
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
static void GLW_ResetDisplaySettings( qbool invalidate )
|
|
|
|
{
|
|
|
|
const char* deviceName = GLW_GetCurrentDisplayDeviceName();
|
|
|
|
ChangeDisplaySettingsEx( deviceName, NULL, NULL, 0, NULL );
|
|
|
|
GLW_UpdateMonitorRect( deviceName );
|
|
|
|
if ( invalidate )
|
|
|
|
glw_state.cdsDevModeValid = qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WIN_SetGameDisplaySettings()
|
|
|
|
{
|
|
|
|
if ( glw_state.cdsDevModeValid )
|
|
|
|
GLW_SetDisplaySettings( glw_state.cdsDevMode );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WIN_SetDesktopDisplaySettings()
|
|
|
|
{
|
|
|
|
// We don't invalidate glw_state.cdsDevModeValid so we can
|
|
|
|
// return to the previous mode later.
|
|
|
|
GLW_ResetDisplaySettings( qfalse );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-22 17:26:26 +00:00
|
|
|
static qbool GLW_SetMode()
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
2019-02-20 19:52:34 +00:00
|
|
|
WIN_InitMonitorList();
|
2017-05-17 05:41:25 +00:00
|
|
|
WIN_UpdateMonitorIndexFromCvar();
|
2017-11-22 17:26:26 +00:00
|
|
|
|
|
|
|
const RECT& monRect = g_wv.monitorRects[g_wv.monitor];
|
|
|
|
const int desktopWidth = (int)(monRect.right - monRect.left);
|
|
|
|
const int desktopHeight = (int)(monRect.bottom - monRect.top);
|
|
|
|
R_ConfigureVideoMode( desktopWidth, desktopHeight );
|
2016-12-18 04:43:04 +00:00
|
|
|
|
|
|
|
DEVMODE dm;
|
2017-06-01 18:48:21 +00:00
|
|
|
ZeroMemory( &dm, sizeof( dm ) );
|
2016-12-18 04:43:04 +00:00
|
|
|
dm.dmSize = sizeof( dm );
|
|
|
|
|
2017-11-22 17:26:26 +00:00
|
|
|
if (glInfo.vidFullscreen != glw_state.cdsDevModeValid) {
|
|
|
|
if (glInfo.vidFullscreen) {
|
2016-12-18 04:43:04 +00:00
|
|
|
dm.dmPelsWidth = glConfig.vidWidth;
|
|
|
|
dm.dmPelsHeight = glConfig.vidHeight;
|
|
|
|
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
|
|
|
|
if ( r_displayRefresh->integer ) {
|
|
|
|
dm.dmDisplayFrequency = r_displayRefresh->integer;
|
|
|
|
dm.dmFields |= DM_DISPLAYFREQUENCY;
|
|
|
|
}
|
|
|
|
|
2017-06-22 06:20:56 +00:00
|
|
|
dm.dmBitsPerPel = 32;
|
|
|
|
dm.dmFields |= DM_BITSPERPEL;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
dm.dmPosition.x = monRect.left;
|
|
|
|
dm.dmPosition.y = monRect.top;
|
|
|
|
dm.dmFields |= DM_POSITION;
|
2016-12-18 04:43:04 +00:00
|
|
|
|
2017-11-22 17:26:26 +00:00
|
|
|
glInfo.vidFullscreen = GLW_SetDisplaySettings( dm );
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-01 18:48:21 +00:00
|
|
|
GLW_ResetDisplaySettings( qtrue );
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-22 17:26:26 +00:00
|
|
|
if (!GLW_CreateWindow())
|
2016-12-18 04:43:04 +00:00
|
|
|
return qfalse;
|
|
|
|
|
2017-06-01 18:48:21 +00:00
|
|
|
if (EnumDisplaySettingsA( GLW_GetCurrentDisplayDeviceName(), ENUM_CURRENT_SETTINGS, &dm ))
|
2016-12-18 04:43:04 +00:00
|
|
|
glInfo.displayFrequency = dm.dmDisplayFrequency;
|
|
|
|
|
2016-12-30 04:13:59 +00:00
|
|
|
return qtrue;
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-25 03:25:59 +00:00
|
|
|
void Sys_V_EndFrame()
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-28 19:49:18 +00:00
|
|
|
void Sys_V_Init()
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
2022-12-28 19:49:18 +00:00
|
|
|
if ( !GLW_SetMode() )
|
|
|
|
ri.Error( ERR_FATAL, "Sys_V_Init - could not load video subsystem\n" );
|
2019-09-25 03:25:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Sys_V_Shutdown()
|
|
|
|
{
|
|
|
|
Sys_ShutdownInput();
|
|
|
|
|
2016-12-18 04:43:04 +00:00
|
|
|
// destroy window
|
|
|
|
if ( g_wv.hWnd )
|
|
|
|
{
|
|
|
|
ri.Printf( PRINT_DEVELOPER, "...destroying window\n" );
|
|
|
|
ShowWindow( g_wv.hWnd, SW_HIDE );
|
|
|
|
DestroyWindow( g_wv.hWnd );
|
|
|
|
g_wv.hWnd = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset display settings
|
2017-06-01 18:48:21 +00:00
|
|
|
if ( glw_state.cdsDevModeValid )
|
2016-12-18 04:43:04 +00:00
|
|
|
{
|
|
|
|
ri.Printf( PRINT_DEVELOPER, "...resetting display\n" );
|
2017-06-01 18:48:21 +00:00
|
|
|
GLW_ResetDisplaySettings( qtrue );
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
2019-09-25 03:25:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qbool Sys_V_IsVSynced()
|
|
|
|
{
|
2022-12-28 19:49:18 +00:00
|
|
|
// with Direct3D 12, our swap interval is (normally) respected
|
|
|
|
return r_swapInterval->integer != 0;
|
2016-12-18 04:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-27 04:43:28 +00:00
|
|
|
qbool Sys_IsMinimized()
|
|
|
|
{
|
|
|
|
return ( g_wv.hWnd != NULL ) && !!IsIconic( g_wv.hWnd );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-07 22:32:50 +00:00
|
|
|
void WIN_UpdateResolution( int width, int height )
|
|
|
|
{
|
2017-11-22 17:26:26 +00:00
|
|
|
glInfo.winWidth = width;
|
|
|
|
glInfo.winHeight = height;
|
2017-12-06 23:36:44 +00:00
|
|
|
if ( r_fullscreen->integer == 0 )
|
|
|
|
{
|
|
|
|
glConfig.vidWidth = width;
|
|
|
|
glConfig.vidHeight = height;
|
|
|
|
}
|
2017-03-07 22:32:50 +00:00
|
|
|
}
|
2017-06-01 18:48:21 +00:00
|
|
|
|