Przeglądaj źródła

Updated version of OTHGMars' updated window and resolution options modes.
Additional changes include disabling certain options when they are not valid(ie, resolution is disabled when in borderless mode)

Areloch 4 lat temu
rodzic
commit
26ef40950b

+ 14 - 0
Engine/source/gui/controls/guiGameListMenuCtrl.cpp

@@ -533,6 +533,14 @@ void GuiGameListMenuCtrl::addRow(const char* label, const char* bitmapName, cons
    addRow(row, label, callback, icon, yPad, true, enabled, Row::Mode::Keybind, tooltip);
 }
 
+void GuiGameListMenuCtrl::removeRow(const S32& row)
+{
+   if (row == -1 || row >= mRows.size())
+      return;
+
+   mRows.erase(row);
+}
+
 Point2I  GuiGameListMenuCtrl::getMinExtent() const
 {
    Point2I parentMin = Parent::getMinExtent();
@@ -1521,6 +1529,12 @@ DefineEngineMethod(GuiGameListMenuCtrl, addKeybindRow, void,
    object->addRow(label, bitmapName, callback, icon, yPad, enabled, tooltip);
 }
 
+DefineEngineMethod(GuiGameListMenuCtrl, removeRow, void, (S32 row),,
+   "Removes the row at the provided index")
+{
+   object->removeRow(row);
+}
+
 DefineEngineMethod(GuiGameListMenuCtrl, getCurrentOption, const char*, (S32 row), ,
    "Gets the text for the currently selected option of the given row.\n\n"
    "@param row Index of the row to get the option from.\n"

+ 3 - 0
Engine/source/gui/controls/guiGameListMenuCtrl.h

@@ -163,6 +163,9 @@ public:
 
    void addRow(const char* label, const char* bitmapName, const char* callback, S32 icon, S32 yPad, bool enabled, const char* tooltip);
 
+   //Removes row at the provided index
+   void GuiGameListMenuCtrl::removeRow(const S32& row);
+
    /// Gets the text for the currently selected option of the given row.
    ///
    /// \param rowIndex Index of the row to get the option from.

+ 35 - 2
Engine/source/gui/core/guiCanvas.cpp

@@ -238,7 +238,8 @@ bool GuiCanvas::onAdd()
          mPlatformWindow->lockSize(true);
       
       // Set a minimum on the window size so people can't break us by resizing tiny.
-      mPlatformWindow->setMinimumWindowSize(Point2I(640,480));
+      mPlatformWindow->setMinimumWindowSize(Point2I(Con::getIntVariable("$Video::minimumXResolution", 1024),
+         Con::getIntVariable("$Video::minimumYResolution", 720)));
 
       // Now, we have to hook in our event callbacks so we'll get
       // appropriate events from the window.
@@ -2462,6 +2463,35 @@ DefineEngineMethod( GuiCanvas, getMonitorRect, RectI, (S32 index),,
    return PlatformWindowManager::get()->getMonitorRect(index);
 }
 
+DefineEngineMethod( GuiCanvas, getMonitorUsableRect, RectI, (S32 index),,
+               "@brief Use this function to get the usable desktop area represented by a display, with the primary display located at 0,0.\n\n"
+               "This is the same area as Canvas.getMonitorRect() reports, but with portions reserved by the system removed. "
+               "For example, on Apple Mac OS X, this subtracts the area occupied by the menu bar and dock.\n"
+               "Setting a window to be fullscreen generally bypasses these unusable areas, so these are good guidelines for "
+               "the maximum space available to a non - fullscreen window."
+               "@param index The monitor index.\n\n"
+               "@return The rectangular region of the requested monitor.")
+{
+   return PlatformWindowManager::get()->getMonitorUsableRect(index);
+}
+
+DefineEngineMethod(GuiCanvas, getMonitorModeCount, S32, (S32 monitorIndex), (0),
+   "Gets the number of video modes available on the selected monitor.\n\n")
+{
+   return PlatformWindowManager::get()->getMonitorModeCount(monitorIndex);
+}
+DefineEngineMethod(GuiCanvas, getMonitorMode, const char*, (S32 monitorIndex, S32 modeIndex), (0),
+   "Gets a video mode string from the selected monitor.\n\n")
+{
+   char* buf = Con::getReturnBuffer(PlatformWindowManager::get()->getMonitorMode(monitorIndex, modeIndex));
+   return buf;
+}
+DefineEngineMethod(GuiCanvas, getMonitorDesktopMode, const char*, (S32 monitorIndex), (0),
+   "Gets the current desktop mode for the selected monitor.\n\n")
+{
+   char* buf = Con::getReturnBuffer(PlatformWindowManager::get()->getMonitorDesktopMode(monitorIndex));
+   return buf;
+}
 
 DefineEngineMethod( GuiCanvas, getVideoMode, const char*, (),,
                "@brief Gets the current screen mode as a string.\n\n"
@@ -2698,8 +2728,11 @@ DefineEngineMethod( GuiCanvas, restoreWindow, void, (), , "() - restore this can
 DefineEngineMethod( GuiCanvas, setFocus, void, (), , "() - Claim OS input focus for this canvas' window.")
 {
    PlatformWindow* window = object->getPlatformWindow();
-   if( window )
+   if (window)
+   {
       window->setFocus();
+      window->appEvent.trigger(window->getWindowId(), GainFocus);
+   }
 }
 
 DefineEngineMethod( GuiCanvas, setMenuBar, void, ( GuiControl* menu ),,

+ 18 - 1
Engine/source/windowManager/platformWindowMgr.h

@@ -90,6 +90,23 @@ public:
    // Get the requested monitor's rectangular region.
    virtual RectI getMonitorRect(U32 index) { return RectI(0, 0, 0, 0); }
 
+   // Get the requested monitor's rectangular region.
+   // Use this function to get the usable desktop area represented by a display,
+   // with the primary display located at 0,0. 
+   virtual RectI getMonitorUsableRect(U32 index) { return RectI(0, 0, 0, 0); }
+
+   // Retrieve the number of display modes available on a monitor.  Provides a default
+   // count of 0 for systems that don't provide information on connected monitors.
+   virtual U32 getMonitorModeCount(U32 monitorIndex) { return 0; }
+
+   // Gets a display mode for a specific monitor.  Provides a default of "" for platorms
+   // that do not provide information on connected monitors.
+   virtual const String getMonitorMode(U32 monitorIndex, U32 modeIndex) { return String::EmptyString; }
+
+   // Gets the current desktop display mode for a specific monitor.  Provides a default
+   // of "" for platorms that do not provide information on connected monitors.
+   virtual const String getMonitorDesktopMode(U32 monitorIndex) { return String::EmptyString; }
+
    /// Populate a vector with all monitors and their extents in window space.
    virtual void getMonitorRegions(Vector<RectI> &regions) = 0;
 
@@ -152,4 +169,4 @@ private:
 /// need to get the window manager from somewhere else.
 PlatformWindowManager *CreatePlatformWindowManager();
 
-#endif
+#endif

+ 59 - 10
Engine/source/windowManager/sdl/sdlWindow.cpp

@@ -161,10 +161,18 @@ void PlatformWindowSDL::_setVideoMode( const GFXVideoMode &mode )
 {
    mVideoMode = mode;
    mSuppressReset = true;
+   S32 newDisplay = Con::getIntVariable("pref::Video::deviceId", 0);
 
    // Set our window to have the right style based on the mode
    if(mode.fullScreen && !Platform::getWebDeployment() && !mOffscreenRender)
-   {     
+   {
+      SDL_Rect rect_sdl;
+      // Move the window onto the correct monitor before setting fullscreen
+      if (0 == SDL_GetDisplayBounds(newDisplay, &rect_sdl))
+      {
+         SDL_SetWindowPosition(mWindowHandle, rect_sdl.x, rect_sdl.y);
+      }
+
       setSize(mode.resolution);
 
       SDL_SetWindowFullscreen( mWindowHandle, SDL_WINDOW_FULLSCREEN);
@@ -187,10 +195,19 @@ void PlatformWindowSDL::_setVideoMode( const GFXVideoMode &mode )
          SDL_SetWindowFullscreen( mWindowHandle, 0);
       }
 
+      // Restore the window to it's original size/position before applying changes
+      SDL_RestoreWindow(mWindowHandle);
+
+      // pref::Video::deviceMode values 0-windowed, 1-borderless, 2-fullscreen
+      bool hasBorder = (0 == Con::getIntVariable("pref::Video::deviceMode", 0));
+      SDL_SetWindowBordered(mWindowHandle, hasBorder ? SDL_TRUE : SDL_FALSE);
       setSize(mode.resolution);
-      centerWindow();
+      SDL_SetWindowPosition(mWindowHandle, SDL_WINDOWPOS_CENTERED_DISPLAY(newDisplay), SDL_WINDOWPOS_CENTERED_DISPLAY(newDisplay));
+      if (hasBorder && Con::getBoolVariable("pref::Video::isMaximized", false))
+         SDL_MaximizeWindow(mWindowHandle);
    }
 
+   getScreenResChangeSignal().trigger(this, true);
    mSuppressReset = false;
 }
 
@@ -216,7 +233,7 @@ void PlatformWindowSDL::_setFullscreen(const bool fullscreen)
    if(fullscreen && !mOffscreenRender)
    {
       Con::printf("PlatformWindowSDL::setFullscreen (full) enter");
-      SDL_SetWindowFullscreen( mWindowHandle, SDL_WINDOW_FULLSCREEN_DESKTOP);
+      SDL_SetWindowFullscreen( mWindowHandle, SDL_WINDOW_FULLSCREEN);
    }
    else
    {
@@ -245,7 +262,7 @@ const char * PlatformWindowSDL::getCaption()
 
 void PlatformWindowSDL::setFocus()
 {
-   SDL_SetWindowInputFocus(mWindowHandle);
+   SDL_RaiseWindow(mWindowHandle);
 }
 
 void PlatformWindowSDL::setClientExtent( const Point2I newExtent )
@@ -322,11 +339,6 @@ void PlatformWindowSDL::centerWindow()
 bool PlatformWindowSDL::setSize( const Point2I &newSize )
 {
    SDL_SetWindowSize(mWindowHandle, newSize.x, newSize.y);
-
-   // Let GFX get an update about the new resolution
-   if (mTarget.isValid())
-      mTarget->resetMode();
-
    return true;
 }
 
@@ -475,6 +487,12 @@ void PlatformWindowSDL::_triggerMouseButtonNotify(const SDL_Event& event)
       case SDL_BUTTON_MIDDLE:
          button = 2;
          break;
+      case SDL_BUTTON_X1:
+         button = 3;
+         break;
+      case SDL_BUTTON_X2:
+         button = 4;
+         break;
       default:
          return;
    }
@@ -547,6 +565,24 @@ void PlatformWindowSDL::_triggerTextNotify(const SDL_Event& evt)
    }
 }
 
+void PlatformWindowSDL::_updateMonitorFromMove(const SDL_Event& evt)
+{
+   SDL_Rect sdlRect;
+   S32 monitorCount = SDL_GetNumVideoDisplays();
+   for (S32 index = 0; index < monitorCount; ++index)
+   {
+      if (0 == SDL_GetDisplayBounds(index, &sdlRect))
+      {
+         if ((evt.window.data1 >= sdlRect.x) && (evt.window.data1 < (sdlRect.x + sdlRect.w)) &&
+            (evt.window.data2 >= sdlRect.y) && (evt.window.data2 < (sdlRect.y + sdlRect.h)))
+         {
+            Con::setIntVariable("pref::Video::deviceId", index);
+            return;
+         }
+      }
+   }
+}
+
 void PlatformWindowSDL::_processSDLEvent(SDL_Event &evt)
 {
    switch(evt.type)
@@ -594,7 +630,11 @@ void PlatformWindowSDL::_processSDLEvent(SDL_Event &evt)
             case SDL_WINDOWEVENT_FOCUS_LOST:
                appEvent.trigger(getWindowId(), LoseFocus);
                break;
-            case SDL_WINDOWEVENT_MAXIMIZED:
+            case SDL_WINDOWEVENT_MOVED:
+            {
+               _updateMonitorFromMove(evt);
+               break;
+            }
             case SDL_WINDOWEVENT_RESIZED:
             {
                int width, height;
@@ -602,6 +642,7 @@ void PlatformWindowSDL::_processSDLEvent(SDL_Event &evt)
                mVideoMode.resolution.set(width, height);
                getGFXTarget()->resetMode();
                resizeEvent.trigger(getWindowId(), width, height);
+               getScreenResChangeSignal().trigger(this, true);
                break;
             }
             case SDL_WINDOWEVENT_CLOSE:
@@ -609,6 +650,14 @@ void PlatformWindowSDL::_processSDLEvent(SDL_Event &evt)
                appEvent.trigger(getWindowId(), WindowClose);
                mClosing = true;
             }
+            case SDL_WINDOWEVENT_MINIMIZED:
+               break;
+            case SDL_WINDOWEVENT_MAXIMIZED:
+               Con::setBoolVariable("pref::Video::isMaximized", true);
+               break;
+            case SDL_WINDOWEVENT_RESTORED:
+               Con::setBoolVariable("pref::Video::isMaximized", false);
+               break;
 
             default:
                break;

+ 1 - 0
Engine/source/windowManager/sdl/sdlWindow.h

@@ -98,6 +98,7 @@ private:
    void _triggerMouseWheelNotify(const SDL_Event& event);
    void _triggerKeyNotify(const SDL_Event& event);
    void _triggerTextNotify(const SDL_Event& event);
+   void _updateMonitorFromMove(const SDL_Event& event);
 
 public:
    PlatformWindowSDL();

+ 72 - 0
Engine/source/windowManager/sdl/sdlWindowMgr.cpp

@@ -143,6 +143,74 @@ RectI PlatformWindowManagerSDL::getMonitorRect(U32 index)
    return RectI(sdlRect.x, sdlRect.y, sdlRect.w, sdlRect.h);
 }
 
+RectI PlatformWindowManagerSDL::getMonitorUsableRect(U32 index)
+{
+   SDL_Rect sdlRect;
+   if (0 != SDL_GetDisplayUsableBounds(index, &sdlRect))
+   {
+      Con::errorf("SDL_GetDisplayUsableBounds() failed: %s", SDL_GetError());
+      return RectI(0, 0, 0, 0);
+   }
+
+   return RectI(sdlRect.x, sdlRect.y, sdlRect.w, sdlRect.h);
+}
+
+U32 PlatformWindowManagerSDL::getMonitorModeCount(U32 monitorIndex)
+{
+   S32 modeCount = SDL_GetNumDisplayModes(monitorIndex);
+   if (modeCount < 0)
+   {
+      Con::errorf("SDL_GetNumDisplayModes(%d) failed: %s", monitorIndex, SDL_GetError());
+      modeCount = 0;
+   }
+
+   return (U32)modeCount;
+}
+
+const String PlatformWindowManagerSDL::getMonitorMode(U32 monitorIndex, U32 modeIndex)
+{
+   SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
+   if (SDL_GetDisplayMode(monitorIndex, modeIndex, &mode) != 0)
+   {
+      Con::errorf("SDL_GetDisplayMode(%d, %d) failed: %s", monitorIndex, modeIndex, SDL_GetError());
+      return String::EmptyString;
+   }
+
+   GFXVideoMode vm;
+   vm.resolution.set(mode.w, mode.h);
+   vm.refreshRate = mode.refresh_rate;
+   vm.bitDepth = 32;
+   vm.antialiasLevel = 0;
+   vm.fullScreen = false;
+   vm.wideScreen = false;
+
+   return vm.toString();
+}
+
+const String PlatformWindowManagerSDL::getMonitorDesktopMode(U32 monitorIndex)
+{
+   SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
+   if (SDL_GetDesktopDisplayMode(monitorIndex, &mode) != 0)
+   {
+      Con::errorf("SDL_GetDesktopDisplayMode(%d) failed: %s", monitorIndex, SDL_GetError());
+      return String::EmptyString;
+   }
+
+   GFXVideoMode vm;
+   vm.resolution.set(mode.w, mode.h);
+   vm.refreshRate = mode.refresh_rate;
+
+   int bbp;
+   unsigned int r, g, b, a;
+   SDL_PixelFormatEnumToMasks(mode.format, &bbp, &r, &g, &b, &a);
+   vm.bitDepth = bbp;
+   vm.antialiasLevel = 0;
+   vm.fullScreen = false;
+   vm.wideScreen = ((mode.w / 16) * 9) == mode.h;
+
+   return vm.toString();
+}
+
 void PlatformWindowManagerSDL::getMonitorRegions(Vector<RectI> &regions)
 {
    SDL_Rect sdlRect;
@@ -251,6 +319,9 @@ PlatformWindow *PlatformWindowManagerSDL::createWindow(GFXDevice *device, const
 
    linkWindow(window);
 
+   SDL_SetWindowMinimumSize(window->mWindowHandle, Con::getIntVariable("$Video::minimumXResolution", 1024),
+         Con::getIntVariable("$Video::minimumYResolution", 720));
+
    return window;
 }
 
@@ -513,3 +584,4 @@ AFTER_MODULE_INIT(gfx)
    SDL_StopTextInput();
 #endif
 }
+

+ 5 - 1
Engine/source/windowManager/sdl/sdlWindowMgr.h

@@ -109,6 +109,10 @@ public:
    virtual U32 getMonitorCount();
    virtual const char* getMonitorName(U32 index);
    virtual RectI getMonitorRect(U32 index);
+   virtual RectI getMonitorUsableRect(U32 index);
+   virtual U32 getMonitorModeCount(U32 monitorIndex);
+   virtual const String getMonitorMode(U32 monitorIndex, U32 modeIndex);
+   virtual const String getMonitorDesktopMode(U32 monitorIndex);
 
    virtual void getMonitorRegions(Vector<RectI> &regions);
    virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode);
@@ -133,4 +137,4 @@ public:
    void updateSDLTextInputState(KeyboardInputState state);
 };
 
