improved multi-monitor support and added r_monitor

only apply hw gamma ramps where and when needed
fixed the start-up console window staying visible when starting in full-screen
black window background to avoid the white flashes when hw gamma is enabled
This commit is contained in:
myT 2017-03-05 22:48:02 +01:00
parent 569247745b
commit e0d55dc63c
9 changed files with 255 additions and 75 deletions

View file

@ -2252,7 +2252,6 @@ void Com_Init( char *commandLine )
#ifndef DEDICATED
CL_Init();
#endif
Sys_ShowConsole( com_viewlog->integer, qfalse );
}
// set com_frameTime so that if a map is started on the
@ -2279,6 +2278,10 @@ void Com_Init( char *commandLine )
CL_StartHunkUsers();
#endif
// moved to fix the console window staying visible when starting the game in full-screen mode
if ( !com_dedicated->integer )
Sys_ShowConsole( com_viewlog->integer, qfalse );
// make sure single player is off by default
Cvar_Set( "sv_singlePlayer", "0" );

View file

@ -46,6 +46,18 @@ float Com_Clamp( float min, float max, float value )
}
int Com_ClampInt( int min, int max, int value )
{
if ( value < min ) {
return min;
}
if ( value > max ) {
return max;
}
return value;
}
const char* COM_SkipPath( const char* pathname )
{
const char* last = pathname;

View file

@ -508,6 +508,7 @@ void QDECL Com_Printf( const char* msg, ... );
void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
float Com_Clamp( float min, float max, float value );
int Com_ClampInt( int min, int max, int value );
const char* COM_SkipPath( const char* pathname );
void COM_StripExtension( const char* in, char* out, int destsize );

View file

@ -32,17 +32,16 @@ extern "C" {
typedef struct
{
HDC hDC; // handle to device context
HGLRC hGLRC; // handle to GL rendering context
HINSTANCE hinstOpenGL; // HINSTANCE for the OpenGL library
int desktopWidth, desktopHeight, desktopBPP;
qbool cdsFullscreen;
qbool pixelFormatSet;
int nPendingPF;
qbool gammaRampSet; // qtrue if our custom ramp is active
// The main window's rendering context is the only one we can ever keep around
// because the window class has the CS_OWNDC style set (for OpenGL).
HDC hDC;
HGLRC hGLRC;
HINSTANCE hinstOpenGL;
int desktopWidth, desktopHeight, desktopBPP;
qbool cdsFullscreen;
qbool pixelFormatSet;
int nPendingPF;
qbool gammaRampSet; // qtrue if our custom ramp is active
} glwstate_t;
extern glwstate_t glw_state;

View file

@ -464,7 +464,7 @@ static qbool GLW_CreateWindow( int width, int height, int colorbits )
wc.hInstance = g_wv.hInstance;
wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = CLIENT_WINDOW_TITLE;
@ -475,55 +475,55 @@ static qbool GLW_CreateWindow( int width, int height, int colorbits )
ri.Printf( PRINT_DEVELOPER, "...registered window class\n" );
}
RECT r;
int x, y, w, h;
//
// create the HWND if one does not already exist
//
if ( !g_wv.hWnd )
{
//
// compute width and height
//
RECT r;
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
int style = WS_VISIBLE | WS_SYSMENU;
int style = WS_VISIBLE | WS_CLIPCHILDREN;
int exstyle;
if ( glInfo.isFullscreen )
{
style |= WS_POPUP;
// WS_BORDER fixes the single black flash that happens when reloading a map
// right after switching from windowed mode to full-screen
style |= WS_POPUP | WS_BORDER;
exstyle = WS_EX_TOPMOST;
}
else
{
style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION;
style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU;
exstyle = 0;
AdjustWindowRect( &r, style, FALSE );
}
w = r.right - r.left;
h = r.bottom - r.top;
const int w = r.right - r.left;
const int h = r.bottom - r.top;
if ( glInfo.isFullscreen )
WIN_GetStartUpMonitorIndex();
const RECT monRect = g_wv.monitorRects[g_wv.monitor];
int dx = 0;
int dy = 0;
if ( !glInfo.isFullscreen )
{
x = 0;
y = 0;
}
else
{
const cvar_t* vid_xpos = ri.Cvar_Get( "vid_xpos", "", 0 );
const cvar_t* vid_ypos = ri.Cvar_Get( "vid_ypos", "", 0 );
x = vid_xpos->integer;
y = vid_ypos->integer;
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 );
}
g_wv.hWnd = CreateWindowEx( exstyle, CLIENT_WINDOW_TITLE, CLIENT_WINDOW_TITLE, style,
const int x = monRect.left + dx;
const int y = monRect.top + dy;
g_wv.hWnd = CreateWindowEx( exstyle, CLIENT_WINDOW_TITLE, " "CLIENT_WINDOW_TITLE, style,
x, y, w, h, NULL, NULL, g_wv.hInstance, NULL );
if ( !g_wv.hWnd )
@ -842,15 +842,66 @@ static void GLW_CheckHardwareGamma()
}
static void GLW_UpdateGammaMonitorInfo()
{
const int screenCount = GetSystemMetrics( SM_CMONITORS );
if ( screenCount <= 1 )
{
g_wv.hGammaMonitor = NULL;
g_wv.gammaMonitorName[0] = '\0';
return;
}
RECT rect;
GetWindowRect( g_wv.hWnd, &rect );
const HMONITOR hMonitor = MonitorFromRect( &rect, MONITOR_DEFAULTTONEAREST );
MONITORINFOEX info;
ZeroMemory( &info, sizeof( info ) );
info.cbSize = sizeof( MONITORINFOEX );
if ( GetMonitorInfo( hMonitor, &info ) )
{
g_wv.hGammaMonitor = hMonitor;
Q_strncpyz( g_wv.gammaMonitorName, info.szDevice, sizeof( g_wv.gammaMonitorName ) );
}
}
static void GLW_RestoreGammaRamp( HDC hDC )
{
if ( SetDeviceGammaRamp( hDC, s_oldHardwareGamma ) )
glw_state.gammaRampSet = qfalse;
}
void GLW_RestoreGamma()
{
if (!glConfig.deviceSupportsGamma || !glw_state.gammaRampSet)
return;
HDC hDC = GetDC( GetDesktopWindow() );
if ( SetDeviceGammaRamp( hDC, s_oldHardwareGamma ) )
glw_state.gammaRampSet = qfalse;
ReleaseDC( GetDesktopWindow(), hDC );
if ( g_wv.hGammaMonitor )
{
const HDC hDC = CreateDC( "DISPLAY", g_wv.gammaMonitorName, NULL, NULL );
GLW_RestoreGammaRamp( hDC );
DeleteDC( hDC );
}
else
{
GLW_RestoreGammaRamp( glw_state.hDC );
}
}
static void GLW_SetGammaRamp( HDC hDC, LPVOID lpRamp )
{
if ( SetDeviceGammaRamp( hDC, lpRamp ) )
{
glw_state.gammaRampSet = qtrue;
}
else
{
Com_Printf( "SetDeviceGammaRamp failed.\n" );
}
}
@ -879,10 +930,16 @@ void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned
}
}
if ( SetDeviceGammaRamp( glw_state.hDC, table ) ) {
glw_state.gammaRampSet = qtrue;
} else {
Com_Printf( "SetDeviceGammaRamp failed.\n" );
GLW_UpdateGammaMonitorInfo();
if ( g_wv.hGammaMonitor )
{
const HDC hDC = CreateDC( "DISPLAY", g_wv.gammaMonitorName, NULL, NULL );
GLW_SetGammaRamp( hDC, table );
DeleteDC( hDC );
}
else
{
GLW_SetGammaRamp( glw_state.hDC, table );
}
}
@ -1088,3 +1145,14 @@ void GLimp_WakeRenderer( void *data ) {
WaitForSingleObject( renderActiveEvent, INFINITE );
}
void WIN_UpdateHardwareGammaRamp( qbool enable )
{
if ( !glConfig.deviceSupportsGamma )
return;
if ( enable )
R_SetColorMappings();
else
GLW_RestoreGamma();
}

