diff --git a/stepmania/src/arch/ArchHooks/ArchHooks.h b/stepmania/src/arch/ArchHooks/ArchHooks.h index f63d0ad311..35fbc55c85 100644 --- a/stepmania/src/arch/ArchHooks/ArchHooks.h +++ b/stepmania/src/arch/ArchHooks/ArchHooks.h @@ -21,6 +21,12 @@ public: */ virtual CString GetPreferredLanguage() { return "EN"; } + /* + * If this is a second instance, return true. Optionally, give focus to the existing + * window. + */ + virtual bool CheckForMultipleInstances() { return false; } + /* Call this to temporarily enter a high-priority or realtime scheduling (depending * on the implementation) mode. This is used to improve timestamp accuracy. Do as * little as possible in this mode; hanging in it might hang the system entirely. */ diff --git a/stepmania/src/arch/ArchHooks/ArchHooks_Win32.cpp b/stepmania/src/arch/ArchHooks/ArchHooks_Win32.cpp index 0cafc52e76..a72e88e2ef 100644 --- a/stepmania/src/arch/ArchHooks/ArchHooks_Win32.cpp +++ b/stepmania/src/arch/ArchHooks/ArchHooks_Win32.cpp @@ -19,6 +19,10 @@ #pragma comment(lib, "winmm.lib") // for timeGetTime #endif +static HANDLE g_hInstanceMutex; +static bool g_bIsMultipleInstance = false; + + ArchHooks_Win32::ArchHooks_Win32() { SetUnhandledExceptionFilter(CrashHandler); @@ -31,10 +35,17 @@ ArchHooks_Win32::ArchHooks_Win32() /* Windows boosts priority on keyboard input, among other things. Disable that for * the main thread. */ SetThreadPriorityBoost( GetCurrentThread(), TRUE ); + + g_hInstanceMutex = CreateMutex( NULL, TRUE, PRODUCT_NAME ); + + g_bIsMultipleInstance = false; + if( GetLastError() == ERROR_ALREADY_EXISTS ) + g_bIsMultipleInstance = true; } ArchHooks_Win32::~ArchHooks_Win32() { + CloseHandle( g_hInstanceMutex ); delete TimeCritMutex; } @@ -47,6 +58,57 @@ void ArchHooks_Win32::DumpDebugInfo() CheckVideoDriver(); } +struct CallbackData +{ + HWND hParent; + HWND hResult; +}; + +// Like GW_ENABLEDPOPUP: +static BOOL CALLBACK GetEnabledPopup( HWND hWnd, LPARAM lParam ) +{ + CallbackData *pData = (CallbackData *) lParam; + if( GetParent(hWnd) != pData->hParent ) + return TRUE; + if( (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUP) != WS_POPUP ) + return TRUE; + if( !IsWindowEnabled(hWnd) ) + return TRUE; + + pData->hResult = hWnd; + return FALSE; +} + +bool ArchHooks_Win32::CheckForMultipleInstances() +{ + if( !g_bIsMultipleInstance ) + return false; + + /* Search for the existing window. Prefer to use the class name, which is less likely to + * have a false match, and will match the gameplay window. If that fails, try the window + * name, which should match the loading window. */ + HWND hWnd = FindWindow( PRODUCT_NAME, NULL ); + if( hWnd == NULL ) + hWnd = FindWindow( NULL, PRODUCT_NAME ); + + if( hWnd != NULL ) + { + /* If the application has a model dialog box open, we want to be sure to give focus to it, + * not the main window. */ + CallbackData data; + data.hParent = hWnd; + data.hResult = NULL; + EnumWindows( GetEnabledPopup, (LPARAM) &data ); + + if( data.hResult != NULL ) + SetForegroundWindow( data.hResult ); + else + SetForegroundWindow( hWnd ); + } + + return true; +} + static CString g_sDriverVersion, g_sURL; static bool g_Hush; static BOOL CALLBACK DriverWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) diff --git a/stepmania/src/arch/ArchHooks/ArchHooks_Win32.h b/stepmania/src/arch/ArchHooks/ArchHooks_Win32.h index e90873c4dc..e10fe13780 100644 --- a/stepmania/src/arch/ArchHooks/ArchHooks_Win32.h +++ b/stepmania/src/arch/ArchHooks/ArchHooks_Win32.h @@ -12,6 +12,7 @@ public: void DumpDebugInfo(); void RestartProgram(); CString GetPreferredLanguage(); + bool CheckForMultipleInstances(); int OldThreadPriority; RageMutex *TimeCritMutex;