Browse Source

add support for dynamic fullscreen resizing

cxgeorge 24 years ago
parent
commit
8a86a45e19

+ 7 - 6
panda/src/wgldisplay/wglGraphicsPipe.cxx

@@ -53,9 +53,11 @@ TypeHandle wglGraphicsPipe::get_class_type(void) {
   return _type_handle;
   return _type_handle;
 }
 }
 
 
+const char *pipe_type_name="wglGraphicsPipe";
+
 void wglGraphicsPipe::init_type(void) {
 void wglGraphicsPipe::init_type(void) {
   InteractiveGraphicsPipe::init_type();
   InteractiveGraphicsPipe::init_type();
-  register_type(_type_handle, "wglGraphicsPipe",
+  register_type(_type_handle, pipe_type_name,
         InteractiveGraphicsPipe::get_class_type());
         InteractiveGraphicsPipe::get_class_type());
 }
 }
 
 
@@ -65,17 +67,16 @@ TypeHandle wglGraphicsPipe::get_type(void) const {
 
 
 wglGraphicsPipe::wglGraphicsPipe(void) {
 wglGraphicsPipe::wglGraphicsPipe(void) {
   wgldisplay_cat.error()
   wgldisplay_cat.error()
-    << "wglGraphicsPipes should not be created with the default constructor"
-    << endl;
+    << pipe_type_name <<"s should not be created with the default constructor" << endl;
 }
 }
 
 
 wglGraphicsPipe::wglGraphicsPipe(const wglGraphicsPipe&) {
 wglGraphicsPipe::wglGraphicsPipe(const wglGraphicsPipe&) {
   wgldisplay_cat.error()
   wgldisplay_cat.error()
-    << "wglGraphicsPipes should not be copied" << endl;
+    << pipe_type_name << "s should not be copied" << endl;
 }
 }
 
 
 wglGraphicsPipe& wglGraphicsPipe::operator=(const wglGraphicsPipe&) {
 wglGraphicsPipe& wglGraphicsPipe::operator=(const wglGraphicsPipe&) {
-  wgldisplay_cat.error()
-    << "wglGraphicsPipes should not be assigned" << endl;
+  wgldisplay_cat.error() 
+  << pipe_type_name << "s should not be assigned" << endl;
   return *this;
   return *this;
 }
 }

+ 138 - 37
panda/src/wgldisplay/wglGraphicsWindow.cxx

@@ -70,6 +70,11 @@ unsigned int hardcoded_modifier_buttons[NUM_MODIFIER_KEYS]={VK_SHIFT,VK_MENU,VK_
                                          VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT,VK_PRIOR,VK_NEXT,VK_HOME,VK_END,
                                          VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT,VK_PRIOR,VK_NEXT,VK_HOME,VK_END,
                                          VK_INSERT,VK_DELETE,VK_ESCAPE};
                                          VK_INSERT,VK_DELETE,VK_ESCAPE};
 
 