-#endif
+#endif

+ 147 - 81
Templates/BaseGame/game/core/gui/scripts/canvas.cs

@@ -54,102 +54,77 @@ $WORD::FULLSCREEN = 2;
 $WORD::BITDEPTH = 3;
 $WORD::REFRESH = 4;
 $WORD::AA = 5;
+$Video::ModeTags = "Windowed\tBorderless\tFullscreen";
+$Video::ModeWindowed = 0;
+$Video::ModeBorderless = 1;
+$Video::ModeFullscreen = 2;
+$Video::minimumXResolution = 1024;
+$Video::minimumYResolution = 720;
 
 function configureCanvas()
 {
    // Setup a good default if we don't have one already.
-   if ($pref::Video::Resolution $= "")
-      $pref::Video::Resolution = "800 600";
-   if ($pref::Video::FullScreen $= "")
-      $pref::Video::FullScreen = false;
-   if ($pref::Video::BitDepth $= "")
-      $pref::Video::BitDepth = "32";
-   if ($pref::Video::RefreshRate $= "")
-      $pref::Video::RefreshRate = "60";
-   if ($pref::Video::AA $= "")
-      $pref::Video::AA = "4";
+   if (($pref::Video::deviceId $= "") || ($pref::Video::deviceId < 0) ||
+         ($pref::Video::deviceId >= Canvas.getMonitorCount()))
+      $pref::Video::deviceId = 0;  // Monitor 0
 
-   %resX = $pref::Video::Resolution.x;
-   %resY = $pref::Video::Resolution.y;
-   %fs = $pref::Video::FullScreen;
-   %bpp = $pref::Video::BitDepth;
-   %rate = $pref::Video::RefreshRate;
-   %aa = $pref::Video::AA;
-   
-   if($cliFullscreen !$= "") {
-      %fs = $cliFullscreen;
-      $cliFullscreen = "";
+   if (($pref::Video::deviceMode $= "") || ($pref::Video::deviceMode < 0) ||
+      ($pref::Video::deviceMode >= getFieldCount($Video::ModeTags)))
+   {
+      $pref::Video::deviceMode = $Video::ModeBorderless;
+      $pref::Video::mode = Canvas.getBestCanvasRes($pref::Video::deviceId, $pref::Video::deviceMode);
+      Canvas.modeStrToPrefs($pref::Video::mode);
    }
-   
+
+   if($cliFullscreen !$= "")
+      $pref::Video::deviceMode = $cliFullscreen ? 2 : 0;
+
+   // Default to borderless at desktop resolution if there is no saved pref or
+   // command line arg
+   if (($pref::Video::Resolution $= "") || ($pref::Video::Resolution.x < $Video::minimumXResolution) ||
+      ($pref::Video::Resolution.y < $Video::minimumYResolution))
+   {
+      $pref::Video::mode = Canvas.getBestCanvasRes($pref::Video::deviceId, $pref::Video::deviceMode);
+      Canvas.modeStrToPrefs($pref::Video::mode);
+   }
+
+   if ($pref::Video::deviceMode != $Video::ModeFullscreen)
+      $pref::Video::FullScreen = false;
+   %modeStr = Canvas.prefsToModeStr();
+
    echo("--------------");
-   echo("Attempting to set resolution to \"" @ %resX SPC %resY SPC %fs SPC %bpp SPC %rate SPC %aa @ "\"");
-   
-   %deskRes    = getDesktopResolution();      
-   %deskResX   = getWord(%deskRes, $WORD::RES_X);
-   %deskResY   = getWord(%deskRes, $WORD::RES_Y);
-   %deskResBPP = getWord(%deskRes, 2);
-   
-   // We shouldn't be getting this any more but just in case...
-   if (%bpp $= "Default")
-      %bpp = %deskResBPP;
-      
+   echo("Attempting to set resolution to \"" @ %modeStr @ "\"");
+
    // Make sure we are running at a valid resolution
-   if (%fs $= "0" || %fs $= "false")
+   if (!Canvas.checkCanvasRes(%modeStr, $pref::Video::deviceId, $pref::Video::deviceMode, true))
    {
-      // Windowed mode has to use the same bit depth as the desktop
-      %bpp = %deskResBPP;
-      
-      // Windowed mode also has to run at a smaller resolution than the desktop
-      if ((%resX >= %deskResX) || (%resY >= %deskResY))
-      {
-         warn("Warning: The requested windowed resolution is equal to or larger than the current desktop resolution. Attempting to find a better resolution");
-      
-         %resCount = Canvas.getModeCount();
-         for (%i = (%resCount - 1); %i >= 0; %i--)
-         {
-            %testRes = Canvas.getMode(%i);
-            %testResX = getWord(%testRes, $WORD::RES_X);
-            %testResY = getWord(%testRes, $WORD::RES_Y);
-            %testBPP  = getWord(%testRes, $WORD::BITDEPTH);
-
-            if (%testBPP != %bpp)
-               continue;
-            
-            if ((%testResX < %deskResX) && (%testResY < %deskResY))
-            {
-               // This will work as our new resolution
-               %resX = %testResX;
-               %resY = %testResY;
-               
-               warn("Warning: Switching to \"" @ %resX SPC %resY SPC %bpp @ "\"");
-               
-               break;
-            }
-         }
-      }
+      %modeStr = Canvas.getBestCanvasRes($pref::Video::deviceId, $pref::Video::deviceMode);
+      Canvas.modeStrToPrefs(%modeStr);
    }
    
-   $pref::Video::Resolution = %resX SPC %resY;
-   $pref::Video::FullScreen = %fs;
-   $pref::Video::BitDepth = %bpp;
-   $pref::Video::RefreshRate = %rate;
-   $pref::Video::AA = %aa;
-   
-   if (%fs == 1 || %fs $= "true")
-      %fsLabel = "Yes";
-   else
-      %fsLabel = "No";
+   %fsLabel = getField($Video::ModeTags, $pref::Video::deviceMode);
+   %resX = $pref::Video::Resolution.x;
+   %resY = $pref::Video::Resolution.y;
+   %bpp  = $pref::Video::BitDepth;
+   %rate = $pref::Video::RefreshRate;
+   %fsaa = $pref::Video::AA;
+   %fs = ($pref::Video::deviceMode == 2);
 
    echo("Accepted Mode: " NL
-      "--Resolution : " @  %resX SPC %resY NL 
-      "--Full Screen : " @ %fsLabel NL
+      "--Resolution     : " @  %resX SPC %resY NL
+      "--Screen Mode    : " @ %fsLabel NL
       "--Bits Per Pixel : " @ %bpp NL
-      "--Refresh Rate : " @ %rate NL
-      "--AA TypeXLevel : " @ %aa NL
+      "--Refresh Rate   : " @ %rate NL
+      "--FSAA Level     : " @ %fsaa NL
       "--------------");
-      
+
    // Actually set the new video mode
    Canvas.setVideoMode(%resX, %resY, %fs, %bpp, %rate, %aa);
+   Canvas.setFocus();
+
+   // Lock and unlock the mouse to force the position to sync with the platform window
+   lockMouse(true);
+   lockMouse(false);
 
    commandToServer('setClientAspectRatio', %resX, %resY);
 
@@ -157,6 +132,97 @@ function configureCanvas()
    // We need to parse the setting between AA modes, and then it's level
    // It's formatted as AATypexAALevel
    // So, FXAAx4 or MLAAx2
-   if ( isObject( FXAAPostFX ) )
-      FXAAPostFX.isEnabled = ( %aa > 0 ) ? true : false;
+   if ( isObject( FXAA_PostEffect ) )
+      FXAA_PostEffect.isEnabled = ( %aa > 0 ) ? true : false;
+}
+
+function GuiCanvas::modeStrToPrefs(%this, %modeStr)
+{
+   $pref::Video::Resolution = %modeStr.x SPC %modeStr.y;
+   $pref::Video::FullScreen = getWord(%modeStr, $WORD::FULLSCREEN);
+   $pref::Video::BitDepth = getWord(%modeStr, $WORD::BITDEPTH);
+   $pref::Video::RefreshRate = getWord(%modeStr, $WORD::REFRESH);
+   $pref::Video::AA = getWord(%modeStr, $WORD::AA);
+}
+
+function GuiCanvas::prefsToModeStr(%this)
+{
+   %modeStr = $pref::Video::Resolution SPC $pref::Video::FullScreen SPC
+      $pref::Video::BitDepth SPC $pref::Video::RefreshRate SPC $pref::Video::AA;
+
+   return %modeStr;
+}
+
+function GuiCanvas::checkCanvasRes(%this, %mode, %deviceId, %deviceMode, %startup)
+{
+   %resX = getWord(%mode, $WORD::RES_X);
+   %resY = getWord(%mode, $WORD::RES_Y);
+
+   // Make sure it meets the minimum resolution requirement
+   if ((%resX < $Video::minimumXResolution) || (%resY < $Video::minimumYResolution))
+      return false;
+
+   if (%deviceMode == $Video::ModeWindowed)
+   {  // Windowed must be smaller than the device usable area
+      %deviceRect = getWords(%this.getMonitorUsableRect(%deviceId), 2);
+      if ((%resY > %deviceRect.y) || (%resX > (%deviceRect.x - 2)))
+         return false;
+      return true;
+   }
+   else if (%deviceMode == $Video::ModeBorderless)
+   {  // Borderless must be at or less than the device res
+      %deviceRect = getWords(%this.getMonitorRect(%deviceId), 2);
+      if ((%resX > %deviceRect.x) || (%resY > %deviceRect.y))
+         return false;
+
+      return true;
+   }
+
+   if (!%startup)
+      return true;
+
+   // Checking saved prefs, make sure the mode still exists
+   %bpp = getWord(%mode, $WORD::BITDEPTH);
+   %rate = getWord(%mode, $WORD::REFRESH);
+
+   %resCount = %this.getMonitorModeCount(%deviceId);
+   for (%i = (%resCount - 1); %i >= 0; %i--)
+   {
+      %testRes = %this.getMonitorMode(%deviceId, %i);
+      %testResX = getWord(%testRes, $WORD::RES_X);
+      %testResY = getWord(%testRes, $WORD::RES_Y);
+      %testBPP  = getWord(%testRes, $WORD::BITDEPTH);
+      %testRate = getWord(%testRes, $WORD::REFRESH);
+
+      if ((%testResX == %resX) && (%testResY == %resY) &&
+            (%testBPP == %bpp) && (%testRate == %rate))
+         return true;
+   }
+
+   return false;
+}
+
+// Find the best video mode setting for the device and display mode
+function GuiCanvas::getBestCanvasRes(%this, %deviceId, %deviceMode)
+{
+   if (%deviceMode == $Video::ModeWindowed)
+      %deviceRect = getWords(%this.getMonitorUsableRect(%deviceId), 2);
+   else
+      %deviceRect = getWords(%this.getMonitorRect(%deviceId), 2);
+
+   %resCount = %this.getModeCount();
+   for (%i = %resCount - 1; %i >= 0; %i--)
+   {
+      %testRes = %this.getMode(%i);
+      %resX = getWord(%testRes, $WORD::RES_X);
+      %resY = getWord(%testRes, $WORD::RES_Y);
+
+      if ((%resX > %deviceRect.x) || (%resY > %deviceRect.y))
+         continue;
+
+      return %testRes;
+   }
+
+   // Nothing found? return first mode
+   return %this.getMonitorMode(%deviceId, 0);
 }

+ 38 - 4
Templates/BaseGame/game/core/rendering/scripts/graphicsOptions.cs

@@ -721,14 +721,19 @@ function _makePrettyResString( %resString, %giveAspectRation )
    return %outRes;   
 }
 
