Forráskód Böngészése

update fullscreen, alt-tab, error handling

cxgeorge 24 éve
szülő
commit
bfc5300e57

+ 170 - 145
panda/src/dxgsg/dxGraphicsStateGuardian.cxx

@@ -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(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) {
+   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)) {
-      dxgsg_cat.error() << "DXGraphicsStateGuardian::unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
-      return;
+  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);
     }
-
-#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);
+    bltfx.dwDDFX |= DDBLTFX_NOTEARING;  // hmm, does any driver actually recognize this flag?
+  }
+
+  hr = _pri->Blt( &_view_rect, _back,  NULL, DDBLT_DDFX | DDBLT_WAIT, &bltfx );
+
+  if (dx_sync_video) {
+    HRESULT hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
     if(hr != DD_OK) {
-      dxgsg_cat.error() << "DXGraphicsStateGuardian::WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl;
+      dxgsg_cat.error() << "WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl;
       exit(1);
     }
   }
 
-  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
+    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.
 
-        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;
-      }
+    
+        HRESULT expected_error = (dx_full_screen ? DDERR_NOEXCLUSIVEMODE : DDERR_EXCLUSIVEMODEALREADYSET);
 
-#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
+        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";
+          }
 
-      _dx_ready = TRUE;
-      return;    // need to re-render scene before we can display it
+          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 {
-      dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl;
-      exit(1);
+        // 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);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 1
panda/src/dxgsg/dxGraphicsStateGuardian.h

@@ -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();

+ 257 - 139
panda/src/wdxdisplay/wdxGraphicsWindow.cxx

@@ -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);
-                }
-
-                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(_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);
                 }
 
-                if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) {
-                    wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
-                    exit(1);
-                }
+                handle_reshape(true);
 
-                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);
+                if(_dxgsg!=NULL) {
+                   _dxgsg->SetDXReady(true);
                 }
-                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,13 +552,130 @@ 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
 //       Access:
@@ -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);
+}
+
+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;
 
-      // Translate virtual key messages
-      TranslateMessage(&msg);
-      // Call window_proc
-      DispatchMessage(&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
 }
 

+ 5 - 0
panda/src/wdxdisplay/wdxGraphicsWindow.h

@@ -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;