convert line ends
[canaan.git] / prj / tech / libsrc / gameshel / wgshelmt.cpp
blob62e5298b822e5003fec7c50e3f246873273e9546
1 ///////////////////////////////////////////////////////////////////////////////
2 // $Source: x:/prj/tech/libsrc/gameshel/RCS/wgshelmt.cpp $
3 // $Author: TOML $
4 // $Date: 1997/09/10 14:01:42 $
5 // $Revision: 1.18 $
6 //
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)
9 //
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.
17 #include <windows.h>
18 #include <lg.h>
19 #include <coremutx.h>
20 #include <comtools.h>
22 #include <dispapi.h>
24 #include <wgshell.h>
25 #include <wgshelmt.h>
27 ///////////////////////////////////////////////////////////////////////////////
29 // CLASS: cMTWinGameShell
32 cMTWinGameShell::cMTWinGameShell(int fFlags, IUnknown * pOuterUnknown)
33 : cWinGameShell(fFlags, pOuterUnknown),
34 m_fMasterBlockedFromDisplay(FALSE),
35 m_fState(kStartup),
36 m_fInGame(FALSE),
37 m_WorkerThreadId(0),
38 m_MasterThreadId(0)
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);
50 AssertInMaster();
51 if (ThreadExists())
53 CallWorker(kGSWTExit);
54 WaitForClose();
57 if (m_hMasterThread)
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
95 if (!m_fInGame)
97 m_fInGame = TRUE;
98 SetPriority(THREAD_PRIORITY_HIGHEST);
100 cWinGameShell::BeginFrame();
103 ////////////////////////////////////////
105 // Message pump
107 // Only called from master, this function handles requests to exit
108 // from the worker
111 STDMETHODIMP_(void) cMTWinGameShell::PumpEvents(int fPumpFlags)
113 AssertInMaster();
115 switch (m_fState)
117 case kStartup:
118 case kActive:
120 cWinGameShell::PumpEvents(fPumpFlags);
121 break;
124 case kClosed:
125 break;
127 default:
128 CriticalMsg("Unknown state");
132 ////////////////////////////////////////
134 // Window procedure
137 long cMTWinGameShell::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
139 HWND hWnd = GetHwnd();
140 long retVal;
142 switch (msg)
144 case WM_NCACTIVATE:
146 if (!wParam && !IsWindowVisible(hWnd))
147 return FALSE;
148 break;
151 case WM_DESTROY:
152 m_fState = kClosed;
153 break;
157 retVal = cWinGameShell::WndProc(msg, wParam, lParam);
159 // We yield our timeslice to feed any starving threads
160 Sleep(0);
162 return retVal;
165 ////////////////////////////////////////
167 void cMTWinGameShell::CreateGameWindow()
169 m_MasterThreadId = GetCurrentThreadId();
171 Create();
172 CallWorker(kGSWTCreateWindow);
173 SetForegroundWindow(GetHwnd());
176 ////////////////////////////////////////
178 void cMTWinGameShell::DestroyGameWindow()
180 AssertInMaster();
181 if (ThreadExists())
183 CallWorker(kGSWTExit);
184 WaitForClose();
186 cWinGameShell::DestroyGameWindow();
188 m_MasterThreadId = 0;
191 ////////////////////////////////////////
193 void cMTWinGameShell::NotifyDisplayOnActivateApp(BOOL fActive)
195 AssertInWorker();
196 if (!fActive)
198 SetThreadPriority(m_hMasterThread, THREAD_PRIORITY_NORMAL);
199 SetPriority(THREAD_PRIORITY_NORMAL);
200 if (OkToBlock())
202 BlockDisplay();
206 cWinGameShell::NotifyDisplayOnActivateApp(fActive);
208 if (fActive)
210 SetThreadPriority(m_hMasterThread, THREAD_PRIORITY_ABOVE_NORMAL);
211 SetPriority(THREAD_PRIORITY_HIGHEST);
212 UnblockDisplay();
216 ////////////////////////////////////////
218 void cMTWinGameShell::OnQuit()
220 AssertInWorker();
221 CriticalMsg("Abnormal termination -- no WM_CLOSE received!");
222 ExitProcess(1);
225 ////////////////////////////////////////
227 void cMTWinGameShell::OnActivateApp(BOOL fActive)
229 AssertInWorker();
231 if (!fActive && OkToBlock())
232 BlockDisplay();
234 cWinGameShell::OnActivateApp(fActive);
236 if (fActive)
237 UnblockDisplay();
240 ////////////////////////////////////////
242 #define kSleepLimit 500
244 DWORD cMTWinGameShell::ThreadProc()
246 m_WorkerThreadId = GetCurrentThreadId();
248 enum eMTGameShellEvent
250 kCallFromMaster,
251 kWindowMessage
254 // Wait for either a call from the master thread, or an item in the queue...
255 DWORD fWaitResult;
256 BOOL fFailedLastWait;
257 HANDLE waitHandles[1];
259 m_fState = kActive;
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);
268 if (gm_fInAssert)
270 // Wait until assertion dialog is dealt with
271 Sleep(50);
272 continue;
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());
282 Sleep(0);
283 fFailedLastWait = TRUE;
284 continue;
286 else
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)
292 MSG msg;
293 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
294 continue;
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())
305 case kGSWTExit:
306 m_fState = kClosed;
307 Reply(S_OK);
308 break;
309 #if 0
310 case kGSWTSuspend:
311 Reply(S_OK);
312 Suspend();
313 break;
314 #endif
316 case kGSWTCreateWindow:
317 cWinGameShell::CreateGameWindow();
318 SetThreadPriority(m_hMasterThread, THREAD_PRIORITY_ABOVE_NORMAL);
319 Reply(S_OK);
320 break;
322 default:
323 CriticalMsg("Unknown call to game shell worker thread");
324 Reply((DWORD)E_FAIL);
325 break;
327 break;
330 // Otherwise, if there's a someting in the message queue...
331 case kWindowMessage:
333 m_pWinDisplayDevice->WaitForMutex();
334 CoreThreadLock();
335 DoPumpEvents();
336 CoreThreadUnlock();
337 m_pWinDisplayDevice->ReleaseMutex();
338 break;
341 default:
342 CriticalMsg("There was nothing to do!");
345 // We yield our timeslice to feed any starving threads
346 Sleep(0);
349 m_WorkerThreadId = 0;
351 return S_OK;
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)
366 case kCritMsgEnter:
367 gm_fInAssert = TRUE;
368 if (gm_pfnPreviousCritMsgNotificationHandler)
369 (*gm_pfnPreviousCritMsgNotificationHandler)(notification);
370 break;
372 case kCritMsgExit:
373 if (gm_pfnPreviousCritMsgNotificationHandler)
374 (*gm_pfnPreviousCritMsgNotificationHandler)(notification);
375 gm_fInAssert = FALSE;
376 break;
378 default:
379 if (gm_pfnPreviousCritMsgNotificationHandler)
380 (*gm_pfnPreviousCritMsgNotificationHandler)(notification);
385 ///////////////////////////////////////////////////////////////////////////////