-function getScreenResolutionList()
+function getScreenResolutionList(%deviceID, %deviceMode)
 {
    %returnsList = "";
-   
+
    %resCount = Canvas.getModeCount();
    for (%i = 0; %i < %resCount; %i++)
    {
       %testResString = Canvas.getMode( %i );
+
+      // Make sure it's valid for the monitor and mode selections
+      if (!Canvas.checkCanvasRes(%testResString, %deviceID, %deviceMode, false))
+         continue;
+
       %testRes = _makePrettyResString( %testResString );
       
       //sanitize
@@ -747,11 +752,40 @@ function getScreenResolutionList()
       if(%found)
          continue;
                      
-      if(%i != 0)
+      if(%returnsList !$= "")
          %returnsList = %returnsList @ "\t" @ %testRes;
       else
          %returnsList = %testRes;
    }
-   
+
+   return %returnsList;
+}
+
+// Return a sorted tab-separated list of all refresh rates available for %resolution.
+function getScreenRefreshList(%resolution)
+{
+   %rateArray = new ArrayObject();
+   %resCount = Canvas.getModeCount();
+   for (%i = 0; %i < %resCount; %i++)
+   {
+      %testRes = Canvas.getMode(%i);
+      if ((%testRes.x != %resolution.x) || (%testRes.y != %resolution.y))
+         continue;
+      %rate = getWord(%testRes, $WORD::REFRESH);
+      if (%rateArray.getIndexFromKey(%rate) == -1)
+         %rateArray.add(%rate, %rate);
+   }
+
+   %rateArray.sort(true);
+   %returnsList = "";
+   for (%i = 0; %i < %rateArray.count(); %i++)
+   {
+      %rate = %rateArray.getKey(%i);
+      %returnsList = %returnsList @ ((%i == 0) ? %rate : ("\t" @ %rate));
+   }
+   if (%returnsList $= "")
+      %returnsList = "60";
+
+   %rateArray.delete();
    return %returnsList;
 }

