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 #ifndef DEDICATED
CL_Init(); CL_Init();
#endif #endif
Sys_ShowConsole( com_viewlog->integer, qfalse );
} }
// set com_frameTime so that if a map is started on the // set com_frameTime so that if a map is started on the
@ -2279,6 +2278,10 @@ void Com_Init( char *commandLine )
CL_StartHunkUsers(); CL_StartHunkUsers();
#endif #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 // make sure single player is off by default
Cvar_Set( "sv_singlePlayer", "0" ); 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* COM_SkipPath( const char* pathname )
{ {
const char* last = 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, ...); void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
float Com_Clamp( float min, float max, float value ); 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 ); const char* COM_SkipPath( const char* pathname );
void COM_StripExtension( const char* in, char* out, int destsize ); void COM_StripExtension( const char* in, char* out, int destsize );

View file

@ -32,17 +32,16 @@ extern "C" {
typedef struct typedef struct
{ {
HDC hDC; // handle to device context // The main window's rendering context is the only one we can ever keep around
HGLRC hGLRC; // handle to GL rendering context // because the window class has the CS_OWNDC style set (for OpenGL).
HINSTANCE hinstOpenGL; // HINSTANCE for the OpenGL library HDC hDC;
HGLRC hGLRC;
int desktopWidth, desktopHeight, desktopBPP; HINSTANCE hinstOpenGL;
int desktopWidth, desktopHeight, desktopBPP;
qbool cdsFullscreen; qbool cdsFullscreen;
qbool pixelFormatSet; qbool pixelFormatSet;
int nPendingPF; int nPendingPF;
qbool gammaRampSet; // qtrue if our custom ramp is active
qbool gammaRampSet; // qtrue if our custom ramp is active
} glwstate_t; } glwstate_t;
extern glwstate_t glw_state; 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.hInstance = g_wv.hInstance;
wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1)); wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor = LoadCursor (NULL,IDC_ARROW); wc.hCursor = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_GRAYTEXT; wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = 0; wc.lpszMenuName = 0;
wc.lpszClassName = CLIENT_WINDOW_TITLE; 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" ); ri.Printf( PRINT_DEVELOPER, "...registered window class\n" );
} }
RECT r;
int x, y, w, h;
// //
// create the HWND if one does not already exist // create the HWND if one does not already exist
// //
if ( !g_wv.hWnd ) if ( !g_wv.hWnd )
{ {
// RECT r;
// compute width and height
//
r.left = 0; r.left = 0;
r.top = 0; r.top = 0;
r.right = width; r.right = width;
r.bottom = height; r.bottom = height;
int style = WS_VISIBLE | WS_SYSMENU; int style = WS_VISIBLE | WS_CLIPCHILDREN;
int exstyle; int exstyle;
if ( glInfo.isFullscreen ) 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; exstyle = WS_EX_TOPMOST;
} }
else else
{ {
style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION; style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU;
exstyle = 0; exstyle = 0;
AdjustWindowRect( &r, style, FALSE ); AdjustWindowRect( &r, style, FALSE );
} }
w = r.right - r.left; const int w = r.right - r.left;
h = r.bottom - r.top; 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; dx = ri.Cvar_Get( "vid_xpos", "0", 0 )->integer;
y = 0; dy = ri.Cvar_Get( "vid_ypos", "0", 0 )->integer;
} dx = Com_ClampInt( 0, max( 0, monRect.right - monRect.left - w ), dx );
else dy = Com_ClampInt( 0, max( 0, monRect.bottom - monRect.top - h ), dy );
{
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;
} }
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 ); x, y, w, h, NULL, NULL, g_wv.hInstance, NULL );
if ( !g_wv.hWnd ) 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() void GLW_RestoreGamma()
{ {
if (!glConfig.deviceSupportsGamma || !glw_state.gammaRampSet) if (!glConfig.deviceSupportsGamma || !glw_state.gammaRampSet)
return; return;
HDC hDC = GetDC( GetDesktopWindow() ); if ( g_wv.hGammaMonitor )
if ( SetDeviceGammaRamp( hDC, s_oldHardwareGamma ) ) {
glw_state.gammaRampSet = qfalse; const HDC hDC = CreateDC( "DISPLAY", g_wv.gammaMonitorName, NULL, NULL );
ReleaseDC( GetDesktopWindow(), hDC ); 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_UpdateGammaMonitorInfo();
glw_state.gammaRampSet = qtrue; if ( g_wv.hGammaMonitor )
} else { {
Com_Printf( "SetDeviceGammaRamp failed.\n" ); 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 ); 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_Frame();
void IN_Shutdown(); void IN_Shutdown();
void WIN_GetStartUpMonitorIndex();
void WIN_GetMonitorIndexFromMainWindow();
void WIN_UpdateHardwareGammaRamp( qbool enable );
void SNDDMA_Activate(); void SNDDMA_Activate();
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void GLW_RestoreGamma(); void GLW_RestoreGamma();
#define MAX_MONITOR_COUNT 16
typedef struct typedef struct
{ {
@ -54,7 +59,16 @@ typedef struct
// when we get a windows message, we store the time off // when we get a windows message, we store the time off
// so keyboard processing can know the exact time of an event // 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; } WinVars_t;
extern WinVars_t g_wv; 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 // should never get a previous instance in Win32
if ( hPrevInstance ) if ( hPrevInstance )
@ -567,6 +641,8 @@ static int WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCm
g_wv.hInstance = hInstance; g_wv.hInstance = hInstance;
WIN_GetMonitorList();
// done before Com/Sys_Init since we need this for error output // done before Com/Sys_Init since we need this for error output
Sys_CreateConsole(); Sys_CreateConsole();

View file

@ -294,14 +294,12 @@ LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
*/ */
void Sys_CreateConsole( void ) void Sys_CreateConsole( void )
{ {
HDC hDC; const int desiredw = 540;
WNDCLASS wc; const int desiredh = 450;
RECT rect;
const char *DEDCLASS = "Q3 WinConsole"; const char *DEDCLASS = "Q3 WinConsole";
int nHeight; const DWORD DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
int swidth, sheight;
int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
WNDCLASS wc;
memset( &wc, 0, sizeof( wc ) ); memset( &wc, 0, sizeof( wc ) );
wc.style = 0; wc.style = 0;
@ -318,25 +316,30 @@ void Sys_CreateConsole( void )
if ( !RegisterClass (&wc) ) if ( !RegisterClass (&wc) )
return; return;
RECT rect;
rect.left = 0; rect.left = 0;
rect.right = 540; rect.right = desiredw;
rect.top = 0; rect.top = 0;
rect.bottom = 450; rect.bottom = desiredh;
AdjustWindowRect( &rect, DEDSTYLE, FALSE ); AdjustWindowRect( &rect, DEDSTYLE, FALSE );
hDC = GetDC( GetDesktopWindow() ); const RECT monRect = g_wv.monitorRects[g_wv.monitor];
swidth = GetDeviceCaps( hDC, HORZRES ); const int swidth = monRect.right - monRect.left;
sheight = GetDeviceCaps( hDC, VERTRES ); const int sheight = monRect.bottom - monRect.top;
ReleaseDC( GetDesktopWindow(), hDC ); 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.windowWidth = w;
s_wcd.windowHeight = rect.bottom - rect.top + 1; s_wcd.windowHeight = h;
s_wcd.hWnd = CreateWindowEx( 0, s_wcd.hWnd = CreateWindowEx( 0,
DEDCLASS, DEDCLASS,
CONSOLE_WINDOW_TITLE, " "CONSOLE_WINDOW_TITLE,
DEDSTYLE, DEDSTYLE,
( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1, x, y,
w, h,
NULL, NULL,
NULL, NULL,
g_wv.hInstance, g_wv.hInstance,
@ -350,8 +353,8 @@ void Sys_CreateConsole( void )
// //
// create fonts // create fonts
// //
hDC = GetDC( s_wcd.hWnd ); const HDC hDC = GetDC( s_wcd.hWnd );
nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY), 72); const int nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
s_wcd.hfBufferFont = CreateFont( nHeight, s_wcd.hfBufferFont = CreateFont( nHeight,
0, 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 // we don't want to act like we're active if we're minimized
g_wv.activeApp = (fActive && !g_wv.isMinimized); g_wv.activeApp = (fActive && !g_wv.isMinimized);
WIN_UpdateHardwareGammaRamp( g_wv.activeApp );
// minimize/restore mouse-capture on demand // minimize/restore mouse-capture on demand
IN_Activate( g_wv.activeApp ); IN_Activate( g_wv.activeApp );
} }
@ -272,25 +274,27 @@ LRESULT CALLBACK MainWndProc (
{ {
if (!r_fullscreen->integer ) if (!r_fullscreen->integer )
{ {
int xPos = (short)LOWORD(lParam); // horizontal position WIN_GetMonitorIndexFromMainWindow();
int yPos = (short)HIWORD(lParam); // vertical position
RECT r; RECT r;
r.left = 0; r.left = 0;
r.top = 0; r.top = 0;
r.right = 1; r.right = 1;
r.bottom = 1; r.bottom = 1;
AdjustWindowRect( &r, GetWindowLong( hWnd, GWL_STYLE ), FALSE ); AdjustWindowRect( &r, GetWindowLong( hWnd, GWL_STYLE ), FALSE );
Cvar_SetValue( "vid_xpos", xPos + r.left ); const RECT monRect = g_wv.monitorRects[g_wv.monitor];
Cvar_SetValue( "vid_ypos", yPos + r.top ); 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_xpos->modified = qfalse;
vid_ypos->modified = qfalse; vid_ypos->modified = qfalse;
if ( g_wv.activeApp ) // @TODO: fix this broken mess
{ if ( g_wv.activeApp )
IN_Activate (qtrue); {
} IN_Activate (qtrue);
}
} }
} }
break; break;