diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index 6c93cd875..c3725f7aa 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/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" diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.h b/Engine/source/gui/controls/guiGameListMenuCtrl.h index 0ec0ec81a..87c5aa769 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.h +++ b/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. diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index 1d8d109f8..f50cc0152 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/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 ),, diff --git a/Engine/source/windowManager/platformWindowMgr.h b/Engine/source/windowManager/platformWindowMgr.h index 41fcd8c9d..d2279c202 100644 --- a/Engine/source/windowManager/platformWindowMgr.h +++ b/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 ®ions) = 0; @@ -152,4 +169,4 @@ private: /// need to get the window manager from somewhere else. PlatformWindowManager *CreatePlatformWindowManager(); -#endif \ No newline at end of file +#endif diff --git a/Engine/source/windowManager/sdl/sdlWindow.cpp b/Engine/source/windowManager/sdl/sdlWindow.cpp index e863c730f..2faccb555 100644 --- a/Engine/source/windowManager/sdl/sdlWindow.cpp +++ b/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; diff --git a/Engine/source/windowManager/sdl/sdlWindow.h b/Engine/source/windowManager/sdl/sdlWindow.h index f96f2d1bf..205945963 100644 --- a/Engine/source/windowManager/sdl/sdlWindow.h +++ b/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(); diff --git a/Engine/source/windowManager/sdl/sdlWindowMgr.cpp b/Engine/source/windowManager/sdl/sdlWindowMgr.cpp index f937ba97e..4a00294c8 100644 --- a/Engine/source/windowManager/sdl/sdlWindowMgr.cpp +++ b/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 ®ions) { 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 } + diff --git a/Engine/source/windowManager/sdl/sdlWindowMgr.h b/Engine/source/windowManager/sdl/sdlWindowMgr.h index fd8a762df..45cd3bfe3 100644 --- a/Engine/source/windowManager/sdl/sdlWindowMgr.h +++ b/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 ®ions); virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode); @@ -133,4 +137,4 @@ public: void updateSDLTextInputState(KeyboardInputState state); }; -#endif \ No newline at end of file +#endif diff --git a/Templates/BaseGame/game/core/gui/scripts/canvas.cs b/Templates/BaseGame/game/core/gui/scripts/canvas.cs index ee0be71a7..55fa1735a 100644 --- a/Templates/BaseGame/game/core/gui/scripts/canvas.cs +++ b/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 + 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 \"" @ %modeStr @ "\""); + + // Make sure we are running at a valid resolution + if (!Canvas.checkCanvasRes(%modeStr, $pref::Video::deviceId, $pref::Video::deviceMode, true)) + { + %modeStr = Canvas.getBestCanvasRes($pref::Video::deviceId, $pref::Video::deviceMode); + Canvas.modeStrToPrefs(%modeStr); + } + + %fsLabel = getField($Video::ModeTags, $pref::Video::deviceMode); %resX = $pref::Video::Resolution.x; %resY = $pref::Video::Resolution.y; - %fs = $pref::Video::FullScreen; - %bpp = $pref::Video::BitDepth; + %bpp = $pref::Video::BitDepth; %rate = $pref::Video::RefreshRate; - %aa = $pref::Video::AA; - - if($cliFullscreen !$= "") { - %fs = $cliFullscreen; - $cliFullscreen = ""; - } - - 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; - - // Make sure we are running at a valid resolution - if (%fs $= "0" || %fs $= "false") - { - // 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; - } - } - } - } - - $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"; + %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); } diff --git a/Templates/BaseGame/game/core/rendering/scripts/graphicsOptions.cs b/Templates/BaseGame/game/core/rendering/scripts/graphicsOptions.cs index c833efe1d..94f8c38be 100644 --- a/Templates/BaseGame/game/core/rendering/scripts/graphicsOptions.cs +++ b/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; } \ No newline at end of file diff --git a/Templates/BaseGame/game/data/defaults.cs b/Templates/BaseGame/game/data/defaults.cs index 1f91fce94..eaea83a8d 100644 --- a/Templates/BaseGame/game/data/defaults.cs +++ b/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"; diff --git a/Templates/BaseGame/game/data/ui/guis/optionsMenu.cs b/Templates/BaseGame/game/data/ui/guis/optionsMenu.cs index c7a8017eb..f4fd33318 100644 --- a/Templates/BaseGame/game/data/ui/guis/optionsMenu.cs +++ b/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); } \ No newline at end of file