+ 0 - 5
Templates/BaseGame/game/data/defaults.cs

@@ -27,11 +27,6 @@ $sceneLighting::cacheLighting = 1;
 
 $pref::Video::displayDevice = "D3D11";
 $pref::Video::disableVerticalSync = 1;
-$pref::Video::Resolution = "1024 768";
-$pref::Video::FullScreen = false;
-$pref::Video::BitDepth = "32";
-$pref::Video::RefreshRate = "60";
-$pref::Video::AA = "4";
 $pref::Video::defaultFenceCount = 0;
 $pref::Video::screenShotSession = 0;
 $pref::Video::screenShotFormat = "PNG";

+ 164 - 45
Templates/BaseGame/game/data/ui/guis/optionsMenu.cs

@@ -196,13 +196,44 @@ function OptionsMenu::populateDisplaySettingsList(%this)
    OptionName.setText("");
    OptionDescription.setText("");
    
-   %resolutionList = getScreenResolutionList();
    OptionsMenuSettingsList.addOptionRow("Display API", "D3D11\tOpenGL", false, "", -1, -30, true, "The display API used for rendering.", $pref::Video::displayDevice);
-   OptionsMenuSettingsList.addOptionRow("Resolution", %resolutionList, false, "", -1, -30, true, "Resolution of the game window", _makePrettyResString( $pref::Video::mode ));
-   OptionsMenuSettingsList.addOptionRow("Fullscreen", "No\tYes", false, "", -1, -30, true, "", convertBoolToYesNo($pref::Video::FullScreen));
-   OptionsMenuSettingsList.addOptionRow("VSync", "No\tYes", false, "", -1, -30, true, "", convertBoolToYesNo(!$pref::Video::disableVerticalSync));
    
