You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1746 lines
42 KiB
1746 lines
42 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
#if defined( USE_SDL ) |
|
#undef PROTECTED_THINGS_ENABLE |
|
#include "SDL.h" |
|
#include "SDL_syswm.h" |
|
|
|
#if defined( OSX ) |
|
#define DONT_DEFINE_BOOL |
|
#include <objc/message.h> |
|
#endif |
|
|
|
#endif |
|
|
|
#if defined( WIN32 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) |
|
#include "winlite.h" |
|
#include "xbox/xboxstubs.h" |
|
#endif |
|
|
|
#if defined( IS_WINDOWS_PC ) && !defined( USE_SDL ) |
|
#include <winsock.h> |
|
#elif defined(_X360) |
|
// nothing to include for 360 |
|
#elif defined(OSX) |
|
#elif defined(LINUX) || defined(PLATFORM_BSD) |
|
#include "tier0/dynfunction.h" |
|
#elif defined(_WIN32) |
|
#include "tier0/dynfunction.h" |
|
#else |
|
#error |
|
#endif |
|
#include "appframework/ilaunchermgr.h" |
|
|
|
#include "igame.h" |
|
#include "cl_main.h" |
|
#include "host.h" |
|
#include "quakedef.h" |
|
#include "tier0/vcrmode.h" |
|
#include "tier0/icommandline.h" |
|
#include "ivideomode.h" |
|
#include "gl_matsysiface.h" |
|
#include "cdll_engine_int.h" |
|
#include "vgui_baseui_interface.h" |
|
#include "iengine.h" |
|
#include "keys.h" |
|
#include "VGuiMatSurface/IMatSystemSurface.h" |
|
#include "tier3/tier3.h" |
|
#include "sound.h" |
|
#include "vgui_controls/Controls.h" |
|
#include "vgui_controls/MessageDialog.h" |
|
#include "sys_dll.h" |
|
#include "inputsystem/iinputsystem.h" |
|
#include "inputsystem/ButtonCode.h" |
|
#ifdef WIN32 |
|
#undef WIN32_LEAN_AND_MEAN |
|
#include "unicode/unicode.h" |
|
#endif |
|
#include "GameUI/IGameUI.h" |
|
#include "matchmaking.h" |
|
#include "sv_main.h" |
|
#include "video/ivideoservices.h" |
|
#include "sys.h" |
|
#include "materialsystem/imaterial.h" |
|
|
|
|
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#include "hl2orange.spa.h" |
|
#endif |
|
|
|
#if defined( LINUX ) |
|
#include "snd_dev_sdl.h" |
|
#endif |
|
|
|
|
|
#ifdef DBGFLAG_ASSERT |
|
|
|
#define AssertExit( _exp ) Assert( _exp ) |
|
#define AssertExitF( _exp ) Assert( _exp ) |
|
|
|
#else |
|
|
|
#define AssertExit( _exp ) if ( !( _exp ) ) return; |
|
#define AssertExitF( _exp ) if ( !( _exp ) ) return false; |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
void S_BlockSound (void); |
|
void S_UnblockSound (void); |
|
void ClearIOStates( void ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Game input events |
|
//----------------------------------------------------------------------------- |
|
enum GameInputEventType_t |
|
{ |
|
IE_Close = IE_FirstAppEvent, |
|
IE_WindowMove, |
|
IE_AppActivated, |
|
}; |
|
|
|
|
|
|
|
#ifdef WIN32 |
|
static IUnicodeWindows *unicode = NULL; |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main game interface, including message pump and window creation |
|
//----------------------------------------------------------------------------- |
|
class CGame : public IGame |
|
{ |
|
public: |
|
CGame( void ); |
|
virtual ~CGame( void ); |
|
|
|
bool Init( void *pvInstance ); |
|
bool Shutdown( void ); |
|
|
|
bool CreateGameWindow( void ); |
|
void DestroyGameWindow(); |
|
void SetGameWindow( void* hWnd ); |
|
|
|
// This is used in edit mode to override the default wnd proc associated w/ |
|
bool InputAttachToGameWindow(); |
|
void InputDetachFromGameWindow(); |
|
|
|
void PlayStartupVideos( void ); |
|
|
|
// This is the SDL_Window* under SDL, HWND otherwise. |
|
void* GetMainWindow( void ); |
|
// This will be the HWND under D3D + Windows (both with and without SDL), SDL_Window* everywhere else. |
|
void* GetMainDeviceWindow( void ); |
|
// This will be the HWND under Windows, the WindowRef under Mac, and (for now) NULL on Linux |
|
void* GetMainWindowPlatformSpecificHandle( void ); |
|
void** GetMainWindowAddress( void ); |
|
|
|
void GetDesktopInfo( int &width, int &height, int &refreshrate ); |
|
|
|
|
|
void SetWindowXY( int x, int y ); |
|
void SetWindowSize( int w, int h ); |
|
void GetWindowRect( int *x, int *y, int *w, int *h ); |
|
|
|
bool IsActiveApp( void ); |
|
|
|
void SetCanPostActivateEvents( bool bEnable ); |
|
bool CanPostActivateEvents(); |
|
|
|
public: |
|
#ifdef USE_SDL |
|
void SetMainWindow( SDL_Window* window ); |
|
#else |
|
#ifdef WIN32 |
|
LRESULT WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); |
|
#endif |
|
void SetMainWindow( HWND window ); |
|
#endif |
|
void SetActiveApp( bool active ); |
|
|
|
bool LoadUnicode(); |
|
void UnloadUnicode(); |
|
|
|
// Message handlers. |
|
public: |
|
void HandleMsg_WindowMove( const InputEvent_t &event ); |
|
void HandleMsg_ActivateApp( const InputEvent_t &event ); |
|
void HandleMsg_Close( const InputEvent_t &event ); |
|
|
|
// Call the appropriate HandleMsg_ function. |
|
void DispatchInputEvent( const InputEvent_t &event ); |
|
|
|
// Dispatch all the queued up messages. |
|
virtual void DispatchAllStoredGameMessages(); |
|
private: |
|
void AppActivate( bool fActive ); |
|
void PlayVideoAndWait( const char *filename, bool bNeedHealthWarning = false); // plays a video file and waits till it's done to return. Can be interrupted by user. |
|
|
|
private: |
|
void AttachToWindow(); |
|
void DetachFromWindow(); |
|
|
|
#ifndef _X360 |
|
static const wchar_t CLASSNAME[]; |
|
#else |
|
static const char CLASSNAME[]; |
|
#endif |
|
|
|
bool m_bExternallySuppliedWindow; |
|
|
|
#if defined( WIN32 ) |
|
HWND m_hWindow; |
|
#if !defined( USE_SDL ) |
|
HINSTANCE m_hInstance; |
|
|
|
// Stores a wndproc to chain message calls to |
|
WNDPROC m_ChainedWindowProc; |
|
|
|
RECT m_rcLastRestoredClientRect; |
|
#endif |
|
#endif |
|
|
|
#if defined( USE_SDL ) |
|
SDL_Window *m_pSDLWindow; |
|
#endif |
|
|
|
int m_x; |
|
int m_y; |
|
int m_width; |
|
int m_height; |
|
bool m_bActiveApp; |
|
CSysModule *m_hUnicodeModule; |
|
|
|
bool m_bCanPostActivateEvents; |
|
int m_iDesktopWidth, m_iDesktopHeight, m_iDesktopRefreshRate; |
|
void UpdateDesktopInformation(); |
|
#ifdef WIN32 |
|
void UpdateDesktopInformation( WPARAM wParam, LPARAM lParam ); |
|
#endif |
|
}; |
|
|
|
static CGame g_Game; |
|
IGame *game = ( IGame * )&g_Game; |
|
|
|
#if !defined( _X360 ) |
|
const wchar_t CGame::CLASSNAME[] = L"Valve001"; |
|
#else |
|
const char CGame::CLASSNAME[] = "Valve001"; |
|
#endif |
|
|
|
// In VCR playback mode, it sleeps this amount each frame. |
|
int g_iVCRPlaybackSleepInterval = 0; |
|
|
|
// During VCR playback, if this is true, then it'll pause at the end of each frame. |
|
bool g_bVCRSingleStep = false; |
|
|
|
bool g_bWaitingForStepKeyUp = false; // Used to prevent it from running frames while you hold the S key down. |
|
|
|
bool g_bShowVCRPlaybackDisplay = true; |
|
|
|
// These are all the windows messages that can change game state. |
|
// See CGame::WindowProc for a description of how they work. |
|
struct GameMessageHandler_t |
|
{ |
|
int m_nEventType; |
|
void (CGame::*pFn)( const InputEvent_t &event ); |
|
}; |
|
|
|
GameMessageHandler_t g_GameMessageHandlers[] = |
|
{ |
|
{ IE_AppActivated, &CGame::HandleMsg_ActivateApp }, |
|
{ IE_WindowMove, &CGame::HandleMsg_WindowMove }, |
|
{ IE_Close, &CGame::HandleMsg_Close }, |
|
{ IE_Quit, &CGame::HandleMsg_Close }, |
|
}; |
|
|
|
|
|
void CGame::AppActivate( bool fActive ) |
|
{ |
|
// If text mode, force it to be active. |
|
if ( g_bTextMode ) |
|
{ |
|
fActive = true; |
|
} |
|
|
|
// Don't bother if we're already in the correct state |
|
if ( IsActiveApp() == fActive ) |
|
return; |
|
|
|
// Don't let video modes changes queue up another activate event |
|
SetCanPostActivateEvents( false ); |
|
|
|
#ifndef SWDS |
|
if ( videomode ) |
|
{ |
|
if ( fActive ) |
|
{ |
|
videomode->RestoreVideo(); |
|
} |
|
else |
|
{ |
|
videomode->ReleaseVideo(); |
|
} |
|
} |
|
|
|
if ( host_initialized ) |
|
{ |
|
if ( fActive ) |
|
{ |
|
// Clear keyboard states (should be cleared already but...) |
|
// VGui_ActivateMouse will reactivate the mouse soon. |
|
ClearIOStates(); |
|
|
|
UpdateMaterialSystemConfig(); |
|
} |
|
else |
|
{ |
|
// Clear keyboard input and deactivate the mouse while we're away. |
|
ClearIOStates(); |
|
|
|
if ( g_ClientDLL ) |
|
{ |
|
g_ClientDLL->IN_DeactivateMouse(); |
|
} |
|
} |
|
} |
|
#endif // SWDS |
|
SetActiveApp( fActive ); |
|
|
|
#ifdef _XBOX |
|
if ( host_initialized ) |
|
{ |
|
ClearIOStates(); |
|
if ( fActive ) |
|
{ |
|
UpdateMaterialSystemConfig(); |
|
} |
|
} |
|
SetActiveApp( fActive ); |
|
#endif |
|
|
|
// Allow queueing of activation events |
|
SetCanPostActivateEvents( true ); |
|
} |
|
|
|
void CGame::HandleMsg_WindowMove( const InputEvent_t &event ) |
|
{ |
|
game->SetWindowXY( event.m_nData, event.m_nData2 ); |
|
#ifndef SWDS |
|
videomode->UpdateWindowPosition(); |
|
#endif |
|
} |
|
|
|
void CGame::HandleMsg_ActivateApp( const InputEvent_t &event ) |
|
{ |
|
AppActivate( event.m_nData ? true : false ); |
|
} |
|
|
|
void CGame::HandleMsg_Close( const InputEvent_t &event ) |
|
{ |
|
if ( eng->GetState() == IEngine::DLL_ACTIVE ) |
|
{ |
|
eng->SetQuitting( IEngine::QUIT_TODESKTOP ); |
|
} |
|
} |
|
|
|
void CGame::DispatchInputEvent( const InputEvent_t &event ) |
|
{ |
|
switch( event.m_nType & 0xFFFF ) |
|
{ |
|
// Handle button events specially, |
|
// since we have all manner of crazy filtering going on when dealing with them |
|
case IE_ButtonPressed: |
|
case IE_ButtonDoubleClicked: |
|
case IE_ButtonReleased: |
|
Key_Event( event ); |
|
break; |
|
case IE_FingerDown: |
|
case IE_FingerUp: |
|
case IE_FingerMotion: |
|
if( g_ClientDLL ) |
|
g_ClientDLL->IN_TouchEvent( event.m_nType, event.m_nData, event.m_nData2, event.m_nData3 ); |
|
default: |
|
// Let vgui have the first whack at events |
|
if ( g_pMatSystemSurface && g_pMatSystemSurface->HandleInputEvent( event ) ) |
|
break; |
|
|
|
for ( int i=0; i < ARRAYSIZE( g_GameMessageHandlers ); i++ ) |
|
{ |
|
if ( g_GameMessageHandlers[i].m_nEventType == event.m_nType ) |
|
{ |
|
(this->*g_GameMessageHandlers[i].pFn)( event ); |
|
break; |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
|
|
void CGame::DispatchAllStoredGameMessages() |
|
{ |
|
#if !defined( NO_VCR ) |
|
if ( VCRGetMode() == VCR_Playback ) |
|
{ |
|
InputEvent_t event; |
|
while ( VCRHook_PlaybackGameMsg( &event ) ) |
|
{ |
|
event.m_nTick = g_pInputSystem->GetPollTick(); |
|
DispatchInputEvent( event ); |
|
} |
|
} |
|
else |
|
{ |
|
int nEventCount = g_pInputSystem->GetEventCount(); |
|
const InputEvent_t* pEvents = g_pInputSystem->GetEventData( ); |
|
for ( int i = 0; i < nEventCount; ++i ) |
|
{ |
|
VCRHook_RecordGameMsg( pEvents[i] ); |
|
DispatchInputEvent( pEvents[i] ); |
|
} |
|
VCRHook_RecordEndGameMsg(); |
|
} |
|
#else |
|
int nEventCount = g_pInputSystem->GetEventCount(); |
|
const InputEvent_t* pEvents = g_pInputSystem->GetEventData( ); |
|
for ( int i = 0; i < nEventCount; ++i ) |
|
{ |
|
DispatchInputEvent( pEvents[i] ); |
|
} |
|
#endif |
|
} |
|
|
|
void VCR_EnterPausedState() |
|
{ |
|
// Turn this off in case they're in single-step mode. |
|
g_bVCRSingleStep = false; |
|
|
|
#ifdef WIN32 |
|
// This is cheesy, but GetAsyncKeyState is blocked (in protected_things.h) |
|
// from being accidentally used, so we get it through it by getting its pointer directly. |
|
static HINSTANCE hInst = LoadLibrary( "user32.dll" ); |
|
if ( !hInst ) |
|
return; |
|
|
|
typedef SHORT (WINAPI *GetAsyncKeyStateFn)( int vKey ); |
|
static GetAsyncKeyStateFn pfn = (GetAsyncKeyStateFn)GetProcAddress( hInst, "GetAsyncKeyState" ); |
|
if ( !pfn ) |
|
return; |
|
|
|
// In this mode, we enter a wait state where we only pay attention to R and Q. |
|
while ( 1 ) |
|
{ |
|
if ( pfn( 'R' ) & 0x8000 ) |
|
break; |
|
|
|
if ( pfn( 'Q' ) & 0x8000 ) |
|
TerminateProcess( GetCurrentProcess(), 1 ); |
|
|
|
if ( pfn( 'S' ) & 0x8000 ) |
|
{ |
|
if ( !g_bWaitingForStepKeyUp ) |
|
{ |
|
// Do a single step. |
|
g_bVCRSingleStep = true; |
|
g_bWaitingForStepKeyUp = true; // Don't do another single step until they release the S key. |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
// Ok, they released the S key, so we'll process it next time the key goes down. |
|
g_bWaitingForStepKeyUp = false; |
|
} |
|
|
|
Sleep( 2 ); |
|
} |
|
#else |
|
Assert( !"Impl me" ); |
|
#endif |
|
} |
|
|
|
#ifdef WIN32 |
|
void VCR_HandlePlaybackMessages( |
|
HWND hWnd, |
|
UINT uMsg, |
|
WPARAM wParam, |
|
LPARAM lParam |
|
) |
|
{ |
|
if ( uMsg == WM_KEYDOWN ) |
|
{ |
|
if ( wParam == VK_SUBTRACT || wParam == 0xbd ) |
|
{ |
|
g_iVCRPlaybackSleepInterval += 5; |
|
} |
|
else if ( wParam == VK_ADD || wParam == 0xbb ) |
|
{ |
|
g_iVCRPlaybackSleepInterval -= 5; |
|
} |
|
else if ( toupper( wParam ) == 'Q' ) |
|
{ |
|
TerminateProcess( GetCurrentProcess(), 1 ); |
|
} |
|
else if ( toupper( wParam ) == 'P' ) |
|
{ |
|
VCR_EnterPausedState(); |
|
} |
|
else if ( toupper( wParam ) == 'S' && !g_bVCRSingleStep ) |
|
{ |
|
g_bWaitingForStepKeyUp = true; |
|
VCR_EnterPausedState(); |
|
} |
|
else if ( toupper( wParam ) == 'D' ) |
|
{ |
|
g_bShowVCRPlaybackDisplay = !g_bShowVCRPlaybackDisplay; |
|
} |
|
|
|
g_iVCRPlaybackSleepInterval = clamp( g_iVCRPlaybackSleepInterval, 0, 500 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Calls the default window procedure |
|
// FIXME: It would be nice to remove the need for this, which we can do |
|
// if we can make unicode work when running inside hammer. |
|
//----------------------------------------------------------------------------- |
|
static LRESULT WINAPI CallDefaultWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
if ( unicode ) |
|
return unicode->DefWindowProcW( hWnd, uMsg, wParam, lParam ); |
|
return DefWindowProc( hWnd, uMsg, wParam, lParam ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: The user has accepted an invitation to a game, we need to detect if |
|
// it's TF2 and restart properly if it is |
|
//----------------------------------------------------------------------------- |
|
void XBX_HandleInvite( DWORD nUserId ) |
|
{ |
|
#ifdef _X360 |
|
// Grab our invite info |
|
XINVITE_INFO inviteInfo; |
|
DWORD dwError = XInviteGetAcceptedInfo( nUserId, &inviteInfo ); |
|
if ( dwError != ERROR_SUCCESS ) |
|
return; |
|
|
|
// We only care if we're asked to join an Orange Box session |
|
if ( inviteInfo.dwTitleID != TITLEID_THE_ORANGE_BOX ) |
|
{ |
|
// Do the normal "we've been invited to a game" behavior |
|
return; |
|
} |
|
|
|
// Otherwise, launch depending on our current MOD |
|
if ( !Q_stricmp( GetCurrentMod(), "tf" ) ) |
|
{ |
|
// We're already running TF2, so just join the session |
|
if ( nUserId != XBX_GetPrimaryUserId() ) |
|
{ |
|
// Switch users, the other had the invite |
|
XBX_SetPrimaryUserId( nUserId ); |
|
} |
|
|
|
// Kick off our join |
|
g_pMatchmaking->JoinInviteSession( &(inviteInfo.hostInfo) ); |
|
} |
|
else |
|
{ |
|
// Save off our session ID for later retrieval |
|
// NOTE: We may need to actually save off the inviter's XID and search for them later on if we took too long or the |
|
// session they were a part of went away |
|
|
|
XBX_SetInviteSessionId( inviteInfo.hostInfo.sessionID ); |
|
XBX_SetInvitedUserId( nUserId ); |
|
|
|
// Quit via the menu path "QuitNoConfirm" |
|
EngineVGui()->SystemNotification( SYSTEMNOTIFY_INVITE_SHUTDOWN ); |
|
|
|
} |
|
#endif //_X360 |
|
} |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
//----------------------------------------------------------------------------- |
|
// Main windows procedure |
|
//----------------------------------------------------------------------------- |
|
LRESULT CGame::WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
|
|
{ |
|
LRESULT lRet = 0; |
|
HDC hdc; |
|
PAINTSTRUCT ps; |
|
|
|
// |
|
// NOTE: the way this function works is to handle all messages that just call through to |
|
// Windows or provide data to it. |
|
// |
|
// Any messages that change the engine's internal state (like key events) are stored in a list |
|
// and processed at the end of the frame. This is necessary for VCR mode to work correctly because |
|
// Windows likes to pump messages during some of its API calls like SetWindowPos, and unless we add |
|
// custom code around every Windows API call so VCR mode can trap the wndproc calls, VCR mode can't |
|
// reproduce the calls to the wndproc. |
|
// |
|
|
|
if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING ) |
|
return CallWindowProc( m_ChainedWindowProc, hWnd, uMsg, wParam, lParam ); |
|
|
|
// If we're playing back, listen to a couple input things used to drive VCR mode |
|
if ( VCRGetMode() == VCR_Playback ) |
|
{ |
|
VCR_HandlePlaybackMessages( hWnd, uMsg, wParam, lParam ); |
|
} |
|
|
|
// |
|
// Note: NO engine state should be changed in here while in VCR record or playback. |
|
// We can send whatever we want to Windows, but if we change its state in here instead of |
|
// in DispatchAllStoredGameMessages, the playback may not work because Windows messages |
|
// are not deterministic, so you might get different messages during playback than you did during record. |
|
// |
|
InputEvent_t event; |
|
memset( &event, 0, sizeof(event) ); |
|
event.m_nTick = g_pInputSystem->GetPollTick(); |
|
|
|
switch ( uMsg ) |
|
{ |
|
case WM_CREATE: |
|
::SetForegroundWindow( hWnd ); |
|
break; |
|
|
|
case WM_ACTIVATEAPP: |
|
{ |
|
if ( CanPostActivateEvents() ) |
|
{ |
|
bool bActivated = ( wParam == 1 ); |
|
event.m_nType = IE_AppActivated; |
|
event.m_nData = bActivated; |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
} |
|
break; |
|
|
|
case WM_POWERBROADCAST: |
|
// Don't go into Sleep mode when running engine, we crash on resume for some reason (as |
|
// do half of the apps I have running usually anyway...) |
|
if ( wParam == PBT_APMQUERYSUSPEND ) |
|
{ |
|
Msg( "OS requested hibernation, ignoring request.\n" ); |
|
return BROADCAST_QUERY_DENY; |
|
} |
|
|
|
lRet = CallWindowProc( m_ChainedWindowProc, hWnd, uMsg, wParam, lParam ); |
|
break; |
|
|
|
case WM_SYSCOMMAND: |
|
if ( ( wParam == SC_MONITORPOWER ) || ( wParam == SC_KEYMENU ) || ( wParam == SC_SCREENSAVE ) ) |
|
return lRet; |
|
|
|
if ( wParam == SC_CLOSE ) |
|
{ |
|
#if !defined( NO_VCR ) |
|
// handle the close message, but make sure |
|
// it's not because we accidently hit ALT-F4 |
|
if ( HIBYTE(VCRHook_GetKeyState(VK_LMENU)) || HIBYTE(VCRHook_GetKeyState(VK_RMENU) ) ) |
|
return lRet; |
|
#endif |
|
Cbuf_Clear(); |
|
Cbuf_AddText( "quit\n" ); |
|
} |
|
|
|
#ifndef SWDS |
|
if ( VCRGetMode() == VCR_Disabled ) |
|
{ |
|
S_BlockSound(); |
|
S_ClearBuffer(); |
|
} |
|
#endif |
|
|
|
lRet = CallWindowProc( m_ChainedWindowProc, hWnd, uMsg, wParam, lParam ); |
|
|
|
#ifndef SWDS |
|
if ( VCRGetMode() == VCR_Disabled ) |
|
{ |
|
S_UnblockSound(); |
|
} |
|
#endif |
|
break; |
|
|
|
case WM_CLOSE: |
|
// Handle close messages |
|
event.m_nType = IE_Close; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return 0; |
|
|
|
case WM_MOVE: |
|
event.m_nType = IE_WindowMove; |
|
event.m_nData = (short)LOWORD(lParam); |
|
event.m_nData2 = (short)HIWORD(lParam); |
|
g_pInputSystem->PostUserEvent( event ); |
|
break; |
|
|
|
case WM_SIZE: |
|
if ( wParam != SIZE_MINIMIZED ) |
|
{ |
|
// Update restored client rect |
|
::GetClientRect( hWnd, &m_rcLastRestoredClientRect ); |
|
} |
|
else |
|
{ |
|
#ifndef _X360 |
|
// Fix the window rect to have same client area as it used to have |
|
// before it got minimized |
|
RECT rcWindow; |
|
::GetWindowRect( hWnd, &rcWindow ); |
|
|
|
rcWindow.right = rcWindow.left + m_rcLastRestoredClientRect.right; |
|
rcWindow.bottom = rcWindow.top + m_rcLastRestoredClientRect.bottom; |
|
|
|
::AdjustWindowRect( &rcWindow, ::GetWindowLong( hWnd, GWL_STYLE ), FALSE ); |
|
::MoveWindow( hWnd, rcWindow.left, rcWindow.top, |
|
rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, FALSE ); |
|
#endif |
|
} |
|
break; |
|
|
|
case WM_SYSCHAR: |
|
// keep Alt-Space from happening |
|
break; |
|
|
|
case WM_COPYDATA: |
|
// Hammer -> engine remote console command. |
|
// Return true to indicate that the message was handled. |
|
Cbuf_AddText( (const char *)(((COPYDATASTRUCT *)lParam)->lpData) ); |
|
Cbuf_AddText( "\n" ); |
|
lRet = 1; |
|
break; |
|
|
|
#if defined( _X360 ) |
|
case WM_XREMOTECOMMAND: |
|
Cbuf_AddText( (const char*)lParam ); |
|
Cbuf_AddText( "\n" ); |
|
break; |
|
|
|
case WM_SYS_STORAGEDEVICESCHANGED: |
|
if ( !EngineVGui()->IsGameUIVisible() ) |
|
{ |
|
EngineVGui()->ActivateGameUI(); |
|
} |
|
EngineVGui()->SystemNotification( SYSTEMNOTIFY_STORAGEDEVICES_CHANGED ); |
|
break; |
|
|
|
case WM_XMP_PLAYBACKCONTROLLERCHANGED: |
|
S_EnableMusic( lParam != 0 ); |
|
break; |
|
|
|
case WM_LIVE_INVITE_ACCEPTED: |
|
XBX_HandleInvite( LOWORD( lParam ) ); |
|
break; |
|
|
|
case WM_SYS_SIGNINCHANGED: |
|
if ( XUserGetSigninState( XBX_GetPrimaryUserId() ) == eXUserSigninState_NotSignedIn ) |
|
{ |
|
// X360TBD: User signed out - pause the game? |
|
} |
|
EngineVGui()->SystemNotification( lParam ? SYSTEMNOTIFY_USER_SIGNEDIN : SYSTEMNOTIFY_USER_SIGNEDOUT ); |
|
break; |
|
|
|
case WM_SYS_UI: |
|
if ( lParam ) |
|
{ |
|
// Don't activate it if it's already active (a sub window may be active) |
|
// Multiplayer doesn't want the UI to appear, since it can't pause anyway |
|
if ( !EngineVGui()->IsGameUIVisible() && g_ServerGlobalVariables.maxClients == 1 ) |
|
{ |
|
Cbuf_AddText( "gameui_activate" ); |
|
} |
|
} |
|
EngineVGui()->SystemNotification( lParam ? SYSTEMNOTIFY_XUIOPENING : SYSTEMNOTIFY_XUICLOSED ); |
|
break; |
|
|
|
case WM_SYS_MUTELISTCHANGED: |
|
g_pMatchmaking->UpdateMuteList(); |
|
break; |
|
|
|
case WM_SYS_INPUTDEVICESCHANGED: |
|
{ |
|
XINPUT_CAPABILITIES caps; |
|
if ( XInputGetCapabilities( XBX_GetPrimaryUserId(), XINPUT_FLAG_GAMEPAD, &caps ) == ERROR_DEVICE_NOT_CONNECTED ) |
|
{ |
|
if ( EngineVGui()->IsGameUIVisible() == false ) |
|
{ |
|
EngineVGui()->ActivateGameUI(); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
#endif |
|
|
|
case WM_PAINT: |
|
hdc = BeginPaint(hWnd, &ps); |
|
RECT rcClient; |
|
GetClientRect( hWnd, &rcClient ); |
|
#ifndef SWDS |
|
// Only renders stuff if running -noshaderapi |
|
if ( videomode ) |
|
{ |
|
videomode->DrawNullBackground( hdc, rcClient.right, rcClient.bottom ); |
|
} |
|
#endif |
|
EndPaint(hWnd, &ps); |
|
break; |
|
|
|
#ifndef _X360 |
|
case WM_DISPLAYCHANGE: |
|
if ( !m_iDesktopHeight || !m_iDesktopWidth ) |
|
{ |
|
UpdateDesktopInformation( wParam, lParam ); |
|
} |
|
break; |
|
#endif |
|
|
|
case WM_IME_NOTIFY: |
|
switch ( wParam ) |
|
{ |
|
default: |
|
break; |
|
|
|
#ifndef SWDS |
|
case 14: |
|
if ( !videomode->IsWindowedMode() ) |
|
return 0; |
|
break; |
|
#endif |
|
} |
|
return CallWindowProc( m_ChainedWindowProc, hWnd, uMsg, wParam, lParam); |
|
|
|
default: |
|
lRet = CallWindowProc( m_ChainedWindowProc, hWnd, uMsg, wParam, lParam ); |
|
break; |
|
} |
|
|
|
// return 0 if handled message, 1 if not |
|
return lRet; |
|
} |
|
#elif defined(OSX) || defined(LINUX) || defined(_WIN32) || defined(PLATFORM_BSD) |
|
|
|
#else |
|
#error |
|
#endif |
|
|
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
//----------------------------------------------------------------------------- |
|
// Creates the game window |
|
//----------------------------------------------------------------------------- |
|
static LRESULT WINAPI HLEngineWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
return g_Game.WindowProc( hWnd, uMsg, wParam, lParam ); |
|
} |
|
|
|
#define DEFAULT_EXE_ICON 101 |
|
|
|
static void DoSomeSocketStuffInOrderToGetZoneAlarmToNoticeUs( void ) |
|
{ |
|
#ifdef IS_WINDOWS_PC |
|
WSAData wsaData; |
|
if ( ! WSAStartup( 0x0101, &wsaData ) ) |
|
{ |
|
SOCKET tmpSocket = socket( AF_INET, SOCK_DGRAM, 0 ); |
|
if ( tmpSocket != INVALID_SOCKET ) |
|
{ |
|
char Options[]={ 1 }; |
|
setsockopt( tmpSocket, SOL_SOCKET, SO_BROADCAST, Options, sizeof(Options)); |
|
char pszHostName[256]; |
|
gethostname( pszHostName, sizeof( pszHostName ) ); |
|
hostent *hInfo = gethostbyname( pszHostName ); |
|
if ( hInfo ) |
|
{ |
|
sockaddr_in myIpAddress; |
|
memset( &myIpAddress, 0, sizeof( myIpAddress ) ); |
|
myIpAddress.sin_family = AF_INET; |
|
myIpAddress.sin_port = htons( 27015 ); // our normal server port |
|
myIpAddress.sin_addr.S_un.S_un_b.s_b1 = hInfo->h_addr_list[0][0]; |
|
myIpAddress.sin_addr.S_un.S_un_b.s_b2 = hInfo->h_addr_list[0][1]; |
|
myIpAddress.sin_addr.S_un.S_un_b.s_b3 = hInfo->h_addr_list[0][2]; |
|
myIpAddress.sin_addr.S_un.S_un_b.s_b4 = hInfo->h_addr_list[0][3]; |
|
if ( bind( tmpSocket, ( sockaddr * ) &myIpAddress, sizeof( myIpAddress ) ) != -1 ) |
|
{ |
|
if ( sendto( tmpSocket, pszHostName, 1, 0, ( sockaddr *) &myIpAddress, sizeof( myIpAddress ) ) == -1 ) |
|
{ |
|
// error? |
|
} |
|
|
|
} |
|
} |
|
closesocket( tmpSocket ); |
|
} |
|
WSACleanup(); |
|
} |
|
|
|
#endif |
|
} |
|
#endif |
|
|
|
bool CGame::CreateGameWindow( void ) |
|
{ |
|
// get the window name |
|
char windowName[256]; |
|
windowName[0] = 0; |
|
KeyValues *modinfo = new KeyValues("ModInfo"); |
|
if (modinfo->LoadFromFile(g_pFileSystem, "gameinfo.txt")) |
|
{ |
|
Q_strncpy( windowName, modinfo->GetString("game"), sizeof(windowName) ); |
|
} |
|
|
|
if (!windowName[0]) |
|
{ |
|
Q_strncpy( windowName, "HALF-LIFE 2", sizeof(windowName) ); |
|
} |
|
|
|
if ( IsOpenGL() ) |
|
{ |
|
#ifdef TOGLES |
|
V_strcat( windowName, " - OpenGLES", sizeof( windowName ) ); |
|
#else |
|
V_strcat( windowName, " - OpenGL", sizeof( windowName ) ); |
|
#endif |
|
} |
|
|
|
#if PIX_ENABLE || defined( PIX_INSTRUMENTATION ) |
|
// PIX_ENABLE/PIX_INSTRUMENTATION is a big slowdown (that should never be checked in, but sometimes is by accident), so add this to the Window title too. |
|
V_strcat( windowName, " - PIX_ENABLE", sizeof( windowName ) ); |
|
#endif |
|
|
|
const char *p = CommandLine()->ParmValue( "-window_name_suffix", "" ); |
|
if ( p && V_strlen( p ) ) |
|
{ |
|
V_strcat( windowName, " - ", sizeof( windowName ) ); |
|
V_strcat( windowName, p, sizeof( windowName ) ); |
|
} |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
#ifndef SWDS |
|
if ( IsPC() ) |
|
{ |
|
if ( !LoadUnicode() ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
#if !defined( _X360 ) |
|
WNDCLASSW wc; |
|
#else |
|
WNDCLASS wc; |
|
#endif |
|
memset( &wc, 0, sizeof( wc ) ); |
|
|
|
wc.style = CS_OWNDC | CS_DBLCLKS; |
|
wc.lpfnWndProc = static_cast<WNDPROC>(CallDefaultWindowProc); |
|
wc.hInstance = m_hInstance; |
|
wc.lpszClassName = CLASSNAME; |
|
|
|
// find the icon file in the filesystem |
|
if ( IsPC() ) |
|
{ |
|
char localPath[ MAX_PATH ]; |
|
if ( g_pFileSystem->GetLocalPath( "resource/game.ico", localPath, sizeof(localPath) ) ) |
|
{ |
|
g_pFileSystem->GetLocalCopy( localPath ); |
|
wc.hIcon = (HICON)::LoadImage(NULL, localPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE); |
|
} |
|
else |
|
{ |
|
wc.hIcon = (HICON)LoadIcon( GetModuleHandle( 0 ), MAKEINTRESOURCE( DEFAULT_EXE_ICON ) ); |
|
} |
|
} |
|
|
|
|
|
#ifndef SWDS |
|
char const *pszGameType = modinfo->GetString( "type" ); |
|
if ( pszGameType && Q_stristr( pszGameType, "multiplayer" ) ) |
|
DoSomeSocketStuffInOrderToGetZoneAlarmToNoticeUs(); |
|
#endif |
|
|
|
wchar_t uc[512]; |
|
if ( IsPC() ) |
|
{ |
|
::MultiByteToWideChar(CP_UTF8, 0, windowName, -1, uc, sizeof( uc ) / sizeof(wchar_t)); |
|
} |
|
|
|
modinfo->deleteThis(); |
|
modinfo = NULL; |
|
// Oops, we didn't clean up the class registration from last cycle which |
|
// might mean that the wndproc pointer is bogus |
|
#ifndef _X360 |
|
unicode->UnregisterClassW( CLASSNAME, m_hInstance ); |
|
// Register it again |
|
unicode->RegisterClassW( &wc ); |
|
#else |
|
RegisterClass( &wc ); |
|
#endif |
|
|
|
// Note, it's hidden |
|
DWORD style = WS_POPUP | WS_CLIPSIBLINGS; |
|
|
|
// Give it a frame if we want a border |
|
if ( videomode->IsWindowedMode() ) |
|
{ |
|
if( !CommandLine()->FindParm( "-noborder" ) ) |
|
{ |
|
style |= WS_OVERLAPPEDWINDOW; |
|
style &= ~WS_THICKFRAME; |
|
} |
|
} |
|
|
|
// Never a max box |
|
style &= ~WS_MAXIMIZEBOX; |
|
|
|
int w, h; |
|
|
|
// Create a full screen size window by default, it'll get resized later anyway |
|
w = GetSystemMetrics( SM_CXSCREEN ); |
|
h = GetSystemMetrics( SM_CYSCREEN ); |
|
|
|
// Create the window |
|
DWORD exFlags = 0; |
|
if ( g_bTextMode ) |
|
{ |
|
style &= ~WS_VISIBLE; |
|
exFlags |= WS_EX_TOOLWINDOW; // So it doesn't show up in the taskbar. |
|
} |
|
|
|
#if !defined( _X360 ) |
|
HWND hwnd = unicode->CreateWindowExW( exFlags, CLASSNAME, uc, style, |
|
0, 0, w, h, NULL, NULL, m_hInstance, NULL ); |
|
// NOTE: On some cards, CreateWindowExW slams the FPU control word |
|
SetupFPUControlWord(); |
|
#else |
|
HWND hwnd = CreateWindowEx( exFlags, CLASSNAME, windowName, style, |
|
0, 0, w, h, NULL, NULL, m_hInstance, NULL ); |
|
#endif |
|
|
|
if ( !hwnd ) |
|
{ |
|
Error( "Fatal Error: Unable to create game window!" ); |
|
return false; |
|
} |
|
|
|
SetMainWindow( hwnd ); |
|
|
|
AttachToWindow( ); |
|
return true; |
|
#else |
|
return true; |
|
#endif |
|
#elif defined( USE_SDL ) |
|
bool windowed = videomode->IsWindowedMode(); |
|
|
|
modinfo->deleteThis(); |
|
modinfo = NULL; |
|
|
|
if ( !g_pLauncherMgr->CreateGameWindow( windowName, windowed, 0, 0 ) ) |
|
{ |
|
Error( "Fatal Error: Unable to create game window!" ); |
|
return false; |
|
} |
|
|
|
char localPath[ MAX_PATH ]; |
|
if ( g_pFileSystem->GetLocalPath( "resource/game-icon.bmp", localPath, sizeof(localPath) ) ) |
|
{ |
|
g_pFileSystem->GetLocalCopy( localPath ); |
|
g_pLauncherMgr->SetApplicationIcon( localPath ); |
|
} |
|
|
|
SetMainWindow( (SDL_Window*)g_pLauncherMgr->GetWindowRef() ); |
|
|
|
AttachToWindow( ); |
|
return true; |
|
#else |
|
#error |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Destroys the game window |
|
//----------------------------------------------------------------------------- |
|
void CGame::DestroyGameWindow() |
|
{ |
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr->DestroyGameWindow(); |
|
#else |
|
#ifndef DEDICATED |
|
// Destroy all things created when the window was created |
|
if ( !m_bExternallySuppliedWindow ) |
|
{ |
|
DetachFromWindow( ); |
|
|
|
if ( m_hWindow ) |
|
{ |
|
DestroyWindow( m_hWindow ); |
|
m_hWindow = (HWND)0; |
|
} |
|
|
|
#if !defined( _X360 ) |
|
unicode->UnregisterClassW( CLASSNAME, m_hInstance ); |
|
UnloadUnicode(); |
|
#else |
|
UnregisterClass( CLASSNAME, m_hInstance ); |
|
#endif |
|
} |
|
else |
|
{ |
|
m_hWindow = (HWND)0; |
|
m_bExternallySuppliedWindow = false; |
|
} |
|
#endif // !defined( SWDS ) |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// This is used in edit mode to specify a particular game window (created by hammer) |
|
//----------------------------------------------------------------------------- |
|
void CGame::SetGameWindow( void *hWnd ) |
|
{ |
|
m_bExternallySuppliedWindow = true; |
|
#if defined( USE_SDL ) |
|
SDL_RaiseWindow( (SDL_Window *)hWnd ); |
|
#else |
|
SetMainWindow( (HWND)hWnd ); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CGame::AttachToWindow() |
|
{ |
|
#if defined( WIN32 ) |
|
if ( !m_hWindow ) |
|
return; |
|
#if !defined( USE_SDL ) |
|
m_ChainedWindowProc = (WNDPROC)GetWindowLongPtrW( m_hWindow, GWLP_WNDPROC ); |
|
SetWindowLongPtrW( m_hWindow, GWLP_WNDPROC, (LONG_PTR)HLEngineWindowProc ); |
|
#endif |
|
#endif // WIN32 |
|
|
|
if ( g_pInputSystem ) |
|
{ |
|
// Attach the input system window proc |
|
#if defined( WIN32 ) |
|
g_pInputSystem->AttachToWindow( (void *)m_hWindow ); |
|
#else |
|
g_pInputSystem->AttachToWindow( (void *)m_pSDLWindow ); |
|
#endif |
|
g_pInputSystem->EnableInput( true ); |
|
g_pInputSystem->EnableMessagePump( false ); |
|
} |
|
|
|
if ( g_pMatSystemSurface ) |
|
{ |
|
// Attach the vgui matsurface window proc |
|
#if defined( WIN32 ) |
|
g_pMatSystemSurface->AttachToWindow( (void *)m_hWindow, true ); |
|
#else |
|
g_pMatSystemSurface->AttachToWindow( (void *)m_pSDLWindow, true ); |
|
#endif |
|
g_pMatSystemSurface->EnableWindowsMessages( true ); |
|
} |
|
} |
|
|
|
void CGame::DetachFromWindow() |
|
{ |
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
if ( !m_hWindow || !m_ChainedWindowProc ) |
|
{ |
|
m_ChainedWindowProc = NULL; |
|
return; |
|
} |
|
#endif |
|
|
|
if ( g_pMatSystemSurface ) |
|
{ |
|
// Detach the vgui matsurface window proc |
|
g_pMatSystemSurface->AttachToWindow( NULL ); |
|
} |
|
|
|
if ( g_pInputSystem ) |
|
{ |
|
// Detach the input system window proc |
|
g_pInputSystem->EnableInput( false ); |
|
g_pInputSystem->DetachFromWindow( ); |
|
} |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
Assert( (WNDPROC)GetWindowLongPtrW( m_hWindow, GWLP_WNDPROC ) == HLEngineWindowProc ); |
|
SetWindowLongPtrW( m_hWindow, GWLP_WNDPROC, (LONG_PTR)m_ChainedWindowProc ); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// This is used in edit mode to override the default wnd proc associated w/ |
|
// the game window specified in SetGameWindow. |
|
//----------------------------------------------------------------------------- |
|
bool CGame::InputAttachToGameWindow() |
|
{ |
|
// We can't use this feature unless we didn't control the creation of the window |
|
if ( !m_bExternallySuppliedWindow ) |
|
return true; |
|
|
|
AttachToWindow(); |
|
|
|
#ifndef DEDICATED |
|
vgui::surface()->OnScreenSizeChanged( videomode->GetModeStereoWidth(), videomode->GetModeStereoHeight() ); |
|
#endif |
|
|
|
// We don't get WM_ACTIVATEAPP messages in this case; simulate one. |
|
AppActivate( true ); |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
// Capture + hide the mouse |
|
SetCapture( m_hWindow ); |
|
#elif defined( USE_SDL ) |
|
Assert( !"Impl me" ); |
|
return false; |
|
#else |
|
#error |
|
#endif |
|
return true; |
|
} |
|
|
|
void CGame::InputDetachFromGameWindow() |
|
{ |
|
// We can't use this feature unless we didn't control the creation of the window |
|
if ( !m_bExternallySuppliedWindow ) |
|
return; |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
if ( !m_ChainedWindowProc ) |
|
return; |
|
|
|
// Release + show the mouse |
|
ReleaseCapture(); |
|
#elif defined( USE_SDL ) |
|
Assert( !"Impl me" ); |
|
#else |
|
#error "have no idea what OS we are building for" |
|
#endif |
|
|
|
// We don't get WM_ACTIVATEAPP messages in this case; simulate one. |
|
AppActivate( false ); |
|
|
|
DetachFromWindow(); |
|
} |
|
|
|
void CGame::PlayStartupVideos( void ) |
|
{ |
|
if ( IsX360() || Plat_IsInBenchmarkMode() ) |
|
return; |
|
|
|
#ifndef SWDS |
|
|
|
// Wait for the mode to change and stabilized |
|
// FIXME: There's really no way to know when this is completed, so we have to guess a time that will mostly be correct |
|
if ( videomode->IsWindowedMode() == false ) |
|
{ |
|
Sys_Sleep( 1000 ); |
|
} |
|
|
|
bool bEndGame = CommandLine()->CheckParm( "-endgamevid" ); |
|
bool bRecap = CommandLine()->CheckParm( "-recapvid" ); // FIXME: This is a temp addition until the movie playback is centralized -- jdw |
|
|
|
bool bNeedHealthWarning = false; |
|
|
|
const char *HealthFile = "media/HealthWarning.txt"; |
|
|
|
FileHandle_t hFile; |
|
|
|
COM_OpenFile( HealthFile, &hFile ); |
|
|
|
//There is no access to steam at this point so we are checking for the presence of an empty file that will only exist in the chinese depot |
|
if ( hFile != FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
bNeedHealthWarning = true; |
|
COM_CloseFile( hFile ); |
|
} |
|
|
|
if (!bNeedHealthWarning && !bEndGame && !bRecap && (CommandLine()->CheckParm("-dev") || CommandLine()->CheckParm("-novid") || CommandLine()->CheckParm("-allowdebug"))) |
|
return; |
|
|
|
const char *pszFile = "media/StartupVids.txt"; |
|
if ( bEndGame ) |
|
{ |
|
// Don't go back into the map that triggered this. |
|
CommandLine()->RemoveParm( "+map" ); |
|
CommandLine()->RemoveParm( "+load" ); |
|
|
|
pszFile = "media/EndGameVids.txt"; |
|
} |
|
else if ( bRecap ) |
|
{ |
|
pszFile = "media/RecapVids.txt"; |
|
} |
|
|
|
int vidFileLength; |
|
|
|
// have to use the malloc memory allocation option in COM_LoadFile since the memory system isn't set up at this point. |
|
const char *buffer = (char *) COM_LoadFile( pszFile, 5, &vidFileLength ); |
|
|
|
if ((buffer == NULL) || (vidFileLength == 0)) |
|
{ |
|
return; |
|
} |
|
|
|
// hide cursor while playing videos |
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr->SetMouseVisible( false ); |
|
#elif defined( WIN32 ) |
|
::ShowCursor( FALSE ); |
|
#endif |
|
|
|
#if defined( LINUX ) |
|
extern void VAudioInit(); |
|
VAudioInit(); |
|
Audio_CreateSDLAudioDevice(); |
|
#endif |
|
|
|
const char *start = buffer; |
|
|
|
while( true ) |
|
{ |
|
start = COM_Parse(start); |
|
if ( Q_strlen( com_token ) <= 0 ) |
|
{ |
|
break; |
|
} |
|
|
|
// get the path to the media file and play it. |
|
char localPath[MAX_PATH]; |
|
|
|
g_pFileSystem->GetLocalPath( com_token, localPath, sizeof(localPath) ); |
|
|
|
PlayVideoAndWait( localPath, bNeedHealthWarning ); |
|
localPath[0] = 0; // just to make sure we don't play the same avi file twice in the case that one movie is there but another isn't. |
|
} |
|
|
|
// show cursor again |
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr->SetMouseVisible( true ); |
|
#elif defined( WIN32 ) |
|
::ShowCursor( TRUE ); |
|
#endif |
|
|
|
// call free on the buffer since the buffer was malloc'd in COM_LoadFile |
|
free( (void *)buffer ); |
|
|
|
#endif // SWDS |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Plays a video until the video completes or ESC is pressed |
|
// Input : *filename - Name of the file (relative to the filesystem) |
|
//----------------------------------------------------------------------------- |
|
void CGame::PlayVideoAndWait( const char *filename, bool bNeedHealthWarning ) |
|
{ |
|
// do we have a filename and a video system, and not on a console? |
|
if ( !filename || !filename[0] || g_pVideo == NULL ) |
|
return; |
|
|
|
// is it the valve logo file? |
|
bool bIsValveLogo = ( Q_strstr( filename, "valve.") != NULL ); |
|
|
|
//Chinese health messages appears for 11 seconds, so we force a minimum delay time for those |
|
float forcedMinTime = ( bIsValveLogo && bNeedHealthWarning ) ? 11.0f : -1.0f; |
|
|
|
#if defined( WIN32 ) && !defined ( _X360 ) && !defined( USE_SDL ) |
|
// Black out the back of the screen once at the beginning of each video (since we're not scaling to fit) |
|
HDC dc = ::GetDC( m_hWindow ); |
|
|
|
RECT rect; |
|
rect.top = 0; |
|
rect.bottom = m_height; |
|
rect.left = 0; |
|
rect.right = m_width; |
|
|
|
HBRUSH hBlackBrush = (HBRUSH) ::GetStockObject( BLACK_BRUSH ); |
|
::SetViewportOrgEx( dc, 0, 0, NULL ); |
|
::FillRect( dc, &rect, hBlackBrush ); |
|
::ReleaseDC( (HWND) GetMainWindow(), dc ); |
|
|
|
#else |
|
// need OS specific way to clear screen |
|
|
|
#endif |
|
|
|
VideoResult_t status = g_pVideo->PlayVideoFileFullScreen( filename, "GAME", GetMainWindowPlatformSpecificHandle (), |
|
m_width, m_height, m_iDesktopWidth, m_iDesktopHeight, videomode->IsWindowedMode(), |
|
forcedMinTime, VideoPlaybackFlags::DEFAULT_FULLSCREEN_OPTIONS | VideoPlaybackFlags::FILL_WINDOW ); |
|
|
|
// Everything ok? |
|
if ( status == VideoResult::SUCCESS ) |
|
{ |
|
return; |
|
} |
|
|
|
// We don't worry if it could not find something to could play |
|
if ( status == VideoResult::VIDEO_FILE_NOT_FOUND ) |
|
{ |
|
return; |
|
} |
|
|
|
// Debug Builds, we want an error looked at by a developer, Release builds just send a message to the spew |
|
#ifdef _DEBUG |
|
Error( "Error %d occurred attempting to play video file %s\n", (int) status, filename ); |
|
#else |
|
Msg( "Error %d occurred attempting to play video file %s\n", (int) status, filename ); |
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CGame::CGame() |
|
{ |
|
#if defined( USE_SDL ) |
|
m_pSDLWindow = 0; |
|
#endif |
|
|
|
#if defined( WIN32 ) |
|
#if !defined( USE_SDL ) |
|
unicode = NULL; |
|
m_hUnicodeModule = NULL; |
|
m_hInstance = 0; |
|
m_ChainedWindowProc = NULL; |
|
#endif |
|
|
|
m_hWindow = 0; |
|
#endif |
|
|
|
m_x = m_y = 0; |
|
m_width = m_height = 0; |
|
m_bActiveApp = false; |
|
m_bCanPostActivateEvents = true; |
|
m_iDesktopWidth = 0; |
|
m_iDesktopHeight = 0; |
|
m_iDesktopRefreshRate = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CGame::~CGame() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CGame::Init( void *pvInstance ) |
|
{ |
|
m_bExternallySuppliedWindow = false; |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
OSVERSIONINFO vinfo; |
|
vinfo.dwOSVersionInfoSize = sizeof(vinfo); |
|
|
|
if ( !GetVersionEx( &vinfo ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32s ) |
|
{ |
|
return false; |
|
} |
|
|
|
m_hInstance = (HINSTANCE)pvInstance; |
|
#endif |
|
return true; |
|
} |
|
|
|
|
|
bool CGame::Shutdown( void ) |
|
{ |
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
m_hInstance = 0; |
|
#endif |
|
return true; |
|
} |
|
|
|
bool CGame::LoadUnicode( void ) |
|
{ |
|
#ifdef WIN32 |
|
m_hUnicodeModule = Sys_LoadModule( "unicode" ); |
|
if ( !m_hUnicodeModule ) |
|
{ |
|
Error( "Unable to load unicode.dll" ); |
|
return false; |
|
} |
|
|
|
CreateInterfaceFn factory = Sys_GetFactory( m_hUnicodeModule ); |
|
if ( !factory ) |
|
{ |
|
Error( "Unable to get factory from unicode.dll" ); |
|
return false; |
|
} |
|
|
|
unicode = ( IUnicodeWindows * )factory( VENGINE_UNICODEINTERFACE_VERSION, NULL ); |
|
if ( !unicode ) |
|
{ |
|
Error( "Unable to load interface '%s' from unicode.dll", VENGINE_UNICODEINTERFACE_VERSION ); |
|
return false; |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
void CGame::UnloadUnicode() |
|
{ |
|
#ifdef WIN32 |
|
unicode = NULL; |
|
|
|
if ( m_hUnicodeModule ) |
|
{ |
|
Sys_UnloadModule( m_hUnicodeModule ); |
|
m_hUnicodeModule = NULL; |
|
} |
|
#endif |
|
} |
|
|
|
void *CGame::GetMainWindow( void ) |
|
{ |
|
#ifdef USE_SDL |
|
return (void*)m_pSDLWindow; |
|
#else |
|
return GetMainWindowPlatformSpecificHandle(); |
|
#endif |
|
} |
|
|
|
void *CGame::GetMainDeviceWindow( void ) |
|
{ |
|
#if defined( DX_TO_GL_ABSTRACTION ) && defined( USE_SDL ) |
|
return (void*)m_pSDLWindow; |
|
#else |
|
return (void*)m_hWindow; |
|
#endif |
|
} |
|
|
|
void *CGame::GetMainWindowPlatformSpecificHandle( void ) |
|
{ |
|
#ifdef WIN32 |
|
return (void*)m_hWindow; |
|
#else |
|
SDL_SysWMinfo pInfo; |
|
SDL_VERSION( &pInfo.version ); |
|
if ( !SDL_GetWindowWMInfo( (SDL_Window*)m_pSDLWindow, &pInfo ) ) |
|
{ |
|
Error( "Fatal Error: Unable to get window info from SDL." ); |
|
return NULL; |
|
} |
|
|
|
#ifdef OSX |
|
id nsWindow = (id)pInfo.info.cocoa.window; |
|
SEL selector = sel_registerName("windowRef"); |
|
id windowRef = ((id(*)(id, SEL))objc_msgSend)( nsWindow, selector ); |
|
return windowRef; |
|
#else |
|
// Not used on Linux. |
|
return NULL; |
|
#endif |
|
|
|
#endif // !WIN32 |
|
} |
|
|
|
|
|
void** CGame::GetMainWindowAddress( void ) |
|
{ |
|
#ifdef WIN32 |
|
return (void**)&m_hWindow; |
|
#else |
|
return NULL; |
|
#endif |
|
} |
|
|
|
void CGame::GetDesktopInfo( int &width, int &height, int &refreshrate ) |
|
{ |
|
#if defined( USE_SDL ) |
|
|
|
width = 640; |
|
height = 480; |
|
refreshrate = 0; |
|
|
|
// Go through all the displays and return the size of the largest. |
|
for( int i = 0; i < SDL_GetNumVideoDisplays(); i++ ) |
|
{ |
|
SDL_Rect rect; |
|
|
|
if ( !SDL_GetDisplayBounds( i, &rect ) ) |
|
{ |
|
if ( ( rect.w > width ) || ( ( rect.w == width ) && ( rect.h > height ) ) ) |
|
{ |
|
width = rect.w; |
|
height = rect.h; |
|
} |
|
} |
|
} |
|
#else |
|
// order of initialization means that this might get called early. In that case go ahead and grab the current |
|
// screen window and setup based on that. |
|
// we need to do this when initializing the base list of video modes, for example |
|
if ( m_iDesktopWidth == 0 ) |
|
{ |
|
HDC dc = ::GetDC( NULL ); |
|
width = ::GetDeviceCaps(dc, HORZRES); |
|
height = ::GetDeviceCaps(dc, VERTRES); |
|
refreshrate = ::GetDeviceCaps(dc, VREFRESH); |
|
::ReleaseDC( NULL, dc ); |
|
return; |
|
} |
|
width = m_iDesktopWidth; |
|
height = m_iDesktopHeight; |
|
refreshrate = m_iDesktopRefreshRate; |
|
#endif |
|
} |
|
|
|
void CGame::UpdateDesktopInformation( ) |
|
{ |
|
#if defined( USE_SDL ) |
|
// Get the size of the display we will be displayed fullscreen on. |
|
static ConVarRef sdl_displayindex( "sdl_displayindex" ); |
|
int displayIndex = sdl_displayindex.IsValid() ? sdl_displayindex.GetInt() : 0; |
|
|
|
SDL_DisplayMode mode; |
|
SDL_GetDesktopDisplayMode( displayIndex, &mode ); |
|
|
|
m_iDesktopWidth = mode.w; |
|
m_iDesktopHeight = mode.h; |
|
m_iDesktopRefreshRate = mode.refresh_rate; |
|
#else |
|
HDC dc = ::GetDC( m_hWindow ); |
|
m_iDesktopWidth = ::GetDeviceCaps(dc, HORZRES); |
|
m_iDesktopHeight = ::GetDeviceCaps(dc, VERTRES); |
|
m_iDesktopRefreshRate = ::GetDeviceCaps(dc, VREFRESH); |
|
::ReleaseDC( m_hWindow, dc ); |
|
#endif |
|
} |
|
|
|
#ifdef WIN32 |
|
void CGame::UpdateDesktopInformation( WPARAM wParam, LPARAM lParam ) |
|
{ |
|
m_iDesktopWidth = LOWORD( lParam ); |
|
m_iDesktopHeight = HIWORD( lParam ); |
|
} |
|
#endif |
|
|
|
#ifndef USE_SDL |
|
void CGame::SetMainWindow( HWND window ) |
|
{ |
|
m_hWindow = window; |
|
|
|
// update our desktop info (since the results will change if we are going to fullscreen mode) |
|
if ( !m_iDesktopWidth || !m_iDesktopHeight ) |
|
{ |
|
UpdateDesktopInformation(); |
|
} |
|
} |
|
#else |
|
void CGame::SetMainWindow( SDL_Window* window ) |
|
{ |
|
#if defined( WIN32 ) |
|
// For D3D, we need to access the underlying HWND of the SDL_Window. |
|
// We also can't do this in GetMainDeviceWindow and just use that, because for some reason |
|
// people use GetMainWindowAddress and store that pointer to our member. |
|
SDL_SysWMinfo pInfo; |
|
SDL_VERSION( &pInfo.version ); |
|
if ( !SDL_GetWindowWMInfo( (SDL_Window*)g_pLauncherMgr->GetWindowRef(), &pInfo ) ) |
|
{ |
|
Error( "Fatal Error: Unable to get window info from SDL." ); |
|
return; |
|
} |
|
|
|
m_hWindow = pInfo.info.win.window; |
|
#endif |
|
|
|
m_pSDLWindow = window; |
|
|
|
// update our desktop info (since the results will change if we are going to fullscreen mode) |
|
if ( !m_iDesktopWidth || !m_iDesktopHeight ) |
|
{ |
|
UpdateDesktopInformation(); |
|
} |
|
} |
|
#endif |
|
|
|
void CGame::SetWindowXY( int x, int y ) |
|
{ |
|
m_x = x; |
|
m_y = y; |
|
} |
|
|
|
void CGame::SetWindowSize( int w, int h ) |
|
{ |
|
m_width = w; |
|
m_height = h; |
|
} |
|
|
|
void CGame::GetWindowRect( int *x, int *y, int *w, int *h ) |
|
{ |
|
if ( x ) |
|
{ |
|
*x = m_x; |
|
} |
|
if ( y ) |
|
{ |
|
*y = m_y; |
|
} |
|
if ( w ) |
|
{ |
|
*w = m_width; |
|
} |
|
if ( h ) |
|
{ |
|
*h = m_height; |
|
} |
|
} |
|
|
|
bool CGame::IsActiveApp( void ) |
|
{ |
|
return m_bActiveApp; |
|
} |
|
|
|
void CGame::SetCanPostActivateEvents( bool bEnabled ) |
|
{ |
|
m_bCanPostActivateEvents = bEnabled; |
|
} |
|
|
|
bool CGame::CanPostActivateEvents() |
|
{ |
|
return m_bCanPostActivateEvents; |
|
} |
|
|
|
void CGame::SetActiveApp( bool active ) |
|
{ |
|
m_bActiveApp = active; |
|
} |
|
|
|
|