View file

@ -38,12 +38,17 @@ qbool IN_ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); // returns tr
void IN_Frame();
void IN_Shutdown();
void WIN_GetStartUpMonitorIndex();
void WIN_GetMonitorIndexFromMainWindow();
void WIN_UpdateHardwareGammaRamp( qbool enable );
void SNDDMA_Activate();
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void GLW_RestoreGamma();
#define MAX_MONITOR_COUNT 16
typedef struct
{
@ -54,7 +59,16 @@ typedef struct
// when we get a windows message, we store the time off
// so keyboard processing can know the exact time of an event
unsigned sysMsgTime;
unsigned sysMsgTime;
HMONITOR hGammaMonitor;
char gammaMonitorName[CCHDEVICENAME];
RECT monitorRects[MAX_MONITOR_COUNT];
HMONITOR hMonitors[MAX_MONITOR_COUNT];
int monitor; // index of the monitor currently used for display
int primaryMonitor;
int monitorCount;
} WinVars_t;
extern WinVars_t g_wv;

View file

@ -558,8 +558,82 @@ void Sys_Init()
///////////////////////////////////////////////////////////////
static BOOL CALLBACK WIN_MonitorEnumCallback( HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData )
{
if ( lprcMonitor )
{
g_wv.monitorRects[g_wv.monitorCount] = *lprcMonitor;
g_wv.hMonitors[g_wv.monitorCount] = hMonitor;
g_wv.monitorCount++;
}
static int WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
if ( g_wv.monitorCount >= MAX_MONITOR_COUNT )
{
return FALSE;
}
return TRUE;
}
static void WIN_GetMonitorList()
{
EnumDisplayMonitors( NULL, NULL, &WIN_MonitorEnumCallback, 0 );
const POINT zero = { 0, 0 };
const HMONITOR hMonitor = MonitorFromPoint( zero, MONITOR_DEFAULTTOPRIMARY );
for ( int i = 0; i < g_wv.monitorCount; i++ )
{
if ( hMonitor == g_wv.hMonitors[i] )
{
g_wv.primaryMonitor = i;
g_wv.monitor = i;
break;
}
}
}
void WIN_GetStartUpMonitorIndex()
{
static qbool called = qfalse;
if ( called )
return;
called = qtrue;
// r_monitor is the 1-based monitor index the user asks for
const int monitor = Cvar_Get( "r_monitor", "0", CVAR_ARCHIVE )->integer;
if ( monitor <= 0 || monitor > g_wv.monitorCount )
{
g_wv.monitor = g_wv.primaryMonitor;
return;
}
g_wv.monitor = monitor - 1;
}
void WIN_GetMonitorIndexFromMainWindow()
{
const HMONITOR hMonitor = MonitorFromWindow( g_wv.hWnd, MONITOR_DEFAULTTONEAREST );
for ( int i = 0; i < g_wv.monitorCount; i++ )
{
if ( hMonitor == g_wv.hMonitors[i] )
{
g_wv.monitor = i;
break;
}
}
}
///////////////////////////////////////////////////////////////
int WINAPI WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// should never get a previous instance in Win32
if ( hPrevInstance )
@ -567,6 +641,8 @@ static int WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCm
g_wv.hInstance = hInstance;
WIN_GetMonitorList();
// done before Com/Sys_Init since we need this for error output
Sys_CreateConsole();

