From c3ee3f2a4d319cae472cb54d8492773b360171d5 Mon Sep 17 00:00:00 2001 From: myT Date: Thu, 1 Jun 2017 20:48:21 +0200 Subject: [PATCH] with r_mode 1, tabbing out of the game will restore display settings on the desktop fixed window restore after minimizing when clicking the task bar or using alt-tab fixed r_mode 1 on secondary monitors fixed in_mouse 2 input on secondary monitors fixed the cursor sometimes staying visible and unclipped when restoring the window --- code/win32/glw_win.h | 3 +- code/win32/win_glimp.cpp | 125 +++++++++++++++++++++++++++++-------- code/win32/win_input.cpp | 64 +++++++------------ code/win32/win_local.h | 5 +- code/win32/win_main.cpp | 2 +- code/win32/win_wndproc.cpp | 42 +++++++++---- 6 files changed, 158 insertions(+), 83 deletions(-) diff --git a/code/win32/glw_win.h b/code/win32/glw_win.h index 2bc7adc..943fcbf 100644 --- a/code/win32/glw_win.h +++ b/code/win32/glw_win.h @@ -38,7 +38,8 @@ typedef struct HGLRC hGLRC; HINSTANCE hinstOpenGL; int desktopBPP; - qbool cdsFullscreen; + qbool cdsDevModeValid; + DEVMODE cdsDevMode; // Custom device mode for full-screen with r_mode 1. qbool pixelFormatSet; int nPendingPF; } glwstate_t; diff --git a/code/win32/win_glimp.cpp b/code/win32/win_glimp.cpp index 64d6cef..d82ea92 100644 --- a/code/win32/win_glimp.cpp +++ b/code/win32/win_glimp.cpp @@ -471,7 +471,7 @@ static qbool GLW_CreateWindow( int width, int height, int colorbits ) const int w = r.right - r.left; const int h = r.bottom - r.top; - const RECT monRect = g_wv.monitorRects[g_wv.monitor]; + const RECT& monRect = g_wv.monitorRects[g_wv.monitor]; int dx = 0; int dy = 0; @@ -517,19 +517,72 @@ static qbool GLW_CreateWindow( int width, int height, int colorbits ) } -static qbool GLW_Fullscreen( DEVMODE& dm ) +static const char* GLW_GetCurrentDisplayDeviceName() { - int cds = ChangeDisplaySettings( &dm, CDS_FULLSCREEN ); + static char deviceName[CCHDEVICENAME + 1]; - if (cds == DISP_CHANGE_SUCCESSFUL) + 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 ); return qtrue; + } - ri.Printf( PRINT_ALL, "...CDS: %ix%i (C%i) failed: ", dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel ); + glw_state.cdsDevModeValid = qfalse; + + ri.Printf( PRINT_ALL, "...CDS: %ix%i (C%i) failed: ", (int)dm.dmPelsWidth, (int)dm.dmPelsHeight, (int)dm.dmBitsPerPel ); #define CDS_ERROR(x) case x: ri.Printf( PRINT_ALL, #x##"\n" ); break; - switch (cds) { + switch (ec) { default: - ri.Printf( PRINT_ALL, "unknown error %d\n", cds ); + ri.Printf( PRINT_ALL, "unknown error %d\n", ec ); break; CDS_ERROR( DISP_CHANGE_RESTART ); CDS_ERROR( DISP_CHANGE_BADPARAM ); @@ -544,6 +597,31 @@ static qbool GLW_Fullscreen( DEVMODE& dm ) } +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 ); +} + + static qbool GLW_SetMode( qbool cdsFullscreen ) { HDC hDC = GetDC( GetDesktopWindow() ); @@ -553,7 +631,7 @@ static qbool GLW_SetMode( qbool cdsFullscreen ) glInfo.isFullscreen = cdsFullscreen; WIN_UpdateMonitorIndexFromCvar(); if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect ) ) { - const RECT monRect = g_wv.monitorRects[g_wv.monitor]; + const RECT& monRect = g_wv.monitorRects[g_wv.monitor]; glConfig.vidWidth = monRect.right - monRect.left; glConfig.vidHeight = monRect.bottom - monRect.top; glConfig.windowAspect = (float)glConfig.vidWidth / glConfig.vidHeight; @@ -562,10 +640,10 @@ static qbool GLW_SetMode( qbool cdsFullscreen ) //ri.Printf( PRINT_DEVELOPER, "...setting mode %dx%d %s\n", glConfig.vidWidth, glConfig.vidHeight, cdsFullscreen ? "FS" : "W" ); DEVMODE dm; - memset( &dm, 0, sizeof( dm ) ); + ZeroMemory( &dm, sizeof( dm ) ); dm.dmSize = sizeof( dm ); - if (cdsFullscreen != glw_state.cdsFullscreen) { + if (cdsFullscreen != glw_state.cdsDevModeValid) { if (cdsFullscreen) { dm.dmPelsWidth = glConfig.vidWidth; dm.dmPelsHeight = glConfig.vidHeight; @@ -581,25 +659,23 @@ static qbool GLW_SetMode( qbool cdsFullscreen ) dm.dmFields |= DM_BITSPERPEL; } - glInfo.isFullscreen = qtrue; - glw_state.cdsFullscreen = qtrue; + const RECT& monRect = g_wv.monitorRects[g_wv.monitor]; + dm.dmPosition.x = monRect.left; + dm.dmPosition.y = monRect.top; + dm.dmFields |= DM_POSITION; - if (!GLW_Fullscreen( dm )) { - glInfo.isFullscreen = qfalse; - glw_state.cdsFullscreen = qfalse; - } + glInfo.isFullscreen = GLW_SetDisplaySettings( dm ); } else { - ChangeDisplaySettings( 0, 0 ); - glw_state.cdsFullscreen = qfalse; + GLW_ResetDisplaySettings( qtrue ); } } if (!GLW_CreateWindow( glConfig.vidWidth, glConfig.vidHeight, glConfig.colorBits )) return qfalse; - if (EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &dm )) + if (EnumDisplaySettingsA( GLW_GetCurrentDisplayDeviceName(), ENUM_CURRENT_SETTINGS, &dm )) glInfo.displayFrequency = dm.dmDisplayFrequency; return qtrue; @@ -668,7 +744,7 @@ static qbool GLW_LoadOpenGL() // load the driver and bind our function pointers to it if ( QGL_Init( OPENGL_DRIVER_NAME ) ) { // create the window and set up the context - if ( GLW_SetMode( (qbool)r_fullscreen->integer ) ) { + if ( GLW_SetMode( (qbool)!!r_fullscreen->integer ) ) { return qtrue; } } @@ -783,11 +859,10 @@ void GLimp_Shutdown() } // reset display settings - if ( glw_state.cdsFullscreen ) + if ( glw_state.cdsDevModeValid ) { ri.Printf( PRINT_DEVELOPER, "...resetting display\n" ); - ChangeDisplaySettings( 0, 0 ); - glw_state.cdsFullscreen = qfalse; + GLW_ResetDisplaySettings( qtrue ); } // shutdown QGL subsystem @@ -895,10 +970,10 @@ void GLimp_WakeRenderer( void *data ) { WaitForSingleObject( renderActiveEvent, INFINITE ); } - + void WIN_UpdateResolution( int width, int height ) { glConfig.vidWidth = width; glConfig.vidHeight = height; } - + diff --git a/code/win32/win_input.cpp b/code/win32/win_input.cpp index 0fc6175..d88dceb 100644 --- a/code/win32/win_input.cpp +++ b/code/win32/win_input.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA struct Mouse { virtual qbool Init() { return qtrue; } virtual qbool Activate( qbool active ); - virtual void OnWindowMoved() {} virtual void Shutdown() {} virtual qbool ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { return qfalse; } // returns true if the event was handled @@ -91,17 +90,14 @@ qbool rawmouse_t::Init() qbool rawmouse_t::Activate( qbool active ) { + // RIDEV_NOLEGACY means we only get WM_INPUT and not WM_LBUTTONDOWN etc RAWINPUTDEVICE rid; + rid.usUsagePage = 1; + rid.usUsage = 2; + rid.dwFlags = active ? RIDEV_NOLEGACY : RIDEV_REMOVE; + rid.hwndTarget = NULL; - rid.usUsagePage = 0x01; - rid.usUsage = 0x02; // page 1 item 2 = mouse, gg constants you asswipes >:( - if (active) - rid.dwFlags = RIDEV_NOLEGACY; - else - rid.dwFlags = RIDEV_REMOVE; - rid.hwndTarget = 0; - - return RegisterRawInputDevices( &rid, 1, sizeof(rid) ); + return !!RegisterRawInputDevices( &rid, 1, sizeof(rid) ); } @@ -162,7 +158,6 @@ qbool rawmouse_t::ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) struct winmouse_t : public Mouse { virtual qbool Activate( qbool active ); - virtual void OnWindowMoved(); virtual qbool ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); void UpdateWindowCenter(); @@ -176,14 +171,9 @@ static winmouse_t winmouse; void winmouse_t::UpdateWindowCenter() { - const int sw = GetSystemMetrics( SM_CXSCREEN ); - const int sh = GetSystemMetrics( SM_CYSCREEN ); - - RECT rc; - GetWindowRect( g_wv.hWnd, &rc ); - - window_center_x = ( max(rc.left, 0) + min(rc.right, sw) ) / 2; - window_center_y = ( max(rc.top, 0) + min(rc.bottom, sh) ) / 2; + const RECT& rect = g_wv.monitorRects[g_wv.monitor]; + window_center_x = (int)( rect.left + rect.right ) / 2; + window_center_y = (int)( rect.top + rect.bottom ) / 2; } @@ -201,17 +191,13 @@ qbool winmouse_t::Activate( qbool _active ) } -void winmouse_t::OnWindowMoved() -{ - UpdateWindowCenter(); -} - - qbool winmouse_t::ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { if ( !active ) return qfalse; + UpdateWindowCenter(); + #define QUEUE_WM_BUTTON( qbutton, mask ) \ Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qbutton, (wParam & mask), 0, NULL ); @@ -352,14 +338,8 @@ void IN_Shutdown() } -// called when the window gains or loses focus or changes in some way -// the window may have been destroyed and recreated between a deactivate and an activate - -void IN_Activate( qbool active ) +void IN_SetCursorSettings( qbool active ) { - if ( !mouse || !mouse->Mouse::Activate( active ) ) - return; - if (active) { while (ShowCursor(FALSE) >= 0) ; @@ -373,17 +353,20 @@ void IN_Activate( qbool active ) ClipCursor( NULL ); ReleaseCapture(); } - - mouse->Activate( active ); } -void IN_WindowMoved() +// called when the window gains or loses focus or changes in some way +// the window may have been destroyed and recreated between a deactivate and an activate + +void IN_Activate( qbool active ) { - if (!mouse) + if ( !mouse || !mouse->Mouse::Activate( active ) ) return; - mouse->OnWindowMoved(); + IN_SetCursorSettings( active ); + + mouse->Activate( active ); } @@ -403,12 +386,7 @@ void IN_Frame() if (!mouse) return; - if (!IN_ShouldBeActive()) { - IN_Activate( qfalse ); - return; - } - - IN_Activate( qtrue ); + IN_Activate( IN_ShouldBeActive() ); } diff --git a/code/win32/win_local.h b/code/win32/win_local.h index 36d4724..c36f2ac 100644 --- a/code/win32/win_local.h +++ b/code/win32/win_local.h @@ -33,8 +33,8 @@ void Conbuf_AppendText( const char *msg ); void IN_Init(); +void IN_SetCursorSettings( qbool active ); void IN_Activate( qbool active ); -void IN_WindowMoved(); qbool IN_ShouldBeActive(); qbool IN_ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); // returns true if the event was handled void IN_Frame(); @@ -45,6 +45,8 @@ void WIN_UpdateMonitorIndexFromMainWindow(); void WIN_UpdateResolution( int width, int height ); void WIN_RegisterLastValidHotKey(); void WIN_UnregisterHotKey(); +void WIN_SetGameDisplaySettings(); +void WIN_SetDesktopDisplaySettings(); void SNDDMA_Activate(); @@ -62,7 +64,6 @@ typedef struct HWND hWnd; HINSTANCE hInstance; qbool activeApp; - qbool isMinimized; // when we get a windows message, we store the time off // using Sys_Milliseconds diff --git a/code/win32/win_main.cpp b/code/win32/win_main.cpp index 1cc6236..01e93dd 100644 --- a/code/win32/win_main.cpp +++ b/code/win32/win_main.cpp @@ -605,7 +605,7 @@ void WIN_UpdateMonitorIndexFromCvar() return; } - g_wv.monitor = monitor - 1; + g_wv.monitor = Com_ClampInt( 0, g_wv.monitorCount - 1, monitor - 1 ); } diff --git a/code/win32/win_wndproc.cpp b/code/win32/win_wndproc.cpp index 66c3ddf..609cf80 100644 --- a/code/win32/win_wndproc.cpp +++ b/code/win32/win_wndproc.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../client/client.h" #include "win_local.h" +#include "glw_win.h" // Console variables that we need to access from this module @@ -72,19 +73,19 @@ static void WIN_EnableAltTab() } -static void VID_AppActivate( BOOL fActive, BOOL minimize ) +static void WIN_AppActivate( BOOL fActive, BOOL fMinimized ) { - if (r_fullscreen->integer) - SetWindowPos( g_wv.hWnd, fActive ? HWND_TOPMOST : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); + const qbool active = fActive && !fMinimized; - g_wv.isMinimized = (minimize == TRUE); + if (r_fullscreen->integer) + SetWindowPos( g_wv.hWnd, active ? HWND_TOPMOST : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); Com_DPrintf("VID_AppActivate: %i\n", fActive ); Key_ClearStates(); // FIXME!!! // we don't want to act like we're active if we're minimized - g_wv.activeApp = (fActive && !g_wv.isMinimized); + g_wv.activeApp = active; IN_Activate( IN_ShouldBeActive() ); } @@ -268,7 +269,7 @@ LRESULT CALLBACK MainWndProc ( break; case WM_ACTIVATE: - VID_AppActivate( (LOWORD(wParam) != WA_INACTIVE), (BOOL)HIWORD(wParam) ); + WIN_AppActivate( (LOWORD(wParam) != WA_INACTIVE), !!(BOOL)HIWORD(wParam) ); SNDDMA_Activate(); break; @@ -285,17 +286,13 @@ LRESULT CALLBACK MainWndProc ( r.bottom = 1; AdjustWindowRect( &r, GetWindowLong( hWnd, GWL_STYLE ), FALSE ); - const RECT monRect = g_wv.monitorRects[g_wv.monitor]; + 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_WindowMoved(); - } } } break; @@ -353,6 +350,29 @@ LRESULT CALLBACK MainWndProc ( } break; + case WM_SETFOCUS: + if ( glw_state.cdsDevModeValid ) // is there a valid mode to restore? + { + WIN_SetGameDisplaySettings(); + if ( glw_state.cdsDevModeValid ) // was the mode successfully restored? + { + const RECT& rect = g_wv.monitorRects[g_wv.monitor]; + const DEVMODE& dm = glw_state.cdsDevMode; + SetWindowPos( hWnd, NULL, (int)rect.left, (int)rect.top, (int)dm.dmPelsWidth, (int)dm.dmPelsHeight, SWP_NOZORDER ); + } + } + g_wv.activeApp = (qbool)!IsIconic( hWnd ); + IN_SetCursorSettings( IN_ShouldBeActive() ); + + break; + + case WM_KILLFOCUS: + g_wv.activeApp = qfalse; + IN_SetCursorSettings( qfalse ); + if ( glw_state.cdsDevModeValid ) + WIN_SetDesktopDisplaySettings(); + break; + default: // 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