update fullscreen, alt-tab, error handling

This commit is contained in:
cxgeorge 2001-07-24 23:27:52 +00:00
parent 13918eb201
commit bfc5300e57
4 changed files with 443 additions and 292 deletions

View File

@ -306,6 +306,9 @@ init_dx( LPDIRECTDRAW7 context,
_d3d = pD3D;
_d3dDevice = pDevice;
_view_rect = viewrect;
_last_testcooplevel_result = S_OK;
HRESULT hr;
if(dx_show_fps_meter) {
@ -500,8 +503,8 @@ init_dx( LPDIRECTDRAW7 context,
_d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFP_POINT);
_d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE);
_d3dDevice->SetTextureStageState(0, D3DTSS_MAXANISOTROPY,_CurTexAnisoDegree);
_d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU));
_d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV));
_d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU));
_d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV));
ta->issue(this); // no curtextcontext, this does nothing. dx should already be properly inited above anyway
@ -567,8 +570,7 @@ clear(const RenderBuffer &buffer) {
HRESULT hr = _d3dDevice->Clear(0, NULL, flags, clear_colr,
(D3DVALUE) _depth_clear_value, (DWORD)_stencil_clear_value);
if (hr != DD_OK)
dxgsg_cat.error()
<< "dxGSG::clear_buffer failed: Clear returned " << ConvD3DErrorToString(hr) << endl;
dxgsg_cat.error() << "clear_buffer failed: Clear returned " << ConvD3DErrorToString(hr) << endl;
/* The following line will cause the background to always clear to a medium red
_color_clear_value[0] = .5;
/* The following lines will cause the background color to cycle from black to red.
@ -653,15 +655,14 @@ prepare_display_region() {
}
}
#if 0
////////////////////////////////////////////////////////////////////
// Function: set_clipper
// Access:
// Description: Useless in DX at the present time
////////////////////////////////////////////////////////////////////
void DXGraphicsStateGuardian::set_clipper(RECT cliprect) {
#if 0
LPDIRECTDRAWCLIPPER Clipper;
HRESULT result;
@ -699,8 +700,8 @@ void DXGraphicsStateGuardian::set_clipper(RECT cliprect) {
}
free(rgn_data);
DeleteObject(hrgn);
#endif
}
#endif
////////////////////////////////////////////////////////////////////
// Function: DXGraphicsStateGuardian::render_frame
@ -711,9 +712,10 @@ void DXGraphicsStateGuardian::set_clipper(RECT cliprect) {
////////////////////////////////////////////////////////////////////
void DXGraphicsStateGuardian::
render_frame(const AllAttributesWrapper &initial_state) {
if (!_dx_ready) return;
if (!_dx_ready)
return;
_win->begin_frame();
_win->begin_frame();
_lighting_enabled_this_frame = false;
#ifdef DO_PSTATS
@ -730,7 +732,20 @@ render_frame(const AllAttributesWrapper &initial_state) {
set_state(state, false);
#endif
_d3dDevice->BeginScene();
HRESULT hr = _d3dDevice->BeginScene();
if(FAILED(hr)) {
if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) {
if(dxgsg_cat.is_debug())
dxgsg_cat.debug() << "BeginScene returns " << ConvD3DErrorToString(hr) << endl;
CheckCooperativeLevel();
} else {
dxgsg_cat.error() << "BeginScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
return;
}
/* _d3dDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &matIdentity); */
@ -780,9 +795,9 @@ render_frame(const AllAttributesWrapper &initial_state) {
// Now we're done with the frame processing. Clean up.
_d3dDevice->EndScene(); // FPS meter drawing MUST occur after EndScene, since it uses GDI
hr = _d3dDevice->EndScene(); // FPS meter drawing MUST occur after EndScene, since it uses GDI
if (_lighting_enabled_this_frame) {
if (_lighting_enabled_this_frame) {
// Let's turn off all the lights we had on, and clear the light
// cache--to force the lights to be reissued next frame, in case
// their parameters or positions have changed between frames.
@ -803,9 +818,22 @@ render_frame(const AllAttributesWrapper &initial_state) {
// ideal--there may be a better way. Maybe if the lights were just
// more aware of whether their parameters or positions have changed
// at all?
}
}
if(dx_show_fps_meter) {
if(FAILED(hr)) {
if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) {
if(dxgsg_cat.is_debug())
dxgsg_cat.debug() << "EndScene returns " << ConvD3DErrorToString(hr) << endl;
CheckCooperativeLevel();
} else {
dxgsg_cat.error() << "EndScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
return;
}
if(dx_show_fps_meter) {
DO_PSTATS_STUFF(PStatTimer timer(_win->_show_fps_pcollector));
DWORD now = timeGetTime(); // this is win32 fn
@ -1056,7 +1084,7 @@ render_subgraph(RenderTraverser *traverser,
// We infer the modelview matrix by doing a wrt on the projection
// node.
LMatrix4f modelview_mat;
get_rel_mat(subgraph, _current_projection_node, modelview_mat); // reversed from GL
get_rel_mat(subgraph, _current_projection_node, modelview_mat); //needs reversal from glgsg, probably due D3D LH coordsys
// get_rel_mat(_current_projection_node, subgraph, modelview_mat);
if (_coordinate_system != CS_yup_left) {
@ -1142,7 +1170,7 @@ void INLINE TestDrawPrimFailure(DP_Type dptype,HRESULT hr,LPDIRECTDRAW7 pDD,DWOR
// loss of exclusive mode is not a real DrawPrim problem
HRESULT testcooplvl_hr = pDD->TestCooperativeLevel();
if((testcooplvl_hr != DDERR_NOEXCLUSIVEMODE)||(testcooplvl_hr != DDERR_EXCLUSIVEMODEALREADYSET)) {
dxgsg_cat.fatal() << ((dptype==DrawPrimStrided) ? "DrawPrimStrided" : "DrawPrim") << "failed: result = " << ConvD3DErrorToString(hr) << endl;
dxgsg_cat.fatal() << ((dptype==DrawPrimStrided) ? "DrawPrimStrided" : "DrawPrim") << " failed: result = " << ConvD3DErrorToString(hr) << endl;
}
exit(1);
}
@ -5488,25 +5516,27 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) {
HRESULT hr;
if (FAILED(hr = _pDD->CreateSurface( &ddsd, &_pri, NULL ))) {
dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl;
dxgsg_cat.fatal() << "resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
// Create a clipper object which handles all our clipping for cases when
// our window is partially obscured by other windows.
LPDIRECTDRAWCLIPPER Clipper;
if (FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) {
dxgsg_cat.fatal()
<< "dxgsg - CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
if(!dx_full_screen) {
// Create a clipper object which handles all our clipping for cases when
// our window is partially obscured by other windows.
LPDIRECTDRAWCLIPPER Clipper;
if (FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) {
dxgsg_cat.fatal()
<< "CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
// Associate the clipper with our window. Note that, afterwards, the
// clipper is internally referenced by the primary surface, so it is safe
// to release our local reference to it.
Clipper->SetHWnd( 0, mwindow );
_pri->SetClipper( Clipper );
Clipper->Release();
}
// Associate the clipper with our window. Note that, afterwards, the
// clipper is internally referenced by the primary surface, so it is safe
// to release our local reference to it.
Clipper->SetHWnd( 0, mwindow );
_pri->SetClipper( Clipper );
Clipper->Release();
// Recreate the backbuffer. (might want to handle failure due to running out of video memory)
@ -5516,7 +5546,7 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) {
PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize backbuffer surf");
if (FAILED(hr = _pDD->CreateSurface( &ddsd_back, &_back, NULL ))) {
dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl;
dxgsg_cat.fatal() << "resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
@ -5524,20 +5554,20 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) {
// Recreate and attach a z-buffer.
if (FAILED(hr = _pDD->CreateSurface( &ddsd_zbuf, &_zbuf, NULL ))) {
dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl;
dxgsg_cat.fatal() << "resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
// Attach the z-buffer to the back buffer.
if ((hr = _back->AddAttachedSurface( _zbuf ) ) != DD_OK) {
dxgsg_cat.fatal()
<< "DXGraphicsStateGuardian::resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl;
<< "resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
if ((hr = _d3dDevice->SetRenderTarget(_back,0x0) ) != DD_OK) {
dxgsg_cat.fatal()
<< "DXGraphicsStateGuardian::resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl;
<< "resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
@ -5546,7 +5576,7 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) {
hr = _d3dDevice->SetViewport( &vp );
if (hr != DD_OK) {
dxgsg_cat.fatal()
<< "DXGraphicsStateGuardian:: SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl;
<< "SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
}
@ -5571,13 +5601,15 @@ HRESULT DXGraphicsStateGuardian::RestoreAllVideoSurfaces(void) {
// note: could go through and just restore surfs that return IsLost() true
// apparently that isnt as reliable w/some drivers tho
if (FAILED(hr = _pDD->RestoreAllSurfaces() )) {
dxgsg_cat.fatal()
<< "DXGraphicsStateGuardian:: RestoreAllSurfs failed : result = " << ConvD3DErrorToString(hr) << endl;
return hr;
dxgsg_cat.fatal() << "RestoreAllSurfs failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
// cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
traverse_prepared_textures(refill_tex_callback,this);
if(dxgsg_cat.is_debug())
dxgsg_cat.debug() << "restore and refill of video surfaces complete...\n";
return S_OK;
}
@ -5627,56 +5659,15 @@ void DXGraphicsStateGuardian::show_full_screen_frame(void) {
// waiting for vsync?
hr = _pri->Flip( NULL, dwFlipFlags);
if(hr == DDERR_SURFACELOST || hr == DDERR_SURFACEBUSY) {
//full screen app has been switched away
HRESULT hr;
// TestCooperativeLevel returns DD_OK: If the current mode is same as the one which the App set.
// The following error is returned only for exclusivemode apps.
// DDERR_NOEXCLUSIVEMODE: Some other app took exclusive mode.
hr = _pDD->TestCooperativeLevel();
while(hr == DDERR_NOEXCLUSIVEMODE) {
// This means that mode changes had taken place, surfaces
// were lost but still we are in the original mode, so we
// simply restore all surfaces and keep going.
#ifdef _DEBUG
if(dxgsg_cat.is_spam() && _dx_ready) {
dxgsg_cat.spam() << "DXGraphicsStateGuardian:: no exclusive mode, waiting...\n";
}
#endif
_dx_ready = FALSE;
_win->update(); // sleep in here, and check for window events
hr = _pDD->TestCooperativeLevel();
if(FAILED(hr)) {
if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) {
CheckCooperativeLevel();
} else {
dxgsg_cat.error() << "show_frame() - Flip failed w/unexpected error code: " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
if(FAILED(hr)) {
dxgsg_cat.error() << "DXGraphicsStateGuardian::unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
return;
}
#ifdef _DEBUG
dxgsg_cat.debug() << "DXGraphicsStateGuardian:: regained exclusive mode, refilling surfs...\n";
#endif
RestoreAllVideoSurfaces();
#ifdef _DEBUG
dxgsg_cat.debug() << "DXGraphicsStateGuardian:: refill done...\n";
#endif
_dx_ready = TRUE;
return; // need to re-render scene before we can display it
}
if(hr != DD_OK) {
dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Flip failed w/unexpected error code: " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
}
////////////////////////////////////////////////////////////////////
@ -5688,6 +5679,8 @@ void DXGraphicsStateGuardian::show_full_screen_frame(void) {
void DXGraphicsStateGuardian::show_windowed_frame(void) {
HRESULT hr;
DX_DECLARE_CLEAN(DDBLTFX, bltfx);
if (dx_sync_video) {
// Wait for the video refresh *before* we blt the rendered image
// onto the window. This will (a) prevent the "tearing" of the
@ -5704,77 +5697,109 @@ void DXGraphicsStateGuardian::show_windowed_frame(void) {
// this behavior; thus, we allow the user to avoid this wait,
// based on the Config settings.
hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
if(hr != DD_OK) {
dxgsg_cat.error() << "DXGraphicsStateGuardian::WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
bltfx.dwDDFX |= DDBLTFX_NOTEARING; // hmm, does any driver actually recognize this flag?
}
DX_DECLARE_CLEAN(DDBLTFX, bltfx);
bltfx.dwDDFX |= DDBLTFX_NOTEARING;
hr = _pri->Blt( &_view_rect, _back, NULL, DDBLT_DDFX | DDBLT_WAIT, &bltfx );
if(FAILED(hr)) {
if(hr == DDERR_SURFACELOST || hr == DDERR_SURFACEBUSY) {
HRESULT hr;
// TestCooperativeLevel returns DD_OK: If the current mode is
// same as the one which the App set. The following two errors
// are returned to NORMALMODE (windowed)apps only.
//
// DDERR_WRONGMODE: If the App is a windowed app and the current mode is
// not the same as the one in which the app was created.
// DDERR_EXCLUSIVEMODEALREADYSET: If another app took exclusivemode access
hr = _pDD->TestCooperativeLevel();
while(hr == DDERR_EXCLUSIVEMODEALREADYSET) {
// This means that mode changes had taken place, surfaces
// were lost but still we are in the original mode, so we
// simply restore all surfaces and keep going.
_dx_ready = FALSE;
#ifdef _DEBUG
dxgsg_cat.spam() << "DXGraphicsStateGuardian:: another app has exclusive mode, waiting...\n";
#endif
Sleep( 500 ); // Dont consume CPU.
throw_event("PandaPaused"); // throw panda event to invoke network-only processing
hr = _pDD->TestCooperativeLevel();
}
if(hr==DDERR_WRONGMODE) {
// This means that the desktop mode has changed
// need to destroy all of dx stuff and recreate everything
// back again, which is a big hit
dxgsg_cat.error() << "DXGraphicsStateGuardian:: detected display mode change in TestCoopLevel, must recreate all DDraw surfaces, D3D devices, this is not handled yet. " << ConvD3DErrorToString(hr) << endl;
exit(1);
return;
}
if(FAILED(hr)) {
dxgsg_cat.error() << "DXGraphicsStateGuardian::unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
return;
}
#ifdef _DEBUG
dxgsg_cat.debug() << "DXGraphicsStateGuardian:: other app relinquished exclusive mode, refilling surfs...\n";
#endif
RestoreAllVideoSurfaces();
#ifdef _DEBUG
dxgsg_cat.debug() << "DXGraphicsStateGuardian:: refill done...\n";
#endif
_dx_ready = TRUE;
return; // need to re-render scene before we can display it
} else {
dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl;
if (dx_sync_video) {
HRESULT hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
if(hr != DD_OK) {
dxgsg_cat.error() << "WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
}
if(FAILED(hr)) {
if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) {
CheckCooperativeLevel();
} else {
dxgsg_cat.error() << "show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
}
}
bool DXGraphicsStateGuardian::CheckCooperativeLevel(bool bDoReactivateWindow) {
HRESULT hr = _pDD->TestCooperativeLevel();
if(SUCCEEDED(_last_testcooplevel_result)) {
if(SUCCEEDED(hr)) // this means this was just a safety check, dont need to restore surfs
return true;
// otherwise something just went wrong
HRESULT hr;
// TestCooperativeLevel returns DD_OK: If the current mode is same as the one which the App set.
// The following error is returned only for exclusivemode apps.
// DDERR_NOEXCLUSIVEMODE: Some other app took exclusive mode.
hr = _pDD->TestCooperativeLevel();
HRESULT expected_error = (dx_full_screen ? DDERR_NOEXCLUSIVEMODE : DDERR_EXCLUSIVEMODEALREADYSET);
if(hr == expected_error) {
// This means that mode changes had taken place, surfaces
// were lost but still we are in the original mode, so we
// simply restore all surfaces and keep going.
if(dxgsg_cat.is_debug()) {
if(dx_full_screen)
dxgsg_cat.debug() << "Lost access to DDRAW exclusive mode, waiting to regain it...\n";
else dxgsg_cat.debug() << "Another app has DDRAW exclusive mode, waiting...\n";
}
if(_dx_ready) {
_win->deactivate_window();
_dx_ready = FALSE;
}
} else if(hr==DDERR_WRONGMODE) {
// This means that the desktop mode has changed
// need to destroy all of dx stuff and recreate everything
// back again, which is a big hit
dxgsg_cat.error() << "detected display mode change in TestCoopLevel, must recreate all DDraw surfaces, D3D devices, this is not handled yet. hr == " << ConvD3DErrorToString(hr) << endl;
exit(1);
} else if(FAILED(hr)) {
dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
} else {
// testcooplvl was failing, handle case where it now succeeds
if(SUCCEEDED(hr)) {
if(_last_testcooplevel_result == DDERR_EXCLUSIVEMODEALREADYSET) {
if(dxgsg_cat.is_debug())
dxgsg_cat.debug() << "other app relinquished exclusive mode, refilling surfs...\n";
} else if(_last_testcooplevel_result == DDERR_NOEXCLUSIVEMODE) {
if(dxgsg_cat.is_debug())
dxgsg_cat.debug() << "regained exclusive mode, refilling surfs...\n";
}
if(bDoReactivateWindow)
_win->reactivate_window(); //must reactivate window before you can restore surfaces (otherwise you are in WRONGVIDEOMODE, and DDraw RestoreAllSurfaces fails)
RestoreAllVideoSurfaces();
_dx_ready = TRUE;
} else if(hr==DDERR_WRONGMODE) {
// This means that the desktop mode has changed
// need to destroy all of dx stuff and recreate everything
// back again, which is a big hit
dxgsg_cat.error() << "detected desktop display mode change in TestCoopLevel, must recreate all DDraw surfaces & D3D devices, this is not handled yet. " << ConvD3DErrorToString(hr) << endl;
_win->close_window();
exit(1);
} else if((hr!=DDERR_NOEXCLUSIVEMODE) && (hr!=DDERR_EXCLUSIVEMODEALREADYSET)) {
dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
}
_last_testcooplevel_result = hr;
return SUCCEEDED(hr);
}
////////////////////////////////////////////////////////////////////

View File

@ -203,6 +203,7 @@ protected:
WORD *_index_buf; // base of malloced array
bool _dx_ready;
HRESULT _last_testcooplevel_result;
bool _bIsTNLDevice;
LPDIRECTDRAWSURFACE7 _back;
LPDIRECTDRAWSURFACE7 _zbuf;
@ -226,7 +227,6 @@ protected:
D3DDEVICEDESC7 _D3DDevDesc;
void set_clipper(RECT cliprect);
void GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize,
void *pIndexSpace,DWORD dwIndexSpaceByteSize,
D3DVECTOR *pCenter, float fRadius,
@ -414,6 +414,9 @@ public:
void dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled);
#define DO_REACTIVATE_WINDOW true
bool CheckCooperativeLevel(bool bDoReactivateWindow = false);
void dx_setup_after_resize(RECT viewrect,HWND mwindow) ;
void show_frame();
void show_full_screen_frame();

View File

@ -27,7 +27,6 @@
#include <keyboardButton.h>
#include <mouseButton.h>
#include <throw_event.h>
#ifdef DO_PSTATS
#include <pStatTimer.h>
@ -57,7 +56,7 @@ extern bool dx_full_screen_antialiasing; // defined in dxgsg_config.cxx
#define MOUSE_ENTERED 0
#define MOUSE_EXITED 1
#define PAUSED_TIMER_ID 7 // completely arbitrary choice
#define DXREADY ((_dxgsg!=NULL)&&(_dxgsg->GetDXReady()))
LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam,LPARAM lparam);
@ -277,33 +276,16 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
}
return 0;
case WM_ACTIVATE: {
if (_props._fullscreen && (!_exiting_window)) {
// handle switching out of fullscreen mode differently than windowed mode.
// here we want to suspend all gfx and execution, switch display modes and minimize ourself
// until we are switched back to
if(LOWORD(wparam)==WA_INACTIVE) {
if(wdxdisplay_cat.is_spam())
wdxdisplay_cat.spam() << "WDX window deactivated...\n";
_window_inactive = true;
if(HIWORD(wparam)==0x0) // otherwise window already minimized
ShowWindow(_mwindow, SW_MINIMIZE);
} else {
if(_window_inactive) {
if(wdxdisplay_cat.is_spam())
wdxdisplay_cat.spam() << "WDX window re-activated...\n";
_window_inactive = false;
// move window to top of zorder,
SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
if(HIWORD(wparam)!=0x0) // window minimized, need to unminimize
ShowWindow(_mwindow, SW_RESTORE);
}
}
case WM_ACTIVATEAPP: {
#ifdef _DEBUG
wdxdisplay_cat.spam() << "WM_ACTIVATEAPP(" << (bool)(wparam!=0) <<") received\n";
#endif
if((!wparam) && _props._fullscreen) {
deactivate_window();
return 0;
} else break;
} // dont want to reactivate until window is actually un-minimized (see WM_SIZE)
break;
}
case WM_PAINT: {
@ -401,95 +383,86 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
handle_window_move(LOWORD(lparam), HIWORD(lparam) );
return 0;
case WM_EXITSIZEMOVE:
#ifdef _DEBUG
wdxdisplay_cat.spam() << "WM_EXITSIZEMOVE received" << endl;
#endif
if(_dxgsg==NULL)
break;
#ifdef _DEBUG
wdxdisplay_cat.spam() << "WM_EXITSIZEMOVE received" << endl;
#endif
if(_WindowAdjustingType==Resizing) {
_dxgsg->SetDXReady(false); // disable rendering whilst we mess with surfs
GdiFlush();
// Want to change rendertarget size without destroying d3d device. To save vid memory
// (and make resizing work on memory-starved 4MB cards), we need to construct
// a temporary mini-sized render target for the d3d device (it cannot point to a
// NULL rendertarget) before creating the fully resized buffers. The old
// rendertargets will be freed when these temp targets are set, and that will give
// us the memory to create the resized target
LPDIRECTDRAWSURFACE7 pddsDummy = NULL,pddsDummyZ = NULL;
HRESULT hr;
DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd );
if(_dxgsg->GetBackBuffer()==NULL) // bugbug why is this ever true??
return DefWindowProc(hwnd, msg, wparam, lparam);
_dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd);
LPDIRECTDRAW7 pDD = _dxgsg->GetDDInterface();
ddsd.dwFlags &= ~DDSD_PITCH;
ddsd.dwWidth = 1; ddsd.dwHeight = 1;
ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER);
PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf");
if(FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) )) {
wdxdisplay_cat.fatal()
<< "Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
if(_dxgsg!=NULL) {
_dxgsg->SetDXReady(false); // disable rendering whilst we mess with surfs
// Want to change rendertarget size without destroying d3d device. To save vid memory
// (and make resizing work on memory-starved 4MB cards), we need to construct
// a temporary mini-sized render target for the d3d device (it cannot point to a
// NULL rendertarget) before creating the fully resized buffers. The old
// rendertargets will be freed when these temp targets are set, and that will give
// us the memory to create the resized target
LPDIRECTDRAWSURFACE7 pddsDummy = NULL, pddsDummyZ = NULL;
HRESULT hr;
DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd );
if(_dxgsg->GetBackBuffer()==NULL) // bugbug why is this ever true??
return DefWindowProc(hwnd, msg, wparam, lparam);
_dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd);
LPDIRECTDRAW7 pDD = _dxgsg->GetDDInterface();
ddsd.dwFlags &= ~DDSD_PITCH;
ddsd.dwWidth = 1; ddsd.dwHeight = 1;
ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER);
PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf");
if(FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) )) {
wdxdisplay_cat.fatal() << "Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ );
_dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ);
ddsdZ.dwFlags &= ~DDSD_PITCH;
ddsdZ.dwWidth = 1; ddsdZ.dwHeight = 1;
PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf");
if(FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) )) {
wdxdisplay_cat.fatal() << "Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) {
wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
if(FAILED( hr = _dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 ))) {
wdxdisplay_cat.fatal()
<< "Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
RELEASE(pddsDummyZ);
RELEASE(pddsDummy);
}
DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ );
_dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ);
ddsdZ.dwFlags &= ~DDSD_PITCH;
ddsdZ.dwWidth = 1; ddsdZ.dwHeight = 1;
handle_reshape(true);
PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf");
if(FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) )) {
wdxdisplay_cat.fatal() << "Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
if(_dxgsg!=NULL) {
_dxgsg->SetDXReady(true);
}
if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) {
wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
if(FAILED( hr = _dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 ))) {
wdxdisplay_cat.fatal()
<< "Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl;
exit(1);
}
RELEASE(pddsDummyZ);
RELEASE(pddsDummy);
RECT view_rect;
GetClientRect( _mwindow, &view_rect );
ClientToScreen( _mwindow, (POINT*)&view_rect.left );
ClientToScreen( _mwindow, (POINT*)&view_rect.right );
_dxgsg->dx_setup_after_resize(view_rect,_mwindow); // create the new resized rendertargets
// change _props xsize,ysize
resized((view_rect.right - view_rect.left),(view_rect.bottom - view_rect.top));
_props._xorg = view_rect.left; // _props origin should reflect view rectangle
_props._yorg = view_rect.top;
_dxgsg->SetDXReady(true);
}
_WindowAdjustingType = NotAdjusting;
_WindowAdjustingType = NotAdjusting;
return 0;
case WM_ENTERSIZEMOVE: {
if(_dxgsg==NULL)
break;
_dxgsg->SetDXReady(true); // dont disable here because I want to see pic as I resize
_dxgsg->SetDXReady(true); // dont disable here because I want to see pic as I resize
_WindowAdjustingType = MovingOrResizing;
}
break;
@ -500,9 +473,18 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
DWORD newbitdepth=wparam;
wdxdisplay_cat.spam() <<"WM_DISPLAYCHANGE received with width:" << width << " height: " << height << " bpp: " << wparam<< endl;
#endif
// Note: TestCoopLevel in dxgsg will return WRONGMODE if there is a problem after a displaymode change
// so we dont need to abort here
// unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't
// seem to think we're back in the original displaymode even after I've received
// the WM_DISPLAYCHANGE msg, and returns WRONGMODE error. So the only way I can
// think of to make this work is to have the timer periodically check for restored
// coop level
// if(_props._fullscreen && _window_inactive) {
// if(_dxgsg!=NULL)
// _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW);
// else reactivate_window();
// }
}
break;
@ -523,7 +505,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
width = LOWORD(lparam); height = HIWORD(lparam);
if(_props._xsize != width || _props._ysize != height) {
if((_props._xsize != width) || (_props._ysize != height)) {
_WindowAdjustingType = Resizing;
// for maximized,unmaximize, need to call resize code artificially
@ -531,8 +513,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(wparam==SIZE_MAXIMIZED) {
_bSizeIsMaximized=TRUE;
window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
} else if((wparam==SIZE_RESTORED)&& _bSizeIsMaximized) {
_bSizeIsMaximized=FALSE;
} else if((wparam==SIZE_RESTORED) && _bSizeIsMaximized) {
_bSizeIsMaximized=FALSE; // only want to reinit dx if restoring from maximized state
window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
}
}
@ -570,12 +552,129 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(_WindowAdjustingType)
break;
return 0; // dont let GDI waste time redrawing the deflt background
case WM_TIMER:
// 2 cases of app deactivation:
//
// 1) user has switched out of fullscreen mode
// this is first signalled when ACTIVATEAPP returns false
// for this case, we dont wake up until WM_SIZE returns restore or maximize
// and WM_TIMER just periodically reawakens app for idle processing
// unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't
// seem to think we're back in the original displaymode even after I've received
// the WM_DISPLAYCHANGE msg, and returns WRONGMODE error. So the only way I can
// think of to make this work is to have the timer periodically check for restored
// coop level, as it does in case 2)
//
// 2) windowed app has lost access to dx because another app has taken dx exclusive mode
// here we rely on WM_TIMER to periodically check if it is ok to reawaken app.
// windowed apps currently run regardless of if its window is in the foreground
// so we cannot rely on window messages to reawaken app
if((wparam==_PandaPausedTimer) && _window_inactive) {
assert(_dxgsg!=NULL);
_dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW);
// wdxdisplay_cat.spam() << "periodic return of control to app\n";
_return_control_to_app = true;
// throw_event("PandaPaused");
// do we still need to do this since I return control to app periodically using timer msgs?
// does app need to know to avoid major computation?
}
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
////////////////////////////////////////////////////////////////////
// Function: handle_reshape
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void wdxGraphicsWindow::handle_reshape(bool bDoDxReset) {
RECT view_rect;
GetClientRect( _mwindow, &view_rect );
ClientToScreen( _mwindow, (POINT*)&view_rect.left ); // translates top,left pnt
ClientToScreen( _mwindow, (POINT*)&view_rect.right ); // translates right,bottom pnt
// change _props xsize,ysize
resized((view_rect.right - view_rect.left),(view_rect.bottom - view_rect.top));
_props._xorg = view_rect.left; // _props origin should reflect upper left of view rectangle
_props._yorg = view_rect.top;
if(wdxdisplay_cat.is_spam()) {
wdxdisplay_cat.spam() << "reshape to origin: (" << _props._xorg << "," << _props._yorg << "), size: (" << _props._xsize << "," << _props._ysize << ")\n";
}
if((_dxgsg!=NULL) && bDoDxReset) {
_dxgsg->dx_setup_after_resize(view_rect,_mwindow); // create the new resized rendertargets
}
}
void wdxGraphicsWindow::deactivate_window(void) {
// current policy is to suspend minimized or deactivated fullscreen windows, but leave
// regular windows running normally
if(_window_inactive || _exiting_window)
return;
if(wdxdisplay_cat.is_spam())
wdxdisplay_cat.spam() << "WDX window deactivated, waiting...\n";
_window_inactive = true;
if(_props._fullscreen) {
// make sure window is minimized
WINDOWPLACEMENT wndpl;
wndpl.length=sizeof(WINDOWPLACEMENT);
if(!GetWindowPlacement(_mwindow,&wndpl)) {
wdxdisplay_cat.error() << "GetWindowPlacement failed!\n";
return;
}
if((wndpl.showCmd!=SW_MINIMIZE)&&(wndpl.showCmd!=SW_SHOWMINIMIZED)) {
ShowWindow(_mwindow, SW_MINIMIZE);
}
}
_PandaPausedTimer = SetTimer(_mwindow,PAUSED_TIMER_ID,500,NULL);
if(_PandaPausedTimer!=PAUSED_TIMER_ID) {
wdxdisplay_cat.error() << "Error in SetTimer!\n";
}
}
void wdxGraphicsWindow::reactivate_window(void) {
if(!_window_inactive)
return;
// first see if dx cooperative level is OK for reactivation
// if(!_dxgsg->CheckCooperativeLevel())
// return;
if(wdxdisplay_cat.is_spam())
wdxdisplay_cat.spam() << "WDX window re-activated...\n";
_window_inactive = false;
if(_PandaPausedTimer!=NULL) {
KillTimer(_mwindow,_PandaPausedTimer);
_PandaPausedTimer = NULL;
}
// move window to top of zorder
// if(_props._fullscreen)
// SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
GdiFlush();
}
////////////////////////////////////////////////////////////////////
// Function: Constructor
@ -613,6 +712,7 @@ void wdxGraphicsWindow::config(void) {
_gsg = _dxgsg = NULL;
_exiting_window = false;
_window_inactive = false;
_return_control_to_app = false;
_hOldForegroundWindow=GetForegroundWindow();
@ -662,24 +762,23 @@ void wdxGraphicsWindow::config(void) {
exit(1);
}
DWORD window_style = WS_POPUP | WS_SYSMENU; // for CreateWindow
// rect now contains the coords for the entire window, not the client
if(dx_full_screen) {
_mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(),
WS_POPUP, 0, 0, _props._xsize,_props._ysize,
window_style, 0, 0, _props._xsize,_props._ysize,
NULL, NULL, hinstance, 0);
} else {
int style;
RECT win_rect;
SetRect(&win_rect, _props._xorg, _props._yorg, _props._xorg + _props._xsize,
_props._yorg + _props._ysize);
if(_props._border)
style = WS_OVERLAPPEDWINDOW;
else
style = WS_POPUP | WS_MAXIMIZE;
window_style |= WS_OVERLAPPEDWINDOW; // should we just use WS_THICKFRAME instead?
AdjustWindowRect(&win_rect, style, FALSE); //compute window size based on desired client area size
AdjustWindowRect(&win_rect, window_style, FALSE); //compute window size based on desired client area size
// make sure origin is on screen
if(win_rect.left < 0) {
@ -690,13 +789,13 @@ void wdxGraphicsWindow::config(void) {
}
_mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(),
style, win_rect.left, win_rect.top, win_rect.right-win_rect.left,
window_style, win_rect.left, win_rect.top, win_rect.right-win_rect.left,
win_rect.bottom-win_rect.top,
NULL, NULL, hinstance, 0);
}
if(!_mwindow) {
wdxdisplay_cat.fatal() << "config() - failed to create Mwindow" << endl;
wdxdisplay_cat.fatal() << "config() - failed to create window" << endl;
exit(1);
}
@ -1063,7 +1162,6 @@ dx_setup() {
}
SetRect(&view_rect, 0, 0, dwRenderWidth, dwRenderHeight);
} // end create full screen buffers
else { // CREATE WINDOWED BUFFERS
@ -1480,20 +1578,38 @@ supports_update() const {
return true;
}
void INLINE wdxGraphicsWindow::process_events(void) {
void INLINE process_1_event(void) {
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if(!GetMessage(&msg, NULL, 0, 0)) {
// WM_QUIT received
DestroyAllWindows(false);
exit(msg.wParam); // this will invoke AtExitFn
}
if(!GetMessage(&msg, NULL, 0, 0)) {
// WM_QUIT received
DestroyAllWindows(false);
exit(msg.wParam); // this will invoke AtExitFn
}
// Translate virtual key messages
TranslateMessage(&msg);
// Call window_proc
DispatchMessage(&msg);
// Translate virtual key messages
TranslateMessage(&msg);
// Call window_proc
DispatchMessage(&msg);
}
void INLINE wdxGraphicsWindow::process_events(void) {
if(_window_inactive) {
// Get 1 msg at a time until no more are left and we block and sleep,
// or message changes _return_control_to_app or _window_inactive status
while(_window_inactive && (!_return_control_to_app)) {
process_1_event();
}
_return_control_to_app = false;
} else {
MSG msg;
// handle all msgs on queue in a row
while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
process_1_event();
}
}
}
@ -1504,27 +1620,29 @@ void INLINE wdxGraphicsWindow::process_events(void) {
////////////////////////////////////////////////////////////////////
void wdxGraphicsWindow::update(void) {
#ifdef DO_PSTATS
if(!_window_inactive) {
_show_code_pcollector.stop();
PStatClient::main_tick();
}
_show_code_pcollector.stop();
if(!_window_inactive) {
PStatClient::main_tick();
}
#endif
process_events();
process_events();
if(_window_inactive) {
Sleep( 500 ); // Dont consume CPU.
throw_event("PandaPaused"); // throw panda event to invoke network-only processing
if(_window_inactive) {
// note _window_inactive must be checked after process_events is called, to avoid draw_callback being called
if(_idle_callback)
call_idle_callback();
return;
}
}
call_draw_callback(true);
call_draw_callback(true);
if (_idle_callback)
if(_idle_callback)
call_idle_callback();
#ifdef DO_PSTATS
_show_code_pcollector.start();
_show_code_pcollector.start();
#endif
}

View File

@ -91,6 +91,7 @@ protected:
public:
HWND _mwindow;
HWND _hOldForegroundWindow;
UINT_PTR _PandaPausedTimer;
private:
HDC _hdc;
@ -107,6 +108,7 @@ private:
bool _ignore_key_repeat;
bool _exiting_window;
bool _window_inactive;
bool _return_control_to_app;
public:
static TypeHandle get_class_type(void);
@ -116,6 +118,9 @@ public:
void DestroyMe(bool bAtExitFnCalled);
virtual void do_close_window();
void deactivate_window(void);
void reactivate_window(void);
void handle_reshape(bool bDoDXReset);
private:
static TypeHandle _type_handle;