View file

@ -294,14 +294,12 @@ LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
*/
void Sys_CreateConsole( void )
{
HDC hDC;
WNDCLASS wc;
RECT rect;
const int desiredw = 540;
const int desiredh = 450;
const char *DEDCLASS = "Q3 WinConsole";
int nHeight;
int swidth, sheight;
int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
const DWORD DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
WNDCLASS wc;
memset( &wc, 0, sizeof( wc ) );
wc.style = 0;
@ -318,25 +316,30 @@ void Sys_CreateConsole( void )
if ( !RegisterClass (&wc) )
return;
RECT rect;
rect.left = 0;
rect.right = 540;
rect.right = desiredw;
rect.top = 0;
rect.bottom = 450;
rect.bottom = desiredh;
AdjustWindowRect( &rect, DEDSTYLE, FALSE );
hDC = GetDC( GetDesktopWindow() );
swidth = GetDeviceCaps( hDC, HORZRES );
sheight = GetDeviceCaps( hDC, VERTRES );
ReleaseDC( GetDesktopWindow(), hDC );
const RECT monRect = g_wv.monitorRects[g_wv.monitor];
const int swidth = monRect.right - monRect.left;
const int sheight = monRect.bottom - monRect.top;
const int x = monRect.left + (swidth - desiredw) / 2;
const int y = monRect.top + (sheight - desiredh) / 2;
const int w = rect.right - rect.left + 1;
const int h = rect.bottom - rect.top + 1;
s_wcd.windowWidth = rect.right - rect.left + 1;
s_wcd.windowHeight = rect.bottom - rect.top + 1;
s_wcd.windowWidth = w;
s_wcd.windowHeight = h;
s_wcd.hWnd = CreateWindowEx( 0,
DEDCLASS,
CONSOLE_WINDOW_TITLE,
" "CONSOLE_WINDOW_TITLE,
DEDSTYLE,
( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1,
x, y,
w, h,
NULL,
NULL,
g_wv.hInstance,
@ -350,8 +353,8 @@ void Sys_CreateConsole( void )
//
// create fonts
//
hDC = GetDC( s_wcd.hWnd );
nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY), 72);
const HDC hDC = GetDC( s_wcd.hWnd );
const int nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
s_wcd.hfBufferFont = CreateFont( nHeight,
0,

View file

@ -86,6 +86,8 @@ static void VID_AppActivate( BOOL fActive, BOOL minimize )
// we don't want to act like we're active if we're minimized
g_wv.activeApp = (fActive && !g_wv.isMinimized);
WIN_UpdateHardwareGammaRamp( g_wv.activeApp );
// minimize/restore mouse-capture on demand
IN_Activate( g_wv.activeApp );
}
@ -272,25 +274,27 @@ LRESULT CALLBACK MainWndProc (
{
if (!r_fullscreen->integer )
{
int xPos = (short)LOWORD(lParam); // horizontal position
int yPos = (short)HIWORD(lParam); // vertical position
WIN_GetMonitorIndexFromMainWindow();
RECT r;
r.left = 0;
r.top = 0;
r.right = 1;
r.bottom = 1;
AdjustWindowRect( &r, GetWindowLong( hWnd, GWL_STYLE ), FALSE );
Cvar_SetValue( "vid_xpos", xPos + r.left );
Cvar_SetValue( "vid_ypos", yPos + r.top );
const RECT monRect = g_wv.monitorRects[g_wv.monitor];
const int x = LOWORD( lParam );
const int y = HIWORD( lParam );
Cvar_SetValue( "vid_xpos", x + r.left - monRect.left );
Cvar_SetValue( "vid_ypos", y + r.top - monRect.top );
vid_xpos->modified = qfalse;
vid_ypos->modified = qfalse;
if ( g_wv.activeApp )
{
IN_Activate (qtrue);
}
// @TODO: fix this broken mess
if ( g_wv.activeApp )
{
IN_Activate (qtrue);
}
}
}
break;