-   OptionsMenuSettingsList.addOptionRow("Refresh Rate", "30\t60\t75", false, "", -1, -30, true, "", $pref::Video::RefreshRate);
+   %numDevices = Canvas.getMonitorCount();
+   %devicesList = "";
+   for(%i = 0; %i < %numDevices; %i++)
+   {
+      %device = (%i+1) @ " - " @ Canvas.getMonitorName(%i);
+      if(%i==0)
+         %devicesList = %device;
+      else
+         %devicesList = %devicesList @ "\t" @ %device;
+   }
+   
+   %selectedDevice = getField(%devicesList, $pref::Video::deviceId);
+   OptionsMenuSettingsList.addOptionRow("Display Device", %devicesList, false, "onDisplayModeChange", -1, -30, true, "The display devices the window should be on.", %selectedDevice);
+      
+   if (%numDevices > 1)
+      OptionsMenuSettingsList.setRowEnabled(1, true);
+   else
+      OptionsMenuSettingsList.setRowEnabled(1, false);
+   
+   %mode = getField($Video::ModeTags, $pref::Video::deviceMode);
+   OptionsMenuSettingsList.addOptionRow("Window Mode", $Video::ModeTags, false, "onDisplayModeChange", -1, -30, true, "", %mode);
+   
+   %resolutionList = getScreenResolutionList($pref::Video::deviceId, $pref::Video::deviceMode);
+   OptionsMenuSettingsList.addOptionRow("Resolution", %resolutionList, false, "onDisplayResChange", -1, -30, true, "Resolution of the game window", _makePrettyResString( $pref::Video::mode ));
+   
+   //If they're doing borderless, the window resolution must match the display resolution
+   if(%mode !$= "Borderless")
+      OptionsMenuSettingsList.setRowEnabled(3, true);
+   else
+      OptionsMenuSettingsList.setRowEnabled(3, false);
+   
+   OptionsMenuSettingsList.addOptionRow("VSync", "No\tYes", false, "", -1, -30, true, "", convertBoolToYesNo(!$pref::Video::disableVerticalSync));
+
+
+   %refreshList = getScreenRefreshList($pref::Video::mode);
+   OptionsMenuSettingsList.addOptionRow("Refresh Rate", %refreshList, false, "", -1, -30, true, "", $pref::Video::RefreshRate);
    
    //move to gameplay tab
    OptionsMenuSettingsList.addSliderRow("Field of View", 75, 5, "65 100", "", -1, -30);
