1 ///////////////////////////////////////////////////////////////////////////////
2 // $Source: x:/prj/tech/libsrc/gameshel/RCS/wgshelmt.cpp $
4 // $Date: 1997/09/10 14:01:42 $
7 // - Because the priority is so high in this thread, it is important
8 // that any code in here be coded with an eye on performance (toml 10-29-96)
10 // - When activation switches away from the app, the shell blocks
11 // the app from using the display. Because Full screen DD, GDI and
12 // windowed DD all have slightly different interactions with
13 // activation change, the blocking code is repeated both in the
14 // pretranslate hook and the window proc hook to cover all cases.
27 ///////////////////////////////////////////////////////////////////////////////
29 // CLASS: cMTWinGameShell
32 cMTWinGameShell::cMTWinGameShell(int fFlags
, IUnknown
* pOuterUnknown
)
33 : cWinGameShell(fFlags
, pOuterUnknown
),
34 m_fMasterBlockedFromDisplay(FALSE
),
40 Verify(DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_hMasterThread
, 0, FALSE
, DUPLICATE_SAME_ACCESS
));
41 SetBlockInactive(FALSE
);
42 gm_pfnPreviousCritMsgNotificationHandler
= CritMsgSetHandler(CritMsgNotificationHandler
);
45 ////////////////////////////////////////
47 cMTWinGameShell::~cMTWinGameShell()
49 CritMsgSetHandler(gm_pfnPreviousCritMsgNotificationHandler
);
53 CallWorker(kGSWTExit
);
58 CloseHandle(m_hMasterThread
);
61 ////////////////////////////////////////
63 inline BOOL
cMTWinGameShell::OkToBlock()
65 return m_fInGame
&& !gm_fInAssert
&& DisplayIsExclusive();
68 ////////////////////////////////////////
70 inline void cMTWinGameShell::BlockDisplay()
72 if (!m_fMasterBlockedFromDisplay
)
74 m_fMasterBlockedFromDisplay
= TRUE
;
75 m_pWinDisplayDevice
->WaitForMutex();
79 ////////////////////////////////////////
81 inline void cMTWinGameShell::UnblockDisplay()
83 if (m_fMasterBlockedFromDisplay
)
85 m_fMasterBlockedFromDisplay
= FALSE
;
86 m_pWinDisplayDevice
->ReleaseMutex();
90 ////////////////////////////////////////
92 STDMETHODIMP_(void) cMTWinGameShell::BeginFrame()
94 // If this is the first frame, set properties of normal operation
98 SetPriority(THREAD_PRIORITY_HIGHEST
);
100 cWinGameShell::BeginFrame();
103 ////////////////////////////////////////
107 // Only called from master, this function handles requests to exit
111 STDMETHODIMP_(void) cMTWinGameShell::PumpEvents(int fPumpFlags
)
120 cWinGameShell::PumpEvents(fPumpFlags
);
128 CriticalMsg("Unknown state");
132 ////////////////////////////////////////
137 long cMTWinGameShell::WndProc(UINT msg
, WPARAM wParam
, LPARAM lParam
)
139 HWND hWnd
= GetHwnd();
146 if (!wParam
&& !IsWindowVisible(hWnd
))
157 retVal
= cWinGameShell::WndProc(msg
, wParam
, lParam
);
159 // We yield our timeslice to feed any starving threads
165 ////////////////////////////////////////
167 void cMTWinGameShell::CreateGameWindow()
169 m_MasterThreadId
= GetCurrentThreadId();
172 CallWorker(kGSWTCreateWindow
);
173 SetForegroundWindow(GetHwnd());
176 ////////////////////////////////////////
178 void cMTWinGameShell::DestroyGameWindow()
183 CallWorker(kGSWTExit
);
186 cWinGameShell::DestroyGameWindow();
188 m_MasterThreadId
= 0;
191 ////////////////////////////////////////
193 void cMTWinGameShell::NotifyDisplayOnActivateApp(BOOL fActive
)
198 SetThreadPriority(m_hMasterThread
, THREAD_PRIORITY_NORMAL
);
199 SetPriority(THREAD_PRIORITY_NORMAL
);
206 cWinGameShell::NotifyDisplayOnActivateApp(fActive
);
210 SetThreadPriority(m_hMasterThread
, THREAD_PRIORITY_ABOVE_NORMAL
);
211 SetPriority(THREAD_PRIORITY_HIGHEST
);
216 ////////////////////////////////////////
218 void cMTWinGameShell::OnQuit()
221 CriticalMsg("Abnormal termination -- no WM_CLOSE received!");
225 ////////////////////////////////////////
227 void cMTWinGameShell::OnActivateApp(BOOL fActive
)
231 if (!fActive
&& OkToBlock())
234 cWinGameShell::OnActivateApp(fActive
);
240 ////////////////////////////////////////
242 #define kSleepLimit 500
244 DWORD
cMTWinGameShell::ThreadProc()
246 m_WorkerThreadId
= GetCurrentThreadId();
248 enum eMTGameShellEvent
254 // Wait for either a call from the master thread, or an item in the queue...
256 BOOL fFailedLastWait
;
257 HANDLE waitHandles
[1];
261 waitHandles
[kCallFromMaster
] = GetCallHandle();
263 while (m_fState
!= kClosed
)
265 // Wait for a message either from the master thread or in the queue...
266 fWaitResult
= MsgWaitForMultipleObjects(1, waitHandles
, FALSE
, kSleepLimit
, QS_ALLINPUT
);
270 // Wait until assertion dialog is dealt with
275 // It apperars MsgWaitForMultipleObjects() fails, seemingly harmlessly, immediately
276 // following a coninuance after a breakpoint is hit. Everything appears fine
277 // on the next iteration. So, what we do is ignore the failure, and blow up
278 // only if the exception repeats on the next wait. (toml 09-23-96)
279 if (fWaitResult
== WAIT_FAILED
)
281 AssertMsg1(!fFailedLastWait
, "Error in multi-threaded game shell message pump: 0x%x", GetLastError());
283 fFailedLastWait
= TRUE
;
287 fFailedLastWait
= FALSE
;
289 // If we hit the sleep limit, peek for a message to inhibit non-critical OS cleanup code from activating
290 if (fWaitResult
== WAIT_TIMEOUT
)
293 PeekMessage(&msg
, NULL
, 0, 0, PM_NOREMOVE
);
297 // Handle the message...
298 switch (fWaitResult
- WAIT_OBJECT_0
)
300 // It's a call from the master thread...
301 case kCallFromMaster
:
303 switch (GetCallParam())
316 case kGSWTCreateWindow
:
317 cWinGameShell::CreateGameWindow();
318 SetThreadPriority(m_hMasterThread
, THREAD_PRIORITY_ABOVE_NORMAL
);
323 CriticalMsg("Unknown call to game shell worker thread");
324 Reply((DWORD
)E_FAIL
);
330 // Otherwise, if there's a someting in the message queue...
333 m_pWinDisplayDevice
->WaitForMutex();
337 m_pWinDisplayDevice
->ReleaseMutex();
342 CriticalMsg("There was nothing to do!");
345 // We yield our timeslice to feed any starving threads
349 m_WorkerThreadId
= 0;
354 ////////////////////////////////////////
356 // Critical message handling -- don't block the display if we're trying to assert
359 BOOL
cMTWinGameShell::gm_fInAssert
;
360 tCritMsgNotificationHandler
cMTWinGameShell::gm_pfnPreviousCritMsgNotificationHandler
;
362 void LGAPI
cMTWinGameShell::CritMsgNotificationHandler(eCritMsgNotification notification
)
364 switch (notification
)
368 if (gm_pfnPreviousCritMsgNotificationHandler
)
369 (*gm_pfnPreviousCritMsgNotificationHandler
)(notification
);
373 if (gm_pfnPreviousCritMsgNotificationHandler
)
374 (*gm_pfnPreviousCritMsgNotificationHandler
)(notification
);
375 gm_fInAssert
= FALSE
;
379 if (gm_pfnPreviousCritMsgNotificationHandler
)
380 (*gm_pfnPreviousCritMsgNotificationHandler
)(notification
);
385 ///////////////////////////////////////////////////////////////////////////////