+// dont pick any video modes < MIN_REFRESH_RATE Hz
+#define MIN_REFRESH_RATE 60
+// EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use driver default rate (assume its >min_refresh_rate)
+#define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
+
 void PrintErrorMessage(DWORD msgID) {
 void PrintErrorMessage(DWORD msgID) {
    LPTSTR pMessageBuffer;
    LPTSTR pMessageBuffer;
 
 
@@ -276,6 +281,27 @@ static DWORD GetAvailVidMem(void) {
     return dwFree;
     return dwFree;
 }
 }
 
 
+bool find_acceptable_display_mode(DWORD dwWidth,DWORD dwHeight,DWORD bpp,DEVMODE &dm) {
+    int modenum=0;
+
+    // look for acceptable mode
+    while(1) {
+        ZeroMemory( &dm, sizeof( dm ) );
+        dm.dmSize = sizeof( dm );
+
+        if(!EnumDisplaySettings(NULL,modenum,&dm))
+          break;
+
+        if((dm.dmPelsWidth==dwWidth) && (dm.dmPelsHeight==dwHeight) &&
+           (dm.dmBitsPerPel==bpp) && ACCEPTABLE_REFRESH_RATE(dm.dmDisplayFrequency)) {
+           return true;
+        }
+        modenum++;
+    }
+
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: config
 //     Function: config
 //       Access:
 //       Access:
@@ -292,7 +318,7 @@ void wglGraphicsWindow::config(void) {
 
 
     _exiting_window = false;
     _exiting_window = false;
     _return_control_to_app = false;
     _return_control_to_app = false;
-    
+    _bIsLowVidMemCard = false; 
     _active_minimized_fullscreen = false;
     _active_minimized_fullscreen = false;
     _PandaPausedTimer = NULL;
     _PandaPausedTimer = NULL;
     _mouse_input_enabled = false;
     _mouse_input_enabled = false;
@@ -379,14 +405,9 @@ void wglGraphicsWindow::config(void) {
 
 
       DWORD dwFullScreenBitDepth=cur_bitdepth;
       DWORD dwFullScreenBitDepth=cur_bitdepth;
 
 
-      // dont pick any video modes < MIN_REFRESH_RATE Hz
-      #define MIN_REFRESH_RATE 60
-      
-      // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use driver default rate
-      #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
-      
       #define LOWVIDMEMTHRESHOLD 3500000
       #define LOWVIDMEMTHRESHOLD 3500000
       if(GetAvailVidMem() < LOWVIDMEMTHRESHOLD) {
       if(GetAvailVidMem() < LOWVIDMEMTHRESHOLD) {
+          _bIsLowVidMemCard = true;
           wgldisplay_cat.debug() << "small video memory card detect, switching fullscreen to minimum 640x480x16 config\n";
           wgldisplay_cat.debug() << "small video memory card detect, switching fullscreen to minimum 640x480x16 config\n";
           // we're going to need  640x480 at 16 bit to save enough tex vidmem
           // we're going to need  640x480 at 16 bit to save enough tex vidmem
           dwFullScreenBitDepth=16;
           dwFullScreenBitDepth=16;
@@ -395,29 +416,8 @@ void wglGraphicsWindow::config(void) {
       }
       }
 
 
       DEVMODE dm;
       DEVMODE dm;
-      bool bGoodModeFound=false;
-      BOOL bGotNewMode;
-      int j=0;
-
-      while(1) {
-          memset( &dm, 0, sizeof( dm ) );
-          dm.dmSize = sizeof( dm );
-
-          bGotNewMode=EnumDisplaySettings(NULL,j,&dm);
-          if(!bGotNewMode)
-            break;
-
-          if((dm.dmPelsWidth==dwWidth) && (dm.dmPelsHeight==dwHeight) &&
-             (dm.dmBitsPerPel==dwFullScreenBitDepth) &&
-             ACCEPTABLE_REFRESH_RATE(dm.dmDisplayFrequency)) {
-              bGoodModeFound=true;
-              break;
-          }
-          j++;
-      }
-
-      if(!bGoodModeFound) {
-          wgldisplay_cat.fatal() << "Videocard has no supported display resolutions at specified res ( " << dwWidth << " X " << dwHeight << " X " << dwFullScreenBitDepth <<" )\n";
+      if(!find_acceptable_display_mode(dwWidth,dwHeight,dwFullScreenBitDepth,dm)) {
+          wgldisplay_cat.fatal() << "Videocard has no supported display resolutions at specified res (" << dwWidth << " X " << dwHeight << " X " << dwFullScreenBitDepth <<")\n";
           exit(1);
           exit(1);
       }
       }
 
 
@@ -427,7 +427,7 @@ void wglGraphicsWindow::config(void) {
                 window_style,0,0,dwWidth,dwHeight,hDesktopWindow, NULL, hinstance, 0);
                 window_style,0,0,dwWidth,dwHeight,hDesktopWindow, NULL, hinstance, 0);
 
 
       // move window to top of zorder,
       // move window to top of zorder,
-      SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
+      SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);  
     
     
       ShowWindow(_mwindow, SW_SHOWNORMAL);
       ShowWindow(_mwindow, SW_SHOWNORMAL);
       ShowWindow(_mwindow, SW_SHOWNORMAL);
       ShowWindow(_mwindow, SW_SHOWNORMAL);
@@ -911,7 +911,7 @@ void wglGraphicsWindow::end_frame(void) {
     // double-buffered mode.  Instead we have to use glBitMap display
     // double-buffered mode.  Instead we have to use glBitMap display
     // lists created by wglUseFontBitmaps
     // lists created by wglUseFontBitmaps
 
 
-    glColor3f(0.0,1.0,1.0);
+    glColor3f(0.0f,1.0f,1.0f);
 
 
     GLboolean tex_was_on = glIsEnabled(GL_TEXTURE_2D);
     GLboolean tex_was_on = glIsEnabled(GL_TEXTURE_2D);
 
 
@@ -925,9 +925,9 @@ void wglGraphicsWindow::end_frame(void) {
     glPushMatrix();
     glPushMatrix();
     glLoadIdentity();
     glLoadIdentity();
 
 
-    glOrtho(0,_props._xsize,
-            0,_props._ysize,
-            -1.0,1.0);
+    glOrtho(0.0f,_props._xsize,
+            0.0f,_props._ysize,
+            -1.0f,1.0f);
 
 
     glRasterPos2f(_props._xsize-70,_props._ysize-20);  // these seem to be good for default font
     glRasterPos2f(_props._xsize-70,_props._ysize-20);  // these seem to be good for default font
 
 
@@ -950,7 +950,8 @@ void wglGraphicsWindow::end_frame(void) {
 
 
   {
   {
     PStatTimer timer(_swap_pcollector);
     PStatTimer timer(_swap_pcollector);
-     if(_is_synced) glFinish();
+     if(_is_synced) 
+        glFinish();
      else SwapBuffers(_hdc);
      else SwapBuffers(_hdc);
   }
   }
   GraphicsWindow::end_frame();
   GraphicsWindow::end_frame();
@@ -962,7 +963,107 @@ void wglGraphicsWindow::end_frame(void) {
 //  Description: Swaps the front and back buffers explicitly.
 //  Description: Swaps the front and back buffers explicitly.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void wglGraphicsWindow::swap(void) {
 void wglGraphicsWindow::swap(void) {
-    if(_is_synced)SwapBuffers(_hdc);
+  if(_is_synced)
+    SwapBuffers(_hdc);
+}
+
+void wglGraphicsWindow::resize(unsigned int xsize,unsigned int ysize) {
+    if (!_props._fullscreen) {
+        // resizing windowed mode is easy
+        SetWindowPos(_mwindow, NULL, 0,0, xsize,ysize, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
+    } else {
+      DWORD dwWidth =  xsize;
+      DWORD dwHeight = ysize;
+
+      HWND hDesktopWindow = GetDesktopWindow();
+      HDC scrnDC=GetDC(hDesktopWindow);
+      DWORD dwFullScreenBitDepth=GetDeviceCaps(scrnDC,BITSPIXEL);
+      ReleaseDC(hDesktopWindow,scrnDC);
+
+      // resize will always leave screen bitdepth unchanged
+
+      // allowing resizing of lowvidmem cards to > 640x480.  why?  I'll assume
+      // check was already done by caller, so he knows what he wants
+
+      DEVMODE dm;
+      if(!find_acceptable_display_mode(dwWidth,dwHeight,dwFullScreenBitDepth,dm)) {
+          wgldisplay_cat.fatal() << "window resize(" << xsize << "," << ysize << ") failed, no compatible fullscreen display mode found!\n";
+          return;
+      }
+
+      // this causes WM_SIZE msg to be produced
+      SetWindowPos(_mwindow, NULL, 0,0, xsize, ysize, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
+
+      int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
+    
+      if(chg_result!=DISP_CHANGE_SUCCESSFUL) {
+            wgldisplay_cat.fatal() << "resize ChangeDisplaySettings failed (error code: " << chg_result <<") for specified res ( " << dwWidth << " X " << dwHeight << " X " << dwFullScreenBitDepth <<" ), " << dm.dmDisplayFrequency  << "Hz\n";
+            exit(1);
+      }
+
+      // this assertion could be violated if we eventually allow dynamic fullscrn/windowed mode switching
+      assert(_pCurrent_display_settings!=NULL);
+      memcpy(_pCurrent_display_settings,&dm,sizeof(DEVMODE));
+    } 
+}
+
+unsigned int wglGraphicsWindow::
+verify_window_sizes(unsigned int numsizes,unsigned int *dimen) {
+  // see if window sizes are supported (i.e. in fullscrn mode)
+  // dimen is an array containing contiguous x,y pairs specifying
+  // possible display sizes, it is numsizes*2 long.  fn will zero
+  // out any invalid x,y size pairs.  return value is number of valid 
+  // sizes that were found.
+  // 
+  // note: it might be better to implement some sort of query
+  //       interface that returns an array of supported sizes,
+  //       but this way is somewhat simpler and will do the job 
+  //       on most cards, assuming they handle the std sizes the app
+  //       knows about.
+
+  if (!_props._fullscreen || (numsizes==0)) {
+      return numsizes;
+  }
+
+  assert(dimen!=NULL);
+
+  HWND hDesktopWindow = GetDesktopWindow();
+  HDC scrnDC=GetDC(hDesktopWindow);
+  DWORD dwFullScreenBitDepth=GetDeviceCaps(scrnDC,BITSPIXEL);
+  ReleaseDC(hDesktopWindow,scrnDC);
+
+  // gonna do an n^2 loop alg for simplicity.  if speed is necessary, 
+  // could do linear time with some kind of STL hash container I guess
+
+  DEVMODE dm;
+  int modenum=0;
+  int goodmodes=0;
+  unsigned int *cur_dim_pair=dimen;
+  for(;modenum<numsizes;modenum++,cur_dim_pair+=2) {
+      bool bIsGoodmode;
+      DWORD dwWidth=cur_dim_pair[0];
+      DWORD dwHeight=cur_dim_pair[1];
+
+      if((dwWidth==0)||(dwHeight==0)) {
+          bIsGoodmode=false;
+      } else {
+          if(_bIsLowVidMemCard) {
+              bIsGoodmode=((dwWidth*dwHeight)<=(640*480));
+          } else {
+              bIsGoodmode = find_acceptable_display_mode(dwWidth,dwHeight,dwFullScreenBitDepth,dm);
+          }
+      }
+
+      if(bIsGoodmode) {
+         goodmodes++;
+      } else {
+         // zero out the bad mode
+         cur_dim_pair[0]=0;
+         cur_dim_pair[1]=0;
+      }
+  }
+
+  return goodmodes;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 5 - 0
panda/src/wgldisplay/wglGraphicsWindow.h

@@ -117,6 +117,7 @@ private:
   UINT_PTR          _PandaPausedTimer;
   UINT_PTR          _PandaPausedTimer;
 
 
   DEVMODE           *_pCurrent_display_settings;
   DEVMODE           *_pCurrent_display_settings;
+  bool              _bIsLowVidMemCard;
 
 
   bool              _window_inactive;
   bool              _window_inactive;
   bool              _active_minimized_fullscreen;
   bool              _active_minimized_fullscreen;
@@ -131,6 +132,7 @@ private:
   bool              _ignore_key_repeat;
   bool              _ignore_key_repeat;
   int               _full_height, _full_width;
   int               _full_height, _full_width;
 
 
+
   // vars for frames/sec meter
   // vars for frames/sec meter
   DWORD _start_time;
   DWORD _start_time;
   DWORD _start_frame_count;
   DWORD _start_frame_count;
@@ -151,6 +153,9 @@ public:
   virtual void deactivate_window(void);
   virtual void deactivate_window(void);
   virtual void reactivate_window(void);
   virtual void reactivate_window(void);
 
 
+  virtual void resize(unsigned int xsize,unsigned int ysize);
+  virtual unsigned int verify_window_sizes(unsigned int numsizes,unsigned int *dimen);
+
 protected:
 protected:
   virtual void do_close_window();
   virtual void do_close_window();