@@ -215,20 +246,8 @@ function OptionsMenu::populateDisplaySettingsList(%this)
 
 function OptionsMenu::applyDisplaySettings(%this)
 {
-   //%newAdapter    = GraphicsMenuDriver.getText();
-	//%numAdapters   = GFXInit::getAdapterCount();
 	%newDevice     = OptionsMenuSettingsList.getCurrentOption(0);
 							
-	/*for( %i = 0; %i < %numAdapters; %i ++ )
-	{
-	   %targetAdapter = GFXInit::getAdapterName( %i );
-	   if( GFXInit::getAdapterName( %i ) $= %newDevice )
-	   {
-	      %newDevice = GFXInit::getAdapterType( %i );
-	      break;
-	   }
-	}*/
-	   
    // Change the device.
    if ( %newDevice !$= $pref::Video::displayDevice )
    {
@@ -324,9 +343,16 @@ function OptionsMenu::applyGraphicsSettings(%this)
                                  
       $pref::Video::defaultAnisotropy = %level;
    }
-   
-   updateDisplaySettings();
-   
+
+   %newFSAA = OptionsMenuSettingsList.getCurrentOption(9);
+   if (%newFSAA $= "off")
+      %newFSAA = 0;
+   if (%newFSAA !$= $pref::Video::AA)
+   {
+      $pref::Video::AA = %newFSAA;
+      configureCanvas();
+   }
+
    echo("Exporting client prefs");
    %prefPath = getPrefpath();
    export("$pref::*", %prefPath @ "/clientPrefs.cs", false);
@@ -335,37 +361,58 @@ function OptionsMenu::applyGraphicsSettings(%this)
 function updateDisplaySettings()
 {
    //Update the display settings now
-   $pref::Video::Resolution = getWord(OptionsMenuSettingsList.getCurrentOption(1), 0) SPC getWord(OptionsMenuSettingsList.getCurrentOption(1), 2);
-   %newBpp        = 32; // ... its not 1997 anymore.
-	$pref::Video::FullScreen = convertOptionToBool(OptionsMenuSettingsList.getCurrentOption(2)) == 0 ? "false" : "true";
-	$pref::Video::RefreshRate    = OptionsMenuSettingsList.getCurrentOption(4);
-	%newVsync = !convertOptionToBool(OptionsMenuSettingsList.getCurrentOption(3));	
-	//$pref::Video::AA = GraphicsMenuAA.getSelected();
-	
-   /*if ( %newFullScreen $= "false" )
-	{
-      // If we're in windowed mode switch the fullscreen check
-      // if the resolution is bigger than the desktop.
-      %deskRes    = getDesktopResolution();      
-      %deskResX   = getWord(%deskRes, $WORD::RES_X);
-      %deskResY   = getWord(%deskRes, $WORD::RES_Y);
-	   if (  getWord( %newRes, $WORD::RES_X ) > %deskResX || 
-	         getWord( %newRes, $WORD::RES_Y ) > %deskResY )
-      {
-         $pref::Video::FullScreen = "true";
-         GraphicsMenuFullScreen.setStateOn( true );
-      }
-	}*/
+   %deviceName = OptionsMenuSettingsList.getCurrentOption(1);
+   %newDeviceID = getWord(%deviceName, 0) - 1;
+   %deviceModeName = OptionsMenuSettingsList.getCurrentOption(2);
+   %newDeviceMode = 0;
+   foreach$(%modeName in $Video::ModeTags)
+   {
+      if (%deviceModeName $= %modeName)
+         break;
+      else
+         %newDeviceMode++;
+   }
 
+   %newRes = getWord(OptionsMenuSettingsList.getCurrentOption(3), 0) SPC getWord(OptionsMenuSettingsList.getCurrentOption(3), 2);
+   %newBpp = 32; // ... its not 1997 anymore.
+	%newFullScreen = %deviceModeName $= "Fullscreen" ? true : false;
+	%newRefresh    = OptionsMenuSettingsList.getCurrentOption(5);
+	%newVsync = !convertOptionToBool(OptionsMenuSettingsList.getCurrentOption(4));	
+	%newFSAA = $pref::Video::AA;
+	
    // Build the final mode string.
-	%newMode = $pref::Video::Resolution SPC $pref::Video::FullScreen SPC %newBpp SPC $pref::Video::RefreshRate SPC $pref::Video::AA;
+	%newMode = %newRes SPC %newFullScreen SPC %newBpp SPC %newRefresh SPC %newFSAA;
 	
    // Change the video mode.   
-   if (  %newMode !$= $pref::Video::mode || 
-         %newVsync != $pref::Video::disableVerticalSync )
+   if (  %newMode !$= $pref::Video::mode || %newDeviceID != $pref::Video::deviceId ||
+         %newVsync != $pref::Video::disableVerticalSync || %newDeviceMode != $pref::Video::deviceMode)
    {
+      if ( %testNeedApply )
+         return true;
+
+      //****Edge Case Hack
+      // If we're in fullscreen mode and switching to a different monitor at the
+      // same resolution and maintaining fullscreen, GFX...WindowTarget::resetMode()
+      // will early-out because there is no "mode change" and the monitor change
+      // will not get applied. Instead of modifying platform code, we're going to
+      // move onto the new monitor in borderless and immediately switch to FS.
+      if (%newFullScreen && $pref::Video::FullScreen &&
+         ($pref::Video::Resolution $= %newRes) && ($pref::Video::deviceId != %newDeviceID))
+      {
+         $pref::Video::deviceId = %newDeviceID;
+         $pref::Video::deviceMode = $Video::ModeBorderless;
+         %tmpModeStr = Canvas.getMonitorMode(%newDeviceID, 0);
+         Canvas.setVideoMode(%tmpModeStr.x, %tmpModeStr.y, false, 32, getWord(%tmpModeStr, $WORD::REFRESH), %aa);
+      }
+
       $pref::Video::mode = %newMode;
-      $pref::Video::disableVerticalSync = %newVsync;      
+      $pref::Video::disableVerticalSync = %newVsync;
+      $pref::Video::deviceId = %newDeviceID;
+      $pref::Video::deviceMode = %newDeviceMode;
+      $pref::Video::Resolution = %newRes;
+      $pref::Video::FullScreen = %newFullScreen;
+      $pref::Video::RefreshRate = %newRefresh;
+      $pref::Video::AA = %newFSAA;
       configureCanvas();
    }
 }
@@ -537,4 +584,76 @@ function convertBoolToOnOff(%val)
       return "On";
    else 
       return "Off";
+}
+
+function onDisplayModeChange(%val)
+{  
+   // The display device (monitor) or screen mode has changed. Refill the
+   // resolution list with only available options.
+   %deviceName = OptionsMenuSettingsList.getCurrentOption(1);
+   %newDeviceID = getWord(%deviceName, 0) - 1;
+   %deviceModeName = OptionsMenuSettingsList.getCurrentOption(2);
+   %newDeviceMode = 0;
+   foreach$(%modeName in $Video::ModeTags)
+   {
+      if (%deviceModeName $= %modeName)
+         break;
+      else
+         %newDeviceMode++;
+   }
+   %resolutionList = getScreenResolutionList(%newDeviceID, %newDeviceMode);
+
+   // If we're switching to borderless, default to monitor res
+   if (%newDeviceMode == $Video::ModeBorderless)
+      %newRes = getWords(Canvas.getMonitorRect(%newDeviceID), 2);
+   else
+   {  // Otherwise, if our old resolution is still in the list, attempt to reset it.
+      %oldRes = getWord(OptionsMenuSettingsList.getCurrentOption(3), 0) SPC getWord(OptionsMenuSettingsList.getCurrentOption(3), 2);
+
+      %found = false;
+      %retCount = getFieldCount(%resolutionList);
+      for (%i = 0; %i < %retCount; %i++)
+      {
+         %existingEntry = getField(%resolutionList, %i);
+         if ((%existingEntry.x $= %oldRes.x) && (%existingEntry.z $= %oldRes.y))
+         {
+            %found = true;
+            %newRes = %oldRes;
+            break;  
+         }
+      }
+
+      if (!%found)
+      {  // Pick the best resoltion available for the device and mode
+         %newRes = Canvas.getBestCanvasRes(%newDeviceID, %newDeviceMode);
+      }
+   }
+   
+   if(%newDeviceMode == $Video::ModeBorderless)
+      OptionsMenuSettingsList.setRowEnabled(3, false);
+   else
+      OptionsMenuSettingsList.setRowEnabled(3, true);
+      
+   OptionsMenuSettingsList.setOptions(3, %resolutionList);
+   OptionsMenuSettingsList.selectOption(3, _makePrettyResString(%newRes));
+}
+
+function onDisplayResChange(%val)
+{  // The resolution has changed. Setup refresh rates available at this res.
+   %newRes = getWord(OptionsMenuSettingsList.getCurrentOption(3), 0) SPC getWord(OptionsMenuSettingsList.getCurrentOption(3), 2);
+   %refreshList = getScreenRefreshList(%newRes);
+
+   // If our old rate still exists, select it
+   %oldRate = OptionsMenuSettingsList.getCurrentOption(5);
+   %retCount = getFieldCount(%refreshList);
+   for (%i = 0; %i < %retCount; %i++)
+   {
+      %existingEntry = getField(%refreshList, %i);
+      %newRate = %existingEntry;
+      if (%existingEntry $= %oldRate)
+         break;  
+   }
+
+   OptionsMenuSettingsList.setOptions(5, %refreshList);
+   OptionsMenuSettingsList.selectOption(5, %newRate);
 }