| 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
| 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
| 3 | #include "menus.h" |
| 4 | |
| 5 | #include <base/str.h> |
| 6 | |
| 7 | #include <engine/graphics.h> |
| 8 | #include <engine/shared/config.h> |
| 9 | |
| 10 | #include <game/client/components/tooltips.h> |
| 11 | #include <game/client/gameclient.h> |
| 12 | #include <game/client/ui.h> |
| 13 | #include <game/client/ui_listbox.h> |
| 14 | #include <game/client/ui_scrollregion.h> |
| 15 | #include <game/localization.h> |
| 16 | |
| 17 | #include <array> |
| 18 | #include <memory> |
| 19 | #include <numeric> |
| 20 | #include <string> |
| 21 | #include <vector> |
| 22 | |
| 23 | void CMenus::(CUIRect MainView) |
| 24 | { |
| 25 | CUIRect Button; |
| 26 | char aBuf[128]; |
| 27 | bool CheckSettings = false; |
| 28 | |
| 29 | static const int MAX_RESOLUTIONS = 256; |
| 30 | static CVideoMode s_aModes[MAX_RESOLUTIONS]; |
| 31 | static int s_NumNodes = Graphics()->GetVideoModes(pModes: s_aModes, MaxModes: MAX_RESOLUTIONS, Screen: g_Config.m_GfxScreen); |
| 32 | static int s_GfxFsaaSamples = g_Config.m_GfxFsaaSamples; |
| 33 | static bool s_GfxBackendChanged = false; |
| 34 | static bool s_GfxGpuChanged = false; |
| 35 | |
| 36 | static int s_InitDisplayAllVideoModes = g_Config.m_GfxDisplayAllVideoModes; |
| 37 | |
| 38 | static bool s_WasInit = false; |
| 39 | static bool s_ModesReload = false; |
| 40 | if(!s_WasInit) |
| 41 | { |
| 42 | s_WasInit = true; |
| 43 | |
| 44 | Graphics()->AddWindowPropChangeListener(pFunc: []() { |
| 45 | s_ModesReload = true; |
| 46 | }); |
| 47 | } |
| 48 | |
| 49 | if(s_ModesReload || g_Config.m_GfxDisplayAllVideoModes != s_InitDisplayAllVideoModes) |
| 50 | { |
| 51 | s_NumNodes = Graphics()->GetVideoModes(pModes: s_aModes, MaxModes: MAX_RESOLUTIONS, Screen: g_Config.m_GfxScreen); |
| 52 | s_ModesReload = false; |
| 53 | s_InitDisplayAllVideoModes = g_Config.m_GfxDisplayAllVideoModes; |
| 54 | } |
| 55 | |
| 56 | CUIRect ModeList, ModeLabel; |
| 57 | MainView.VSplitLeft(Cut: 350.0f, pLeft: &MainView, pRight: &ModeList); |
| 58 | ModeList.HSplitTop(Cut: 24.0f, pTop: &ModeLabel, pBottom: &ModeList); |
| 59 | MainView.VSplitLeft(Cut: 340.0f, pLeft: &MainView, pRight: nullptr); |
| 60 | |
| 61 | // display mode list |
| 62 | static CListBox s_ListBox; |
| 63 | static const float sc_RowHeightResList = 22.0f; |
| 64 | static const float = 12.0f; |
| 65 | static const float sc_FontSizeResList = 10.0f; |
| 66 | |
| 67 | { |
| 68 | int G = std::gcd(m: g_Config.m_GfxScreenWidth, n: g_Config.m_GfxScreenHeight); |
| 69 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s: %dx%d @%dhz (%d:%d)" , |
| 70 | Localize(pStr: "Current" ), |
| 71 | (int)(g_Config.m_GfxScreenWidth * Graphics()->ScreenHiDPIScale()), |
| 72 | (int)(g_Config.m_GfxScreenHeight * Graphics()->ScreenHiDPIScale()), |
| 73 | g_Config.m_GfxScreenRefreshRate, |
| 74 | g_Config.m_GfxScreenWidth / G, |
| 75 | g_Config.m_GfxScreenHeight / G); |
| 76 | Ui()->DoLabel(pRect: &ModeLabel, pText: aBuf, Size: sc_FontSizeResListHeader, Align: TEXTALIGN_MC); |
| 77 | } |
| 78 | |
| 79 | int OldSelected = -1; |
| 80 | s_ListBox.SetActive(!Ui()->IsPopupOpen()); |
| 81 | s_ListBox.DoStart(RowHeight: sc_RowHeightResList, NumItems: s_NumNodes, ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: OldSelected, pRect: &ModeList); |
| 82 | |
| 83 | for(int i = 0; i < s_NumNodes; ++i) |
| 84 | { |
| 85 | if(g_Config.m_GfxScreenWidth == s_aModes[i].m_WindowWidth && |
| 86 | g_Config.m_GfxScreenHeight == s_aModes[i].m_WindowHeight && |
| 87 | g_Config.m_GfxScreenRefreshRate == s_aModes[i].m_RefreshRate) |
| 88 | { |
| 89 | OldSelected = i; |
| 90 | } |
| 91 | |
| 92 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &s_aModes[i], Selected: OldSelected == i); |
| 93 | if(!Item.m_Visible) |
| 94 | continue; |
| 95 | |
| 96 | int G = std::gcd(m: s_aModes[i].m_WindowWidth, n: s_aModes[i].m_WindowHeight); |
| 97 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: " %dx%d @%dhz (%d:%d)" , s_aModes[i].m_CanvasWidth, s_aModes[i].m_CanvasHeight, s_aModes[i].m_RefreshRate, s_aModes[i].m_WindowWidth / G, s_aModes[i].m_WindowHeight / G); |
| 98 | Ui()->DoLabel(pRect: &Item.m_Rect, pText: aBuf, Size: sc_FontSizeResList, Align: TEXTALIGN_ML); |
| 99 | } |
| 100 | |
| 101 | const int NewSelected = s_ListBox.DoEnd(); |
| 102 | if(OldSelected != NewSelected) |
| 103 | { |
| 104 | g_Config.m_GfxScreenWidth = s_aModes[NewSelected].m_WindowWidth; |
| 105 | g_Config.m_GfxScreenHeight = s_aModes[NewSelected].m_WindowHeight; |
| 106 | g_Config.m_GfxScreenRefreshRate = s_aModes[NewSelected].m_RefreshRate; |
| 107 | Graphics()->ResizeToScreen(); |
| 108 | } |
| 109 | |
| 110 | // switches |
| 111 | CUIRect WindowModeDropDown; |
| 112 | MainView.HSplitTop(Cut: 20.0f, pTop: &WindowModeDropDown, pBottom: &MainView); |
| 113 | |
| 114 | const char *apWindowModes[] = {Localize(pStr: "Windowed" ), Localize(pStr: "Windowed borderless" ), Localize(pStr: "Windowed fullscreen" ), Localize(pStr: "Desktop fullscreen" ), Localize(pStr: "Fullscreen" )}; |
| 115 | static const int s_NumWindowMode = std::size(apWindowModes); |
| 116 | |
| 117 | const int OldWindowMode = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 4 : (g_Config.m_GfxFullscreen == 2 ? 3 : 2)) : (g_Config.m_GfxBorderless ? 1 : 0)); |
| 118 | |
| 119 | static CUi::SDropDownState s_WindowModeDropDownState; |
| 120 | static CScrollRegion s_WindowModeDropDownScrollRegion; |
| 121 | s_WindowModeDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_WindowModeDropDownScrollRegion; |
| 122 | const int NewWindowMode = Ui()->DoDropDown(pRect: &WindowModeDropDown, CurSelection: OldWindowMode, pStrs: apWindowModes, Num: s_NumWindowMode, State&: s_WindowModeDropDownState); |
| 123 | if(OldWindowMode != NewWindowMode) |
| 124 | { |
| 125 | if(NewWindowMode == 0) |
| 126 | Graphics()->SetWindowParams(FullscreenMode: 0, IsBorderless: false); |
| 127 | else if(NewWindowMode == 1) |
| 128 | Graphics()->SetWindowParams(FullscreenMode: 0, IsBorderless: true); |
| 129 | else if(NewWindowMode == 2) |
| 130 | Graphics()->SetWindowParams(FullscreenMode: 3, IsBorderless: false); |
| 131 | else if(NewWindowMode == 3) |
| 132 | Graphics()->SetWindowParams(FullscreenMode: 2, IsBorderless: false); |
| 133 | else if(NewWindowMode == 4) |
| 134 | Graphics()->SetWindowParams(FullscreenMode: 1, IsBorderless: false); |
| 135 | } |
| 136 | |
| 137 | if(Graphics()->GetNumScreens() > 1) |
| 138 | { |
| 139 | CUIRect ScreenDropDown; |
| 140 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 141 | MainView.HSplitTop(Cut: 20.0f, pTop: &ScreenDropDown, pBottom: &MainView); |
| 142 | |
| 143 | const int NumScreens = Graphics()->GetNumScreens(); |
| 144 | static std::vector<std::string> s_vScreenNames; |
| 145 | static std::vector<const char *> s_vpScreenNames; |
| 146 | s_vScreenNames.resize(sz: NumScreens); |
| 147 | s_vpScreenNames.resize(sz: NumScreens); |
| 148 | |
| 149 | for(int i = 0; i < NumScreens; ++i) |
| 150 | { |
| 151 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s %d: %s" , Localize(pStr: "Screen" ), i, Graphics()->GetScreenName(Screen: i)); |
| 152 | s_vScreenNames[i] = aBuf; |
| 153 | s_vpScreenNames[i] = s_vScreenNames[i].c_str(); |
| 154 | } |
| 155 | |
| 156 | static CUi::SDropDownState s_ScreenDropDownState; |
| 157 | static CScrollRegion s_ScreenDropDownScrollRegion; |
| 158 | s_ScreenDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_ScreenDropDownScrollRegion; |
| 159 | const int NewScreen = Ui()->DoDropDown(pRect: &ScreenDropDown, CurSelection: g_Config.m_GfxScreen, pStrs: s_vpScreenNames.data(), Num: s_vpScreenNames.size(), State&: s_ScreenDropDownState); |
| 160 | if(NewScreen != g_Config.m_GfxScreen) |
| 161 | Graphics()->SwitchWindowScreen(Index: NewScreen, MoveToCenter: true); |
| 162 | } |
| 163 | |
| 164 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 165 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 166 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s (%s)" , Localize(pStr: "V-Sync" ), Localize(pStr: "may cause delay" )); |
| 167 | if(DoButton_CheckBox(pId: &g_Config.m_GfxVsync, pText: aBuf, Checked: g_Config.m_GfxVsync, pRect: &Button)) |
| 168 | { |
| 169 | Graphics()->SetVSync(!g_Config.m_GfxVsync); |
| 170 | } |
| 171 | |
| 172 | bool MultiSamplingChanged = false; |
| 173 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 174 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s (%s)" , Localize(pStr: "FSAA samples" ), Localize(pStr: "may cause delay" )); |
| 175 | int GfxFsaaSamplesMouseButton = DoButton_CheckBox_Number(pId: &g_Config.m_GfxFsaaSamples, pText: aBuf, Checked: g_Config.m_GfxFsaaSamples, pRect: &Button); |
| 176 | int CurFSAA = g_Config.m_GfxFsaaSamples == 0 ? 1 : g_Config.m_GfxFsaaSamples; |
| 177 | if(GfxFsaaSamplesMouseButton == 1) // inc |
| 178 | { |
| 179 | g_Config.m_GfxFsaaSamples = std::pow(x: 2, y: (int)std::log2(x: CurFSAA) + 1); |
| 180 | if(g_Config.m_GfxFsaaSamples > 64) |
| 181 | g_Config.m_GfxFsaaSamples = 0; |
| 182 | MultiSamplingChanged = true; |
| 183 | } |
| 184 | else if(GfxFsaaSamplesMouseButton == 2) // dec |
| 185 | { |
| 186 | if(CurFSAA == 1) |
| 187 | g_Config.m_GfxFsaaSamples = 64; |
| 188 | else if(CurFSAA == 2) |
| 189 | g_Config.m_GfxFsaaSamples = 0; |
| 190 | else |
| 191 | g_Config.m_GfxFsaaSamples = std::pow(x: 2, y: (int)std::log2(x: CurFSAA) - 1); |
| 192 | MultiSamplingChanged = true; |
| 193 | } |
| 194 | |
| 195 | uint32_t MultiSamplingCountBackend = 0; |
| 196 | if(MultiSamplingChanged) |
| 197 | { |
| 198 | if(Graphics()->SetMultiSampling(ReqMultiSamplingCount: g_Config.m_GfxFsaaSamples, MultiSamplingCountBackend)) |
| 199 | { |
| 200 | // try again with 0 if mouse click was increasing multi sampling |
| 201 | // else just accept the current value as is |
| 202 | if((uint32_t)g_Config.m_GfxFsaaSamples > MultiSamplingCountBackend && GfxFsaaSamplesMouseButton == 1) |
| 203 | Graphics()->SetMultiSampling(ReqMultiSamplingCount: 0, MultiSamplingCountBackend); |
| 204 | g_Config.m_GfxFsaaSamples = (int)MultiSamplingCountBackend; |
| 205 | } |
| 206 | else |
| 207 | { |
| 208 | CheckSettings = true; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 213 | if(DoButton_CheckBox(pId: &g_Config.m_GfxHighDetail, pText: Localize(pStr: "High Detail" ), Checked: g_Config.m_GfxHighDetail, pRect: &Button)) |
| 214 | g_Config.m_GfxHighDetail ^= 1; |
| 215 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_GfxHighDetail, pNearRect: &Button, pText: Localize(pStr: "Allows maps to render with more detail" )); |
| 216 | |
| 217 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 218 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowfps, pText: Localize(pStr: "Show FPS" ), Checked: g_Config.m_ClShowfps, pRect: &Button)) |
| 219 | g_Config.m_ClShowfps ^= 1; |
| 220 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClShowfps, pNearRect: &Button, pText: Localize(pStr: "Renders your frame rate in the top right" )); |
| 221 | |
| 222 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 223 | str_copy(dst&: aBuf, src: " " ); |
| 224 | str_append(dst&: aBuf, src: Localize(pStr: "Hz" , pContext: "Hertz" )); |
| 225 | Ui()->DoScrollbarOption(pId: &g_Config.m_GfxRefreshRate, pOption: &g_Config.m_GfxRefreshRate, pRect: &Button, pStr: Localize(pStr: "Refresh Rate" ), Min: 10, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_NOCLAMPVALUE | CUi::SCROLLBAR_OPTION_DELAYUPDATE, pSuffix: aBuf); |
| 226 | |
| 227 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 228 | static CButtonContainer s_UiColorResetId; |
| 229 | DoLine_ColorPicker(pResetId: &s_UiColorResetId, LineSize: 25.0f, LabelSize: 13.0f, BottomMargin: 2.0f, pMainRect: &MainView, pText: Localize(pStr: "UI Color" ), pColorValue: &g_Config.m_UiColor, DefaultColor: color_cast<ColorRGBA>(hsl: ColorHSLA(0xE4A046AFU, true)), CheckBoxSpacing: false, pCheckBoxValue: nullptr, Alpha: true); |
| 230 | |
| 231 | // Backend list |
| 232 | struct |
| 233 | { |
| 234 | int m_Major = 0; |
| 235 | int m_Minor = 0; |
| 236 | int m_Patch = 0; |
| 237 | const char *m_pBackendName = "" ; |
| 238 | bool m_Found = false; |
| 239 | }; |
| 240 | std::array<std::array<SMenuBackendInfo, EGraphicsDriverAgeType::GRAPHICS_DRIVER_AGE_TYPE_COUNT>, EBackendType::BACKEND_TYPE_COUNT> aaSupportedBackends{}; |
| 241 | uint32_t FoundBackendCount = 0; |
| 242 | for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i) |
| 243 | { |
| 244 | if(EBackendType(i) == BACKEND_TYPE_AUTO) |
| 245 | continue; |
| 246 | for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n) |
| 247 | { |
| 248 | auto &Info = aaSupportedBackends[i][n]; |
| 249 | if(Graphics()->GetDriverVersion(DriverAgeType: EGraphicsDriverAgeType(n), Major&: Info.m_Major, Minor&: Info.m_Minor, Patch&: Info.m_Patch, pName&: Info.m_pBackendName, BackendType: EBackendType(i))) |
| 250 | { |
| 251 | // don't count blocked opengl drivers |
| 252 | if(EBackendType(i) != BACKEND_TYPE_OPENGL || EGraphicsDriverAgeType(n) == GRAPHICS_DRIVER_AGE_TYPE_LEGACY || g_Config.m_GfxDriverIsBlocked == 0) |
| 253 | { |
| 254 | Info.m_Found = true; |
| 255 | ++FoundBackendCount; |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | if(FoundBackendCount > 1) |
| 262 | { |
| 263 | CUIRect Text, BackendDropDown; |
| 264 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 265 | MainView.HSplitTop(Cut: 20.0f, pTop: &Text, pBottom: &MainView); |
| 266 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 267 | MainView.HSplitTop(Cut: 20.0f, pTop: &BackendDropDown, pBottom: &MainView); |
| 268 | Ui()->DoLabel(pRect: &Text, pText: Localize(pStr: "Renderer" ), Size: 16.0f, Align: TEXTALIGN_MC); |
| 269 | |
| 270 | static std::vector<std::string> s_vBackendIdNames; |
| 271 | static std::vector<const char *> s_vpBackendIdNamesCStr; |
| 272 | static std::vector<SMenuBackendInfo> s_vBackendInfos; |
| 273 | |
| 274 | size_t BackendCount = FoundBackendCount + 1; |
| 275 | s_vBackendIdNames.resize(sz: BackendCount); |
| 276 | s_vpBackendIdNamesCStr.resize(sz: BackendCount); |
| 277 | s_vBackendInfos.resize(sz: BackendCount); |
| 278 | |
| 279 | char aTmpBackendName[256]; |
| 280 | |
| 281 | auto IsInfoDefault = [](const SMenuBackendInfo &CheckInfo) { |
| 282 | return str_comp_nocase(a: CheckInfo.m_pBackendName, b: DefaultConfig::GfxBackend) == 0 && CheckInfo.m_Major == DefaultConfig::GfxGLMajor && CheckInfo.m_Minor == DefaultConfig::GfxGLMinor && CheckInfo.m_Patch == DefaultConfig::GfxGLPatch; |
| 283 | }; |
| 284 | |
| 285 | int OldSelectedBackend = -1; |
| 286 | uint32_t CurCounter = 0; |
| 287 | for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i) |
| 288 | { |
| 289 | for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n) |
| 290 | { |
| 291 | auto &Info = aaSupportedBackends[i][n]; |
| 292 | if(Info.m_Found) |
| 293 | { |
| 294 | bool IsDefault = IsInfoDefault(Info); |
| 295 | str_format(buffer: aTmpBackendName, buffer_size: sizeof(aTmpBackendName), format: "%s (%d.%d.%d)%s%s" , Info.m_pBackendName, Info.m_Major, Info.m_Minor, Info.m_Patch, IsDefault ? " - " : "" , IsDefault ? Localize(pStr: "default" ) : "" ); |
| 296 | s_vBackendIdNames[CurCounter] = aTmpBackendName; |
| 297 | s_vpBackendIdNamesCStr[CurCounter] = s_vBackendIdNames[CurCounter].c_str(); |
| 298 | if(str_comp_nocase(a: Info.m_pBackendName, b: g_Config.m_GfxBackend) == 0 && g_Config.m_GfxGLMajor == Info.m_Major && g_Config.m_GfxGLMinor == Info.m_Minor && g_Config.m_GfxGLPatch == Info.m_Patch) |
| 299 | { |
| 300 | OldSelectedBackend = CurCounter; |
| 301 | } |
| 302 | |
| 303 | s_vBackendInfos[CurCounter] = Info; |
| 304 | ++CurCounter; |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | if(OldSelectedBackend != -1) |
| 310 | { |
| 311 | // no custom selected |
| 312 | BackendCount -= 1; |
| 313 | } |
| 314 | else |
| 315 | { |
| 316 | // custom selected one |
| 317 | str_format(buffer: aTmpBackendName, buffer_size: sizeof(aTmpBackendName), format: "%s (%s %d.%d.%d)" , Localize(pStr: "custom" ), g_Config.m_GfxBackend, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch); |
| 318 | s_vBackendIdNames[CurCounter] = aTmpBackendName; |
| 319 | s_vpBackendIdNamesCStr[CurCounter] = s_vBackendIdNames[CurCounter].c_str(); |
| 320 | OldSelectedBackend = CurCounter; |
| 321 | |
| 322 | s_vBackendInfos[CurCounter].m_pBackendName = "custom" ; |
| 323 | s_vBackendInfos[CurCounter].m_Major = g_Config.m_GfxGLMajor; |
| 324 | s_vBackendInfos[CurCounter].m_Minor = g_Config.m_GfxGLMinor; |
| 325 | s_vBackendInfos[CurCounter].m_Patch = g_Config.m_GfxGLPatch; |
| 326 | } |
| 327 | |
| 328 | static int s_OldSelectedBackend = -1; |
| 329 | if(s_OldSelectedBackend == -1) |
| 330 | s_OldSelectedBackend = OldSelectedBackend; |
| 331 | |
| 332 | static CUi::SDropDownState s_BackendDropDownState; |
| 333 | static CScrollRegion s_BackendDropDownScrollRegion; |
| 334 | s_BackendDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_BackendDropDownScrollRegion; |
| 335 | const int NewBackend = Ui()->DoDropDown(pRect: &BackendDropDown, CurSelection: OldSelectedBackend, pStrs: s_vpBackendIdNamesCStr.data(), Num: BackendCount, State&: s_BackendDropDownState); |
| 336 | if(OldSelectedBackend != NewBackend) |
| 337 | { |
| 338 | str_copy(dst&: g_Config.m_GfxBackend, src: s_vBackendInfos[NewBackend].m_pBackendName); |
| 339 | g_Config.m_GfxGLMajor = s_vBackendInfos[NewBackend].m_Major; |
| 340 | g_Config.m_GfxGLMinor = s_vBackendInfos[NewBackend].m_Minor; |
| 341 | g_Config.m_GfxGLPatch = s_vBackendInfos[NewBackend].m_Patch; |
| 342 | |
| 343 | CheckSettings = true; |
| 344 | s_GfxBackendChanged = s_OldSelectedBackend != NewBackend; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | // GPU list |
| 349 | const auto &GpuList = Graphics()->GetGpus(); |
| 350 | if(GpuList.m_vGpus.size() > 1) |
| 351 | { |
| 352 | CUIRect Text, GpuDropDown; |
| 353 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 354 | MainView.HSplitTop(Cut: 20.0f, pTop: &Text, pBottom: &MainView); |
| 355 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 356 | MainView.HSplitTop(Cut: 20.0f, pTop: &GpuDropDown, pBottom: &MainView); |
| 357 | Ui()->DoLabel(pRect: &Text, pText: Localize(pStr: "Graphics card" ), Size: 16.0f, Align: TEXTALIGN_MC); |
| 358 | |
| 359 | static std::vector<const char *> s_vpGpuIdNames; |
| 360 | |
| 361 | size_t GpuCount = GpuList.m_vGpus.size() + 1; |
| 362 | s_vpGpuIdNames.resize(sz: GpuCount); |
| 363 | |
| 364 | char aCurDeviceName[256 + 4]; |
| 365 | |
| 366 | int OldSelectedGpu = -1; |
| 367 | for(size_t i = 0; i < GpuCount; ++i) |
| 368 | { |
| 369 | if(i == 0) |
| 370 | { |
| 371 | str_format(buffer: aCurDeviceName, buffer_size: sizeof(aCurDeviceName), format: "%s (%s)" , Localize(pStr: "auto" ), GpuList.m_AutoGpu.m_aName); |
| 372 | s_vpGpuIdNames[i] = aCurDeviceName; |
| 373 | if(str_comp(a: "auto" , b: g_Config.m_GfxGpuName) == 0) |
| 374 | { |
| 375 | OldSelectedGpu = 0; |
| 376 | } |
| 377 | } |
| 378 | else |
| 379 | { |
| 380 | s_vpGpuIdNames[i] = GpuList.m_vGpus[i - 1].m_aName; |
| 381 | if(str_comp(a: GpuList.m_vGpus[i - 1].m_aName, b: g_Config.m_GfxGpuName) == 0) |
| 382 | { |
| 383 | OldSelectedGpu = i; |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | static int s_OldSelectedGpu = -1; |
| 389 | if(s_OldSelectedGpu == -1) |
| 390 | s_OldSelectedGpu = OldSelectedGpu; |
| 391 | |
| 392 | static CUi::SDropDownState s_GpuDropDownState; |
| 393 | static CScrollRegion s_GpuDropDownScrollRegion; |
| 394 | s_GpuDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_GpuDropDownScrollRegion; |
| 395 | const int NewGpu = Ui()->DoDropDown(pRect: &GpuDropDown, CurSelection: OldSelectedGpu, pStrs: s_vpGpuIdNames.data(), Num: GpuCount, State&: s_GpuDropDownState); |
| 396 | if(OldSelectedGpu != NewGpu) |
| 397 | { |
| 398 | if(NewGpu == 0) |
| 399 | str_copy(dst&: g_Config.m_GfxGpuName, src: "auto" ); |
| 400 | else |
| 401 | str_copy(dst&: g_Config.m_GfxGpuName, src: GpuList.m_vGpus[NewGpu - 1].m_aName); |
| 402 | CheckSettings = true; |
| 403 | s_GfxGpuChanged = NewGpu != s_OldSelectedGpu; |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | // check if the new settings require a restart |
| 408 | if(CheckSettings) |
| 409 | { |
| 410 | m_NeedRestartGraphics = !(s_GfxFsaaSamples == g_Config.m_GfxFsaaSamples && |
| 411 | !s_GfxBackendChanged && |
| 412 | !s_GfxGpuChanged); |
| 413 | } |
| 414 | } |
| 415 | |