Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/GameSettingsScreen.cpp
5654 views
1
2
// Copyright (c) 2013- PPSSPP Project.
3
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, version 2.0 or later versions.
7
8
// This program is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
// GNU General Public License 2.0 for more details.
12
13
// A copy of the GPL 2.0 should have been included with the program.
14
// If not, see http://www.gnu.org/licenses/
15
16
// Official git repository and contact information can be found at
17
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18
19
#include "ppsspp_config.h"
20
21
#include <algorithm>
22
#include <set>
23
24
#include "Common/Net/Resolve.h"
25
#include "Common/Audio/AudioBackend.h"
26
#include "Common/GPU/OpenGL/GLFeatures.h"
27
#include "Common/Render/DrawBuffer.h"
28
#include "Common/UI/Root.h"
29
#include "Common/UI/View.h"
30
#include "Common/UI/ViewGroup.h"
31
#include "Common/UI/Context.h"
32
#include "Common/UI/Notice.h"
33
#include "Common/Render/ManagedTexture.h"
34
#include "Common/VR/PPSSPPVR.h"
35
#include "Common/BitSet.h"
36
#include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres
37
#include "Common/System/Request.h"
38
#include "Common/System/OSD.h"
39
#include "Common/System/NativeApp.h"
40
#include "Common/Data/Color/RGBAUtil.h"
41
#include "Common/Data/Text/I18n.h"
42
#include "Common/Data/Encoding/Utf8.h"
43
#include "Common/UI/PopupScreens.h"
44
#include "UI/EmuScreen.h"
45
#include "UI/GameSettingsScreen.h"
46
#include "UI/GameInfoCache.h"
47
#include "UI/GamepadEmu.h"
48
#include "UI/ControlMappingScreen.h"
49
#include "UI/DevScreens.h"
50
#include "UI/DeveloperToolsScreen.h"
51
#include "UI/DisplayLayoutScreen.h"
52
#include "UI/RemoteISOScreen.h"
53
#include "UI/SavedataScreen.h"
54
#include "UI/SystemInfoScreen.h"
55
#include "UI/TouchControlLayoutScreen.h"
56
#include "UI/TouchControlVisibilityScreen.h"
57
#include "UI/TiltAnalogSettingsScreen.h"
58
#include "UI/MemStickScreen.h"
59
#include "UI/Theme.h"
60
#include "UI/RetroAchievementScreens.h"
61
#include "UI/OnScreenDisplay.h"
62
#include "UI/DiscordIntegration.h"
63
#include "UI/Background.h"
64
#include "UI/BackgroundAudio.h"
65
#include "UI/MiscViews.h"
66
67
#include "Common/File/FileUtil.h"
68
#include "Common/File/AndroidContentURI.h"
69
#include "Common/StringUtils.h"
70
#include "Core/Config.h"
71
#include "Core/ConfigValues.h"
72
#include "Core/KeyMap.h"
73
#include "Core/TiltEventProcessor.h"
74
#include "Core/Instance.h"
75
#include "Core/System.h"
76
#include "Core/Reporting.h"
77
#include "Core/HLE/sceUsbCam.h"
78
#include "Core/HLE/sceUsbMic.h"
79
#include "Core/HLE/sceUtility.h"
80
#include "GPU/Common/PostShader.h"
81
82
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
83
#include "UI/DarwinFileSystemServices.h"
84
#endif
85
86
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
87
#pragma warning(disable:4091) // workaround bug in VS2015 headers
88
#include "Windows/MainWindow.h"
89
#include <shlobj.h>
90
#include "Windows/W32Util/ShellUtil.h"
91
#endif
92
93
#if PPSSPP_PLATFORM(ANDROID)
94
95
#include "android/jni/AndroidAudio.h"
96
#include "Common/File/AndroidStorage.h"
97
98
extern AndroidAudioState *g_audioState;
99
100
#endif
101
102
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
103
void SetMemStickDirDarwin(int requesterToken) {
104
auto initialPath = g_Config.memStickDirectory;
105
INFO_LOG(Log::System, "Current path: %s", initialPath.c_str());
106
System_BrowseForFolder(requesterToken, "", initialPath, [](const std::string &value, int) {
107
INFO_LOG(Log::System, "Selected path: %s", value.c_str());
108
DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(Path(value));
109
});
110
}
111
#endif
112
113
class SettingHint : public UI::TextView {
114
public:
115
SettingHint(std::string_view text)
116
: UI::TextView(text, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT)) {
117
SetTextSize(UI::TextSize::Tiny);
118
SetPadding(UI::Margins(14, 0, 0, 8));
119
SetAlign(FLAG_WRAP_TEXT);
120
}
121
122
void Draw(UIContext &dc) override {
123
UI::Style style = dc.GetTheme().itemStyle;
124
dc.FillRect(style.background, bounds_);
125
UI::TextView::Draw(dc);
126
}
127
};
128
129
GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)
130
: UITabbedBaseDialogScreen(gamePath, TabDialogFlags::HorizontalOnlyIcons | TabDialogFlags::VerticalShowIcons), gameID_(gameID), editGameSpecificThenRestore_(editThenRestore) {
131
prevInflightFrames_ = g_Config.iInflightFrames;
132
analogSpeedMapped_ = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);
133
134
if (editGameSpecificThenRestore_) {
135
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
136
g_Config.LoadGameConfig(gameID_);
137
}
138
139
iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60;
140
iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60;
141
iAlternateSpeedPercentAnalog_ = (g_Config.iAnalogFpsLimit * 100) / 60;
142
}
143
144
GameSettingsScreen::~GameSettingsScreen() {
145
Reporting::Enable(enableReports_, "report.ppsspp.org");
146
Reporting::UpdateConfig();
147
if (!g_Config.Save("GameSettingsScreen::onFinish")) {
148
System_Toast("Failed to save settings!\nCheck permissions, or try to restart the device.");
149
}
150
151
if (editGameSpecificThenRestore_) {
152
// We already saved above. Just unload the game config.
153
// Note that we are leaving the screen here in practice, we don't need to reset the bool.
154
g_Config.UnloadGameConfig();
155
}
156
157
System_Notify(SystemNotification::UI);
158
159
KeyMap::UpdateNativeMenuKeys();
160
161
// Wipe some caches after potentially changing settings.
162
// Let's not send resize messages here, handled elsewhere.
163
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
164
}
165
166
void GameSettingsScreen::PreCreateViews() {
167
ReloadAllPostShaderInfo(screenManager()->getDrawContext());
168
ReloadAllThemeInfo();
169
}
170
171
// This needs before run CheckGPUFeatures()
172
// TODO: Remove this if fix the issue
173
static bool CheckSupportShaderTessellationGLES() {
174
#if PPSSPP_PLATFORM(UWP)
175
return true;
176
#else
177
// TODO: Make work with non-GL backends
178
int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;
179
bool vertexTexture = maxVertexTextureImageUnits >= 3; // At least 3 for hardware tessellation
180
181
bool textureFloat = gl_extensions.ARB_texture_float || gl_extensions.OES_texture_float;
182
bool hasTexelFetch = gl_extensions.GLES3 || (!gl_extensions.IsGLES && gl_extensions.VersionGEThan(3, 3, 0)) || gl_extensions.EXT_gpu_shader4;
183
184
return vertexTexture && textureFloat && hasTexelFetch;
185
#endif
186
}
187
188
static bool DoesBackendSupportHWTess() {
189
switch (GetGPUBackend()) {
190
case GPUBackend::OPENGL:
191
return CheckSupportShaderTessellationGLES();
192
case GPUBackend::VULKAN:
193
case GPUBackend::DIRECT3D11:
194
return true;
195
default:
196
return false;
197
}
198
}
199
200
static bool UsingHardwareTextureScaling() {
201
// For now, Vulkan only.
202
return g_Config.bTexHardwareScaling && GetGPUBackend() == GPUBackend::VULKAN && !g_Config.bSoftwareRendering;
203
}
204
205
static std::string TextureTranslateName(std::string_view value) {
206
const TextureShaderInfo *info = GetTextureShaderInfo(value);
207
if (info) {
208
auto ts = GetI18NCategory(I18NCat::TEXTURESHADERS);
209
return std::string(ts->T(value, info->name.c_str()));
210
} else {
211
return std::string(value);
212
}
213
}
214
215
static std::string *GPUDeviceNameSetting() {
216
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
217
return &g_Config.sVulkanDevice;
218
}
219
#ifdef _WIN32
220
if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D11) {
221
return &g_Config.sD3D11Device;
222
}
223
#endif
224
return nullptr;
225
}
226
227
static bool PathToVisualUsbPath(Path path, std::string &outPath) {
228
switch (path.Type()) {
229
case PathType::NATIVE:
230
if (path.StartsWith(g_Config.memStickDirectory)) {
231
return g_Config.memStickDirectory.ComputePathTo(path, outPath);
232
}
233
break;
234
case PathType::CONTENT_URI:
235
#if PPSSPP_PLATFORM(ANDROID)
236
{
237
// Try to parse something sensible out of the content URI.
238
AndroidContentURI uri(path.ToString());
239
outPath = uri.RootPath();
240
if (startsWith(outPath, "primary:")) {
241
outPath = "/" + outPath.substr(8);
242
}
243
return true;
244
}
245
#endif
246
default:
247
break;
248
}
249
return false;
250
}
251
252
void GameSettingsScreen::CreateTabs() {
253
using namespace UI;
254
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
255
256
AddTab("GameSettingsGraphics", ms->T("Graphics"), ImageID("I_DISPLAY"), [this](UI::LinearLayout *parent) {
257
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
258
parent->Add(new PaneTitleBar(gamePath_, ms->T("Graphics"), "graphics", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
259
CreateGraphicsSettings(parent);
260
});
261
262
AddTab("GameSettingsControls", ms->T("Controls"), ImageID("I_CONTROLLER"), [this](UI::LinearLayout *parent) {
263
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
264
parent->Add(new PaneTitleBar(gamePath_, ms->T("Controls"), "controls"));
265
CreateControlsSettings(parent);
266
});
267
268
AddTab("GameSettingsAudio", ms->T("Audio"), ImageID("I_SPEAKER_MAX"), [this](UI::LinearLayout *parent) {
269
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
270
parent->Add(new PaneTitleBar(gamePath_, ms->T("Audio"), "audio"));
271
CreateAudioSettings(parent);
272
});
273
274
AddTab("GameSettingsNetworking", ms->T("Networking"), ImageID("I_WIFI"), [this](UI::LinearLayout *parent) {
275
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
276
parent->Add(new PaneTitleBar(gamePath_, ms->T("Networking"), "network"));
277
CreateNetworkingSettings(parent);
278
});
279
280
AddTab("GameSettingsTools", ms->T("Tools"), ImageID("I_TOOLS"), [this](UI::LinearLayout *parent) {
281
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
282
parent->Add(new PaneTitleBar(gamePath_, ms->T("Tools"), "tools"));
283
CreateToolsSettings(parent);
284
});
285
286
AddTab("GameSettingsSystem", ms->T("System"), ImageID("I_PSP"), [this](UI::LinearLayout *parent) {
287
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
288
parent->Add(new PaneTitleBar(gamePath_, ms->T("System"), "system"));
289
CreateSystemSettings(parent);
290
});
291
292
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
293
if ((deviceType == DEVICE_TYPE_VR) || g_Config.bForceVR) {
294
AddTab("GameSettingsVR", ms->T("VR"), ImageID::invalid(), [this](UI::LinearLayout *parent) {
295
CreateVRSettings(parent);
296
});
297
}
298
}
299
300
// Graphics
301
void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) {
302
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
303
auto vr = GetI18NCategory(I18NCat::VR);
304
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
305
306
using namespace UI;
307
308
graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode")));
309
310
Draw::DrawContext *draw = screenManager()->getDrawContext();
311
312
#if !PPSSPP_PLATFORM(UWP)
313
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
314
PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));
315
renderingBackendChoice->SetPreOpenCallback([this](UI::PopupMultiChoice *choice) {
316
// Don't filter until the last possible moment, since it involves trying to initialize Vulkan, if we were
317
// started in OpenGL mode on Android.
318
choice->HideChoice(1);
319
choice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend);
320
321
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
322
choice->HideChoice((int)GPUBackend::OPENGL);
323
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
324
choice->HideChoice((int)GPUBackend::DIRECT3D11);
325
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
326
choice->HideChoice((int)GPUBackend::VULKAN);
327
});
328
329
if (!IsFirstInstance()) {
330
// If we're not the first instance, can't save the setting, and it requires a restart, so...
331
renderingBackendChoice->SetEnabled(false);
332
}
333
#endif
334
335
// Backends that don't allow a device choice will only expose one device.
336
if (draw->GetDeviceList().size() > 1) {
337
std::string *deviceNameSetting = GPUDeviceNameSetting();
338
if (deviceNameSetting) {
339
PopupMultiChoiceDynamic *deviceChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(deviceNameSetting, gr->T("Device"), draw->GetDeviceList(), I18NCat::NONE, screenManager()));
340
deviceChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingDevice);
341
}
342
}
343
344
static const char *internalResolutions[] = { "Auto (1:1)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP", "6x PSP", "7x PSP", "8x PSP", "9x PSP", "10x PSP" };
345
PopupMultiChoice *resolutionChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInternalResolution, gr->T("Rendering Resolution"), internalResolutions, 0, ARRAY_SIZE(internalResolutions), I18NCat::GRAPHICS, screenManager()));
346
resolutionChoice->OnChoice.Add([](UI::EventParams &e) {
347
if (g_Config.iAndroidHwScale == 1) {
348
System_RecreateActivity();
349
}
350
Reporting::UpdateConfig();
351
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
352
});
353
resolutionChoice->SetEnabledFunc([] {
354
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
355
});
356
357
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
358
359
if (deviceType != DEVICE_TYPE_VR) {
360
CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)")));
361
softwareGPU->SetEnabled(!PSP_IsInited());
362
}
363
364
if (draw->GetDeviceCaps().multiSampleLevelsMask != 1) {
365
static const char *msaaModes[] = { "Off", "2x", "4x", "8x", "16x" };
366
auto msaaChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iMultiSampleLevel, gr->T("Antialiasing (MSAA)"), msaaModes, 0, ARRAY_SIZE(msaaModes), I18NCat::GRAPHICS, screenManager()));
367
msaaChoice->OnChoice.Add([&](UI::EventParams &) -> void {
368
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
369
});
370
msaaChoice->SetEnabledFunc([] {
371
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
372
});
373
if (g_Config.iMultiSampleLevel > 1 && draw->GetDeviceCaps().isTilingGPU) {
374
msaaChoice->SetIconRight(ImageID("I_WARNING"), 0.7f);
375
}
376
msaaChoice->SetEnabledFunc([] {
377
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
378
});
379
380
// Hide unsupported levels.
381
for (int i = 1; i < 5; i++) {
382
if ((draw->GetDeviceCaps().multiSampleLevelsMask & (1 << i)) == 0) {
383
msaaChoice->HideChoice(i);
384
} else if (i > 0 && draw->GetDeviceCaps().isTilingGPU) {
385
msaaChoice->SetChoiceIcon(i, ImageID("I_WARNING"));
386
}
387
}
388
} else {
389
g_Config.iMultiSampleLevel = 0;
390
}
391
392
#if PPSSPP_PLATFORM(ANDROID)
393
if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {
394
static const char *deviceResolutions[] = { "Native device resolution", "Same as Rendering resolution", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" };
395
int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2;
396
if (max_res_temp == 3)
397
max_res_temp = 4; // At least allow 2x
398
int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));
399
UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager()));
400
hwscale->OnChoice.Add([](UI::EventParams &) {
401
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
402
System_PostUIMessage(UIMessage::GPU_DISPLAY_RESIZED);
403
System_RecreateActivity();
404
});
405
}
406
#endif
407
408
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
409
410
graphicsSettings->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));
411
412
graphicsSettings->Add(new ItemHeader(gr->T("Display")));
413
414
if (deviceType != DEVICE_TYPE_VR) {
415
#if !defined(MOBILE_DEVICE)
416
CheckBox *fullscreenCheckbox = graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")));
417
fullscreenCheckbox->OnClick.Add([](UI::EventParams &e) {
418
System_ApplyFullscreenState();
419
});
420
if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) {
421
CheckBox *fullscreenMulti = graphicsSettings->Add(new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays")));
422
fullscreenMulti->SetEnabledFunc([] {
423
return g_Config.bFullScreen;
424
});
425
fullscreenMulti->OnClick.Add([](UI::EventParams &e) {
426
System_ApplyFullscreenState();
427
});
428
}
429
#endif
430
431
#if PPSSPP_PLATFORM(ANDROID)
432
// Hide Immersive Mode on pre-kitkat Android
433
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) {
434
// Let's reuse the Fullscreen translation string from desktop.
435
graphicsSettings->Add(new CheckBox(&config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange);
436
}
437
#endif
438
// Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc.
439
Choice *displayEditor = graphicsSettings->Add(new Choice(gr->T("Display layout & effects")));
440
displayEditor->OnClick.Add([&](UI::EventParams &) -> void {
441
screenManager()->push(new DisplayLayoutScreen(gamePath_));
442
});
443
}
444
445
// If only one mode is supported (like FIFO on iOS), no need to show the options.
446
if (CountSetBits((u32)draw->GetDeviceCaps().presentModesSupported) > 1) {
447
// Immediate means non-synchronized, tearing.
448
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) {
449
CheckBox *vSync = graphicsSettings->Add(new CheckBox(&g_Config.bVSync, gr->T("VSync")));
450
vSync->OnClick.Add([=](EventParams &e) {
451
NativeResized(); // TODO: Remove
452
});
453
}
454
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) {
455
CheckBox *lowLatency = graphicsSettings->Add(new CheckBox(&g_Config.bLowLatencyPresent, gr->T("Low latency display")));
456
lowLatency->OnClick.Add([=](EventParams &e) {
457
NativeResized(); // TODO: Remove
458
});
459
460
// If the immediate mode is supported, we can tie low latency present to VSync.
461
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) {
462
lowLatency->SetEnabledPtr(&g_Config.bVSync);
463
}
464
}
465
}
466
467
graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control")));
468
static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"};
469
PopupMultiChoice *frameSkipping = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), I18NCat::GRAPHICS, screenManager()));
470
frameSkipping->SetEnabledFunc([] {
471
return !g_Config.bAutoFrameSkip;
472
});
473
474
CheckBox *frameSkipAuto = graphicsSettings->Add(new CheckBox(&g_Config.bAutoFrameSkip, gr->T("Auto FrameSkip")));
475
frameSkipAuto->OnClick.Add([](UI::EventParams &e) {
476
g_Config.UpdateAfterSettingAutoFrameSkip();
477
});
478
479
PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent1_, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited")));
480
altSpeed1->SetFormat("%i%%");
481
altSpeed1->SetZeroLabel(gr->T("Unlimited"));
482
altSpeed1->SetNegativeDisable(gr->T("Disabled"));
483
484
PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited")));
485
altSpeed2->SetFormat("%i%%");
486
altSpeed2->SetZeroLabel(gr->T("Unlimited"));
487
altSpeed2->SetNegativeDisable(gr->T("Disabled"));
488
489
if (analogSpeedMapped_) {
490
PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercentAnalog_, 1, 1000, UI::NO_DEFAULT_INT, gr->T("Analog alternative speed", "Analog alternative speed (in %)"), 5, screenManager(), "%"));
491
altSpeed2->SetFormat("%i%%");
492
}
493
494
graphicsSettings->Add(new ItemHeader(gr->T("Speed Hacks", "Speed Hacks (can cause rendering errors!)")));
495
496
CheckBox *skipBufferEffects = graphicsSettings->Add(new CheckBox(&g_Config.bSkipBufferEffects, gr->T("Skip Buffer Effects")));
497
graphicsSettings->Add(new SettingHint(gr->T("RenderingMode NonBuffered Tip", "Faster, but graphics may be missing in some games")));
498
skipBufferEffects->OnClick.Add([=](EventParams &e) {
499
if (g_Config.bSkipBufferEffects) {
500
g_Config.bAutoFrameSkip = false;
501
}
502
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
503
});
504
skipBufferEffects->SetDisabledPtr(&g_Config.bSoftwareRendering);
505
506
CheckBox *disableCulling = graphicsSettings->Add(new CheckBox(&g_Config.bDisableRangeCulling, gr->T("Disable culling")));
507
disableCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);
508
509
static const char *skipGpuReadbackModes[] = { "No (default)", "Skip", "Copy to texture" };
510
511
PopupMultiChoice *skipGPUReadbacks = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSkipGPUReadbackMode, gr->T("Skip GPU Readbacks"), skipGpuReadbackModes, 0, ARRAY_SIZE(skipGpuReadbackModes), I18NCat::GRAPHICS, screenManager()));
512
skipGPUReadbacks->SetDisabledPtr(&g_Config.bSoftwareRendering);
513
514
static const char *depthRasterModes[] = { "Auto (default)", "Low", "Off", "Always on" };
515
516
PopupMultiChoice *depthRasterMode = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iDepthRasterMode, gr->T("Lens flare occlusion"), depthRasterModes, 0, ARRAY_SIZE(depthRasterModes), I18NCat::GRAPHICS, screenManager()));
517
depthRasterMode->SetDisabledPtr(&g_Config.bSoftwareRendering);
518
depthRasterMode->SetChoiceIcon(3, ImageID("I_WARNING")); // It's a performance trap.
519
if (g_Config.iDepthRasterMode != 3)
520
depthRasterMode->HideChoice(3);
521
522
CheckBox *texBackoff = graphicsSettings->Add(new CheckBox(&g_Config.bTextureBackoffCache, gr->T("Lazy texture caching", "Lazy texture caching (speedup)")));
523
texBackoff->SetDisabledPtr(&g_Config.bSoftwareRendering);
524
graphicsSettings->Add(new SettingHint(gr->T("Lazy texture caching Tip", "Faster, but can cause text problems in a few games")));
525
526
static const char *quality[] = { "Low", "Medium", "High" };
527
graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSplineBezierQuality, gr->T("LowCurves", "Spline/Bezier curves quality"), quality, 0, ARRAY_SIZE(quality), I18NCat::GRAPHICS, screenManager()));
528
graphicsSettings->Add(new SettingHint(gr->T("LowCurves Tip", "Only used by some games, controls smoothness of curves")));
529
530
graphicsSettings->Add(new ItemHeader(gr->T("Performance")));
531
CheckBox *frameDuplication = graphicsSettings->Add(new CheckBox(&g_Config.bRenderDuplicateFrames, gr->T("Render duplicate frames to 60hz")));
532
frameDuplication->SetEnabledFunc([] {
533
return !g_Config.bSkipBufferEffects && g_Config.iFrameSkip == 0;
534
});
535
graphicsSettings->Add(new SettingHint(gr->T("RenderDuplicateFrames Tip", "Can make framerate smoother in games that run at lower framerates")));
536
537
if (draw->GetDeviceCaps().setMaxFrameLatencySupported) {
538
static const char *bufferOptions[] = { "No buffer", "Up to 1", "Up to 2" };
539
PopupMultiChoice *inflightChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInflightFrames, gr->T("Buffer graphics commands (faster, input lag)"), bufferOptions, 1, ARRAY_SIZE(bufferOptions), I18NCat::GRAPHICS, screenManager()));
540
inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice);
541
}
542
543
if (GetGPUBackend() == GPUBackend::VULKAN) {
544
const bool usable = draw->GetDeviceCaps().geometryShaderSupported && !draw->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);
545
const bool vertexSupported = draw->GetDeviceCaps().clipDistanceSupported && draw->GetDeviceCaps().cullDistanceSupported;
546
if (usable && !vertexSupported) {
547
CheckBox *geometryCulling = graphicsSettings->Add(new CheckBox(&g_Config.bUseGeometryShader, gr->T("Geometry shader culling")));
548
geometryCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);
549
}
550
}
551
552
if (deviceType != DEVICE_TYPE_VR) {
553
CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform")));
554
hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering);
555
}
556
557
CheckBox *swSkin = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareSkinning, gr->T("Software Skinning")));
558
swSkin->SetDisabledPtr(&g_Config.bSoftwareRendering);
559
graphicsSettings->Add(new SettingHint(gr->T("SoftwareSkinning Tip", "Combine skinned model draws on the CPU, faster in most games")));
560
561
if (DoesBackendSupportHWTess()) {
562
CheckBox *tessellationHW = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTessellation, gr->T("Hardware Tessellation")));
563
tessellationHW->SetEnabledFunc([]() {
564
return !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
565
});
566
graphicsSettings->Add(new SettingHint(gr->T("HardwareTessellation Tip", "Uses hardware to make curves")));
567
}
568
569
graphicsSettings->Add(new ItemHeader(gr->T("Texture upscaling")));
570
571
if (GetGPUBackend() == GPUBackend::VULKAN) {
572
ChoiceWithValueDisplay *textureShaderChoice = graphicsSettings->Add(new ChoiceWithValueDisplay(&g_Config.sTextureShaderName, gr->T("GPU texture upscaler (fast)"), &TextureTranslateName));
573
textureShaderChoice->OnClick.Add([this](UI::EventParams &e) {
574
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
575
auto shaderScreen = new TextureShaderScreen(gr->T("GPU texture upscaler (fast)"));
576
shaderScreen->OnChoice.Add([this](UI::EventParams &e) {
577
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
578
RecreateViews(); // Update setting name
579
g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";
580
});
581
if (e.v)
582
shaderScreen->SetPopupOrigin(e.v);
583
screenManager()->push(shaderScreen);
584
});
585
textureShaderChoice->SetDisabledPtr(&g_Config.bSoftwareRendering);
586
}
587
588
#ifndef MOBILE_DEVICE
589
static const char *texScaleLevels[] = {"Off", "2x", "3x", "4x", "5x"};
590
#else
591
static const char *texScaleLevels[] = {"Off", "2x", "3x"};
592
#endif
593
594
static const char *texScaleAlgos[] = { "xBRZ", "Hybrid", "Bicubic", "Hybrid + Bicubic", };
595
PopupMultiChoice *texScalingType = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingType, gr->T("CPU texture upscaler (slow)"), texScaleAlgos, 0, ARRAY_SIZE(texScaleAlgos), I18NCat::GRAPHICS, screenManager()));
596
texScalingType->SetEnabledFunc([]() {
597
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
598
});
599
PopupMultiChoice *texScalingChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingLevel, gr->T("Upscale Level"), texScaleLevels, 1, ARRAY_SIZE(texScaleLevels), I18NCat::GRAPHICS, screenManager()));
600
// TODO: Better check? When it won't work, it scales down anyway.
601
if (!gl_extensions.OES_texture_npot && GetGPUBackend() == GPUBackend::OPENGL) {
602
texScalingChoice->HideChoice(3); // 3x
603
texScalingChoice->HideChoice(5); // 5x
604
}
605
graphicsSettings->Add(new SettingHint(gr->T("UpscaleLevel Tip", "CPU heavy - some scaling may be delayed to avoid stutter")));
606
607
texScalingChoice->SetEnabledFunc([]() {
608
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
609
});
610
611
CheckBox *deposterize = graphicsSettings->Add(new CheckBox(&g_Config.bTexDeposterize, gr->T("Deposterize")));
612
deposterize->SetEnabledFunc([]() {
613
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
614
});
615
graphicsSettings->Add(new SettingHint(gr->T("Deposterize Tip", "Fixes visual banding glitches in upscaled textures")));
616
617
graphicsSettings->Add(new ItemHeader(gr->T("Texture Filtering")));
618
static const char *anisoLevels[] = { "Off", "2x", "4x", "8x", "16x" };
619
PopupMultiChoice *anisoFiltering = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAnisotropyLevel, gr->T("Anisotropic Filtering"), anisoLevels, 0, ARRAY_SIZE(anisoLevels), I18NCat::GRAPHICS, screenManager()));
620
anisoFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);
621
622
static const char *texFilters[] = { "Auto", "Nearest", "Linear", "Auto Max Quality"};
623
PopupMultiChoice *filters = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexFiltering, gr->T("Texture Filter"), texFilters, 1, ARRAY_SIZE(texFilters), I18NCat::GRAPHICS, screenManager()));
624
filters->SetDisabledPtr(&g_Config.bSoftwareRendering);
625
626
CheckBox *smartFiltering = graphicsSettings->Add(new CheckBox(&g_Config.bSmart2DTexFiltering, gr->T("Smart 2D texture filtering")));
627
smartFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);
628
629
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
630
bool showCardboardSettings = deviceType != DEVICE_TYPE_VR;
631
#else
632
// If you enabled it through the ini, you can see this. Useful for testing.
633
bool showCardboardSettings = config.bEnableCardboardVR;
634
#endif
635
if (showCardboardSettings) {
636
graphicsSettings->Add(new ItemHeader(gr->T("Cardboard VR Settings", "Cardboard VR Settings")));
637
graphicsSettings->Add(new CheckBox(&config.bEnableCardboardVR, gr->T("Enable Cardboard VR", "Enable Cardboard VR")));
638
PopupSliderChoice *cardboardScreenSize = graphicsSettings->Add(new PopupSliderChoice(&config.iCardboardScreenSize, 30, 150, 50, gr->T("Cardboard Screen Size", "Screen Size (in % of the viewport)"), 1, screenManager(), gr->T("% of viewport")));
639
cardboardScreenSize->SetEnabledPtr(&config.bEnableCardboardVR);
640
PopupSliderChoice *cardboardXShift = graphicsSettings->Add(new PopupSliderChoice(&config.iCardboardXShift, -150, 150, 0, gr->T("Cardboard Screen X Shift", "X Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));
641
cardboardXShift->SetEnabledPtr(&config.bEnableCardboardVR);
642
PopupSliderChoice *cardboardYShift = graphicsSettings->Add(new PopupSliderChoice(&config.iCardboardYShift, -100, 100, 0, gr->T("Cardboard Screen Y Shift", "Y Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));
643
cardboardYShift->SetEnabledPtr(&config.bEnableCardboardVR);
644
}
645
646
std::vector<std::string> cameraList = Camera::getDeviceList();
647
if (cameraList.size() >= 1) {
648
graphicsSettings->Add(new ItemHeader(gr->T("Camera")));
649
PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, I18NCat::NONE, screenManager()));
650
cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange);
651
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
652
graphicsSettings->Add(new CheckBox(&g_Config.bCameraMirrorHorizontal, gr->T("Mirror camera image")));
653
#endif
654
}
655
656
graphicsSettings->Add(new ItemHeader(gr->T("Hack Settings", "Hack Settings (these WILL cause glitches)")));
657
658
static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" };
659
PopupMultiChoice *bloomHack = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBloomHack, gr->T("Lower resolution for effects (reduces artifacts)"), bloomHackOptions, 0, ARRAY_SIZE(bloomHackOptions), I18NCat::GRAPHICS, screenManager()));
660
bloomHack->SetEnabledFunc([] {
661
return !g_Config.bSoftwareRendering && g_Config.iInternalResolution != 1;
662
});
663
664
graphicsSettings->Add(new ItemHeader(gr->T("Overlay Information")));
665
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::FPS_COUNTER, gr->T("Show FPS Counter")));
666
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::SPEED_COUNTER, gr->T("Show Speed")));
667
if (System_GetPropertyBool(SYSPROP_CAN_READ_BATTERY_PERCENTAGE)) {
668
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::BATTERY_PERCENT, gr->T("Show Battery %")));
669
}
670
AddOverlayList(graphicsSettings, screenManager());
671
}
672
673
void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
674
using namespace UI;
675
676
auto a = GetI18NCategory(I18NCat::AUDIO);
677
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
678
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
679
auto di = GetI18NCategory(I18NCat::DIALOG);
680
681
#if PPSSPP_PLATFORM(IOS)
682
CheckBox *respectSilentMode = audioSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));
683
respectSilentMode->OnClick.Add([=](EventParams &e) {
684
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
685
});
686
respectSilentMode->SetEnabledPtr(&g_Config.bEnableSound);
687
CheckBox *mixWithOthers = audioSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));
688
mixWithOthers->OnClick.Add([=](EventParams &e) {
689
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
690
});
691
mixWithOthers->SetEnabledPtr(&g_Config.bEnableSound);
692
#endif
693
audioSettings->Add(new ItemHeader(a->T("Audio playback")));
694
695
static const char *syncModes[] = { "Smooth (reduces artifacts)", "Classic (lowest latency)" };
696
697
audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioPlaybackMode, a->T("Playback mode"), syncModes, 0, ARRAY_SIZE(syncModes), I18NCat::AUDIO, screenManager()));
698
audioSettings->Add(new CheckBox(&g_Config.bFillAudioGaps, a->T("Fill audio gaps")))->SetEnabledFunc([]() {
699
return g_Config.iAudioPlaybackMode == (int)AudioSyncMode::GRANULAR;
700
});
701
702
audioSettings->Add(new ItemHeader(a->T("Game volume")));
703
704
// This is here because it now only applies to in-game. Muting the menu sounds is separate.
705
CheckBox *enableSound = audioSettings->Add(new CheckBox(&g_Config.bEnableSound, a->T("Enable Sound")));
706
707
PopupSliderChoice *volume = audioSettings->Add(new PopupSliderChoice(&g_Config.iGameVolume, VOLUME_OFF, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iGameVolume), a->T("Game volume"), screenManager()));
708
volume->SetFormat("%d%%");
709
volume->SetEnabledPtr(&g_Config.bEnableSound);
710
volume->SetZeroLabel(a->T("Mute"));
711
712
PopupSliderChoice *reverbVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iReverbVolume, VOLUME_OFF, 2 * VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iReverbVolume), a->T("Reverb volume"), screenManager()));
713
reverbVolume->SetFormat("%d%%");
714
reverbVolume->SetEnabledPtr(&g_Config.bEnableSound);
715
reverbVolume->SetZeroLabel(a->T("Disabled"));
716
717
PopupSliderChoice *altVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAltSpeedVolume, VOLUME_OFF, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iAltSpeedVolume), a->T("Alternate speed volume"), screenManager()));
718
altVolume->SetFormat("%d%%");
719
altVolume->SetEnabledPtr(&g_Config.bEnableSound);
720
altVolume->SetZeroLabel(a->T("Mute"));
721
722
PopupSliderChoice *achievementVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAchievementVolume, VOLUME_OFF, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iAchievementVolume), ac->T("Achievement sound volume"), screenManager()));
723
achievementVolume->SetFormat("%d%%");
724
achievementVolume->SetEnabledPtr(&g_Config.bEnableSound);
725
achievementVolume->SetZeroLabel(a->T("Mute"));
726
achievementVolume->OnChange.Add([](UI::EventParams &e) {
727
// Audio preview
728
float achievementVolume = Volume100ToMultiplier(g_Config.iAchievementVolume);
729
g_BackgroundAudio.SFX().Play(UI::UISound::ACHIEVEMENT_UNLOCKED, achievementVolume);
730
});
731
732
audioSettings->Add(new ItemHeader(a->T("UI sound")));
733
734
audioSettings->Add(new CheckBox(&g_Config.bUISound, a->T("UI sound")));
735
PopupSliderChoice *uiVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iUIVolume, 0, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iUIVolume), a->T("UI volume"), screenManager()));
736
uiVolume->SetFormat("%d%%");
737
uiVolume->SetZeroLabel(a->T("Mute"));
738
uiVolume->SetLiveUpdate(true);
739
uiVolume->OnChange.Add([](UI::EventParams &e) {
740
static double lastTimePlayed = 0.0;
741
double now = time_now_d();
742
if (now - lastTimePlayed < 0.1) {
743
return; // Don't play if we just played one, to avoid spamming when dragging.
744
}
745
lastTimePlayed = now;
746
// Audio preview
747
PlayUISound(UI::UISound::CONFIRM);
748
});
749
uiVolume->SetEnabledPtr(&g_Config.bUISound);
750
751
PopupSliderChoice *gamePreviewVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iGamePreviewVolume, VOLUME_OFF, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iGamePreviewVolume), a->T("Game preview volume"), screenManager()));
752
gamePreviewVolume->SetFormat("%d%%");
753
gamePreviewVolume->SetZeroLabel(a->T("Mute"));
754
755
bool sdlAudio = false;
756
757
#if defined(SDL)
758
audioSettings->Add(new ItemHeader(a->T("Audio backend")));
759
std::vector<std::string> audioDeviceList;
760
SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList);
761
audioDeviceList.insert(audioDeviceList.begin(), a->T_cstr("Auto"));
762
PopupMultiChoiceDynamic *audioDevice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sAudioDevice, a->T("Device"), audioDeviceList, I18NCat::NONE, screenManager()));
763
audioDevice->OnChoice.Handle(this, &GameSettingsScreen::OnAudioDevice);
764
sdlAudio = true;
765
766
static const int bufferSizes[] = {128, 256, 512, 1024, 2048};
767
PopupSliderChoice *bufferSize = audioSettings->Add(new PopupSliderChoice(&g_Config.iSDLAudioBufferSize, 0, 2048, 256, a->T("Buffer size"), screenManager()));
768
bufferSize->RestrictChoices(bufferSizes, ARRAY_SIZE(bufferSizes));
769
audioSettings->Add(new SettingHint(di->T("This change will not take effect until PPSSPP is restarted.")));
770
#endif
771
772
#if PPSSPP_PLATFORM(WINDOWS)
773
extern AudioBackend *g_audioBackend;
774
775
std::vector<std::string> audioDeviceNames;
776
std::vector<std::string> audioDeviceIds;
777
778
std::vector<AudioDeviceDesc> deviceDescs;
779
g_audioBackend->EnumerateDevices(&deviceDescs);
780
if (!deviceDescs.empty()) {
781
audioSettings->Add(new ItemHeader(a->T("Audio backend")));
782
for (auto &desc : deviceDescs) {
783
audioDeviceNames.push_back(desc.name);
784
audioDeviceIds.push_back(desc.uniqueId);
785
}
786
787
audioDeviceNames.insert(audioDeviceNames.begin(), std::string(a->T("Auto")));
788
audioDeviceIds.insert(audioDeviceIds.begin(), "");
789
790
PopupMultiChoiceDynamic *audioDevice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sAudioDevice, a->T("Device"), audioDeviceNames, I18NCat::NONE, screenManager(), &audioDeviceIds));
791
audioDevice->OnChoice.Add([](UI::EventParams &) {
792
bool reverted;
793
if (g_audioBackend->InitOutputDevice(g_Config.sAudioDevice, LatencyMode::Aggressive, &reverted)) {
794
if (reverted) {
795
WARN_LOG(Log::Audio, "Unexpected: After a direct choice, audio device reverted to default. '%s'", g_Config.sAudioDevice.c_str());
796
}
797
} else {
798
WARN_LOG(Log::Audio, "InitOutputDevice failed");
799
}
800
});
801
CheckBox *autoAudio = audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));
802
autoAudio->SetEnabledFunc([]()->bool {
803
return g_Config.sAudioDevice.empty();
804
});
805
}
806
807
const bool isWindows = true;
808
#else
809
const bool isWindows = false;
810
audioSettings->Add(new ItemHeader(a->T("Audio backend")));
811
812
if (sdlAudio) {
813
audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));
814
}
815
816
#if PPSSPP_PLATFORM(ANDROID)
817
CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)")));
818
819
// Show OpenSL debug info
820
const std::string audioErrorStr = AndroidAudio_GetErrorString(g_audioState);
821
if (!audioErrorStr.empty()) {
822
audioSettings->Add(new InfoItem(a->T("Audio Error"), audioErrorStr));
823
}
824
#endif
825
#endif
826
827
std::vector<std::string> micList = Microphone::getDeviceList();
828
if (!micList.empty()) {
829
audioSettings->Add(new ItemHeader(a->T("Microphone")));
830
PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, a->T("Microphone Device"), micList, I18NCat::NONE, screenManager()));
831
MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange);
832
}
833
}
834
835
void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) {
836
using namespace UI;
837
838
auto co = GetI18NCategory(I18NCat::CONTROLS);
839
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
840
auto di = GetI18NCategory(I18NCat::DIALOG);
841
842
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
843
844
controlsSettings->Add(new ItemHeader(ms->T("Controls")));
845
controlsSettings->Add(new Choice(co->T("Control mapping")))->OnClick.Add([this](UI::EventParams &e) {
846
screenManager()->push(new ControlMappingScreen(gamePath_));
847
});
848
controlsSettings->Add(new Choice(co->T("Calibrate analog stick")))->OnClick.Add([this](UI::EventParams &e) {
849
screenManager()->push(new AnalogCalibrationScreen(gamePath_));
850
});
851
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogTriggerThreshold, 0.02f, 0.98f, 0.75f, co->T("Analog trigger threshold"), screenManager()));
852
853
#if defined(USING_WIN_UI) || (PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID))
854
controlsSettings->Add(new CheckBox(&g_Config.bSystemControls, co->T("Enable standard shortcut keys")));
855
#endif
856
#if defined(USING_WIN_UI)
857
controlsSettings->Add(new CheckBox(&g_Config.bGamepadOnlyFocused, co->T("Ignore gamepads when not focused")));
858
#endif
859
860
if (System_GetPropertyBool(SYSPROP_HAS_ACCELEROMETER)) {
861
// Show the tilt type on the item.
862
Choice *customizeTilt = controlsSettings->Add(new ChoiceWithCallbackValueDisplay(co->T("Tilt control setup"), []() -> std::string {
863
if (g_Config.bTiltInputEnabled && (u32)g_Config.iTiltInputType < (u32)g_numTiltTypes) {
864
auto co = GetI18NCategory(I18NCat::CONTROLS);
865
return std::string(co->T(g_tiltTypes[g_Config.iTiltInputType]));
866
} else {
867
auto di = GetI18NCategory(I18NCat::DIALOG);
868
return std::string(di->T("Disabled"));
869
}
870
}));
871
customizeTilt->OnClick.Add([this](UI::EventParams &e) {
872
screenManager()->push(new TiltAnalogSettingsScreen(gamePath_));
873
});
874
} else if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) { // TODO: This seems like a regression
875
controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));
876
}
877
878
// TVs don't have touch control, at least not yet.
879
if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {
880
controlsSettings->Add(new ItemHeader(co->T("On-screen touch controls")));
881
controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, co->T("On-screen touch controls")));
882
Choice *layoutEditorChoice = controlsSettings->Add(new Choice(co->T("Edit touch control layout")));
883
layoutEditorChoice->OnClick.Add([this](UI::EventParams &e) {
884
screenManager()->push(new TouchControlLayoutScreen(gamePath_));
885
});
886
layoutEditorChoice->SetEnabledPtr(&g_Config.bShowTouchControls);
887
888
Choice *gesture = controlsSettings->Add(new Choice(co->T("Gesture mapping")));
889
gesture->OnClick.Add([=](EventParams &e) {
890
screenManager()->push(new GestureMappingScreen(gamePath_));
891
});
892
gesture->SetEnabledPtr(&g_Config.bShowTouchControls);
893
894
static const char *touchControlStyles[] = { "Classic", "Thin borders", "Glowing borders" };
895
View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), I18NCat::CONTROLS, screenManager()));
896
style->SetEnabledPtr(&g_Config.bShowTouchControls);
897
898
PopupSliderChoice *opacity = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 100, 65, co->T("Button Opacity"), screenManager(), "%"));
899
opacity->SetEnabledPtr(&g_Config.bShowTouchControls);
900
opacity->SetFormat("%i%%");
901
PopupSliderChoice *autoHide = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonHideSeconds, 0, 300, 20, co->T("Auto-hide buttons after delay"), screenManager(), di->T("seconds, 0:off")));
902
autoHide->SetEnabledPtr(&g_Config.bShowTouchControls);
903
autoHide->SetFormat(di->T("%d seconds"));
904
autoHide->SetZeroLabel(co->T("Off"));
905
906
CheckBox *touchGliding = controlsSettings->Add(new CheckBox(&g_Config.bTouchGliding, co->T("Keep first touched button pressed when dragging")));
907
touchGliding->SetEnabledPtr(&g_Config.bShowTouchControls);
908
909
TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation());
910
911
// Hide stick background, useful when increasing the size
912
CheckBox *hideStickBackground = controlsSettings->Add(new CheckBox(&touch.bHideStickBackground, co->T("Hide touch analog stick background circle")));
913
hideStickBackground->SetEnabledPtr(&g_Config.bShowTouchControls);
914
915
// Sticky D-pad.
916
CheckBox *stickyDpad = controlsSettings->Add(new CheckBox(&g_Config.bStickyTouchDPad, co->T("Sticky D-Pad (easier sweeping movements)")));
917
stickyDpad->SetEnabledPtr(&g_Config.bShowTouchControls);
918
919
// Re-centers itself to the touch location on touch-down.
920
CheckBox *floatingAnalog = controlsSettings->Add(new CheckBox(&g_Config.bAutoCenterTouchAnalog, co->T("Auto-centering analog stick")));
921
floatingAnalog->SetEnabledPtr(&g_Config.bShowTouchControls);
922
923
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
924
controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));
925
}
926
927
// The pause button is now a regular on-screen button.
928
929
CheckBox *disableDiags = controlsSettings->Add(new CheckBox(&g_Config.bDisableDpadDiagonals, co->T("Disable D-Pad diagonals (4-way touch)")));
930
disableDiags->SetEnabledPtr(&g_Config.bShowTouchControls);
931
}
932
933
if (deviceType != DEVICE_TYPE_VR) {
934
controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));
935
#if defined(USING_WIN_UI)
936
controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, co->T("Ignore Windows Key")));
937
#endif // #if defined(USING_WIN_UI)
938
auto analogLimiter = new PopupSliderChoiceFloat(&g_Config.fAnalogLimiterDeadzone, 0.0f, 1.0f, 0.6f, co->T("Analog Limiter"), 0.10f, screenManager(), "/ 1.0");
939
controlsSettings->Add(analogLimiter);
940
controlsSettings->Add(new SettingHint(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed")));
941
controlsSettings->Add(new PopupSliderChoice(&g_Config.iRapidFireInterval, 1, 10, 5, co->T("Rapid fire interval"), screenManager(), "frames"));
942
#if defined(USING_WIN_UI) || defined(SDL) || PPSSPP_PLATFORM(ANDROID)
943
bool enableMouseSettings = true;
944
#if PPSSPP_PLATFORM(ANDROID)
945
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) < 12) {
946
enableMouseSettings = false;
947
}
948
#endif
949
#else
950
bool enableMouseSettings = false;
951
#endif
952
if (enableMouseSettings) {
953
// The mousewheel button-release setting is independent of actual mouse delta control.
954
controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings")));
955
auto wheelUpDelaySlider = controlsSettings->Add(new PopupSliderChoice(&g_Config.iMouseWheelUpDelayMs, 10, 300, 1, co->T("Mouse wheel button-release delay"), screenManager()));
956
wheelUpDelaySlider->SetFormat(di->T("%d ms"));
957
958
CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control")));
959
controlsSettings->Add(new SettingHint(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon.")));
960
961
#if !PPSSPP_PLATFORM(ANDROID)
962
controlsSettings->Add(new CheckBox(&g_Config.bMouseConfine, co->T("Confine Mouse", "Trap mouse within window/display area")))->SetEnabledPtr(&g_Config.bMouseControl);
963
#endif
964
auto sensitivitySlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSensitivity, 0.01f, 1.0f, 0.1f, co->T("Mouse sensitivity"), 0.01f, screenManager(), "x"));
965
sensitivitySlider->SetEnabledPtr(&g_Config.bMouseControl);
966
sensitivitySlider->SetLiveUpdate(true);
967
auto smoothingSlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, 0.9f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"));
968
smoothingSlider->SetEnabledPtr(&g_Config.bMouseControl);
969
smoothingSlider->SetLiveUpdate(true);
970
}
971
}
972
}
973
974
// Compound view just like the audio file choosers
975
class MacAddressChooser : public UI::LinearLayout {
976
public:
977
MacAddressChooser(RequesterToken token, Path gamePath, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr);
978
};
979
980
MacAddressChooser::MacAddressChooser(RequesterToken token, Path gamePath, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(ORIENT_HORIZONTAL, layoutParams) {
981
using namespace UI;
982
SetSpacing(5.0f);
983
if (!layoutParams) {
984
layoutParams_->width = FILL_PARENT;
985
layoutParams_->height = ITEM_HEIGHT;
986
}
987
auto n = GetI18NCategory(I18NCat::NETWORKING);
988
989
std::string initialValue = *value;
990
Add(new PopupTextInputChoice(token, value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) {
991
// Validate the chosen address, and restore to initialValue if bad.
992
if (g_Config.sMACAddress.size() != 17) {
993
// TODO: Alert the user
994
*value = initialValue;
995
}
996
});
997
Add(new Choice(n->T("Randomize"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) {
998
auto n = GetI18NCategory(I18NCat::NETWORKING);
999
auto di = GetI18NCategory(I18NCat::DIALOG);
1000
1001
std::string_view confirmMessage = n->T("ChangeMacSaveConfirm", "Generate a new MAC address?");
1002
std::string_view warningMessage = n->T("ChangeMacSaveWarning", "Some games verify the MAC address when loading savedata, so this may break old saves.");
1003
std::string combined = g_Config.sMACAddress + "\n\n" + std::string(confirmMessage) + "\n\n" + std::string(warningMessage);
1004
1005
auto confirmScreen = new PromptScreen(
1006
gamePath,
1007
combined, di->T("Yes"), di->T("No"),
1008
[&](bool success) {
1009
if (success) {
1010
g_Config.sMACAddress = CreateRandMAC();
1011
}}
1012
);
1013
screenManager->push(confirmScreen);
1014
});
1015
}
1016
1017
void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) {
1018
using namespace UI;
1019
1020
auto n = GetI18NCategory(I18NCat::NETWORKING);
1021
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
1022
auto di = GetI18NCategory(I18NCat::DIALOG);
1023
1024
networkingSettings->Add(new ItemHeader(ms->T("Networking")));
1025
1026
Choice *wiki = networkingSettings->Add(new Choice(n->T("Open PPSSPP Multiplayer Wiki Page"), ImageID("I_LINK_OUT")));
1027
wiki->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides);
1028
1029
networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)")));
1030
networkingSettings->Add(new MacAddressChooser(GetRequesterToken(), gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager()));
1031
static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" };
1032
auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, n->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), I18NCat::NETWORKING, screenManager()));
1033
for (int i = 0; i < 4; i++) {
1034
wlanChannelChoice->HideChoice(i + 2);
1035
wlanChannelChoice->HideChoice(i + 7);
1036
}
1037
1038
if (Discord::IsAvailable()) {
1039
networkingSettings->Add(new CheckBox(&g_Config.bDiscordRichPresence, n->T("Send Discord Presence information")));
1040
}
1041
1042
networkingSettings->Add(new ItemHeader(n->T("Ad Hoc multiplayer")));
1043
networkingSettings->Add(new CheckBox(&g_Config.bUseServerRelay, n->T("Try to use server-provided packet relay")))->SetEnabled(!PSP_IsInited());
1044
networkingSettings->Add(new SettingHint(n->T("PacketRelayHint", "Available on servers that provide 'aemu_postoffice' packet relay, like socom.cc. Disable this for LAN or VPN play. Can be more reliable, but sometimes slower.")));
1045
1046
networkingSettings->Add(new ItemHeader(n->T("Ad Hoc server")));
1047
networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server")));
1048
networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sProAdhocServer, n->T("Change proAdhocServer Address"), I18NCat::NONE))->OnClick.Add([=](UI::EventParams &) {
1049
screenManager()->push(new HostnameSelectScreen(&g_Config.sProAdhocServer, &g_Config.proAdhocServerList, n->T("proAdhocServer Address:")));
1050
});
1051
networkingSettings->Add(new SettingHint(n->T("Change proAdhocServer address hint")));
1052
1053
networkingSettings->Add(new ItemHeader(n->T("Infrastructure")));
1054
if (g_Config.sInfrastructureUsername.empty()) {
1055
networkingSettings->Add(new NoticeView(NoticeLevel::WARN, n->T("To play in Infrastructure Mode, you must enter a username"), ""));
1056
}
1057
PopupTextInputChoice *usernameChoice = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sInfrastructureUsername, di->T("Username"), "", 16, screenManager()));
1058
usernameChoice->SetRestriction(StringRestriction::AlphaNumDashUnderscore, 3);
1059
usernameChoice->OnChange.Add([this](UI::EventParams &e) {
1060
RecreateViews();
1061
});
1062
1063
networkingSettings->Add(new CheckBox(&g_Config.bInfrastructureAutoDNS, n->T("Autoconfigure")));
1064
auto *dnsServer = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sInfrastructureDNSServer, n->T("DNS server"), "", 32, screenManager()));
1065
dnsServer->SetDisabledPtr(&g_Config.bInfrastructureAutoDNS);
1066
1067
networkingSettings->Add(new ItemHeader(n->T("UPnP (port-forwarding)")));
1068
networkingSettings->Add(new CheckBox(&g_Config.bEnableUPnP, n->T("Enable UPnP", "Enable UPnP (need a few seconds to detect)")));
1069
auto useOriPort = networkingSettings->Add(new CheckBox(&g_Config.bUPnPUseOriginalPort, n->T("UPnP use original port", "UPnP use original port (Enabled = PSP compatibility)")));
1070
networkingSettings->Add(new SettingHint(n->T("UseOriginalPort Tip", "May not work for all devices or games, see wiki.")));
1071
1072
useOriPort->SetEnabledPtr(&g_Config.bEnableUPnP);
1073
1074
networkingSettings->Add(new ItemHeader(n->T("Chat")));
1075
networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat")));
1076
static const char *chatButtonPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };
1077
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
1078
static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right" };
1079
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
1080
1081
#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) // Missing only iOS?
1082
networkingSettings->Add(new ItemHeader(n->T("QuickChat", "Quick Chat")));
1083
CheckBox *qc = networkingSettings->Add(new CheckBox(&g_Config.bEnableQuickChat, n->T("EnableQuickChat", "Enable Quick Chat")));
1084
qc->SetEnabledPtr(&g_Config.bEnableNetworkChat);
1085
#endif
1086
1087
#if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) // TODO: Add all platforms where KeyInputFlags::CHAR support is added
1088
PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat[0], n->T("Quick Chat 1"), "", 32, screenManager()));
1089
PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat[1], n->T("Quick Chat 2"), "", 32, screenManager()));
1090
PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat[2], n->T("Quick Chat 3"), "", 32, screenManager()));
1091
PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat[3], n->T("Quick Chat 4"), "", 32, screenManager()));
1092
PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat[4], n->T("Quick Chat 5"), "", 32, screenManager()));
1093
#elif defined(USING_QT_UI)
1094
Choice *qc1 = networkingSettings->Add(new Choice(n->T("Quick Chat 1")));
1095
Choice *qc2 = networkingSettings->Add(new Choice(n->T("Quick Chat 2")));
1096
Choice *qc3 = networkingSettings->Add(new Choice(n->T("Quick Chat 3")));
1097
Choice *qc4 = networkingSettings->Add(new Choice(n->T("Quick Chat 4")));
1098
Choice *qc5 = networkingSettings->Add(new Choice(n->T("Quick Chat 5")));
1099
#elif PPSSPP_PLATFORM(ANDROID)
1100
ChoiceWithValueDisplay *qc1 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat[0], n->T("Quick Chat 1"), I18NCat::NONE));
1101
ChoiceWithValueDisplay *qc2 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat[1], n->T("Quick Chat 2"), I18NCat::NONE));
1102
ChoiceWithValueDisplay *qc3 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat[2], n->T("Quick Chat 3"), I18NCat::NONE));
1103
ChoiceWithValueDisplay *qc4 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat[3], n->T("Quick Chat 4"), I18NCat::NONE));
1104
ChoiceWithValueDisplay *qc5 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat[4], n->T("Quick Chat 5"), I18NCat::NONE));
1105
#endif
1106
1107
#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
1108
qc1->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
1109
qc2->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
1110
qc3->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
1111
qc4->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
1112
qc5->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
1113
#endif
1114
1115
#if defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
1116
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
1117
qc1->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat0);
1118
qc2->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat1);
1119
qc3->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat2);
1120
qc4->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat3);
1121
qc5->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat4);
1122
}
1123
#endif
1124
1125
networkingSettings->Add(new ItemHeader(n->T("Misc", "Misc (default = compatibility)")));
1126
networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, 10000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager()));
1127
networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 0, 15000, 0, n->T("Minimum Timeout", "Minimum Timeout (override in ms, 0 = default)"), 50, screenManager()))->SetFormat(di->T("%d ms"));
1128
networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)")));
1129
networkingSettings->Add(new CheckBox(&g_Config.bAllowSpeedControlWhileConnected, n->T("Allow speed control while connected (not recommended)")));
1130
}
1131
1132
void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) {
1133
using namespace UI;
1134
1135
auto sa = GetI18NCategory(I18NCat::SAVEDATA);
1136
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1137
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
1138
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
1139
auto ri = GetI18NCategory(I18NCat::REMOTEISO);
1140
1141
tools->Add(new ItemHeader(ms->T("Tools")));
1142
1143
const bool showRetroAchievements = System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR;
1144
if (showRetroAchievements) {
1145
auto retro = tools->Add(new Choice(sy->T("RetroAchievements")));
1146
retro->OnClick.Add([=](UI::EventParams &) -> void {
1147
screenManager()->push(new RetroAchievementsSettingsScreen(gamePath_));
1148
});
1149
retro->SetIconRight(ImageID("I_RETROACHIEVEMENTS_LOGO"));
1150
}
1151
1152
// These were moved here so use the wrong translation objects, to avoid having to change all inis... This isn't a sustainable situation :P
1153
tools->Add(new Choice(sa->T("Savedata Manager")))->OnClick.Add([=](UI::EventParams &) {
1154
screenManager()->push(new SavedataScreen(gamePath_));
1155
});
1156
tools->Add(new Choice(dev->T("System Information")))->OnClick.Add([=](UI::EventParams &) {
1157
screenManager()->push(new SystemInfoScreen(gamePath_));
1158
});
1159
tools->Add(new Choice(sy->T("Developer Tools")))->OnClick.Add([=](UI::EventParams &) {
1160
screenManager()->push(new DeveloperToolsScreen(gamePath_));
1161
});
1162
tools->Add(new Choice(ri->T("Remote disc streaming")))->OnClick.Add([=](UI::EventParams &) {
1163
screenManager()->push(new RemoteISOScreen(gamePath_));
1164
});
1165
}
1166
1167
void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
1168
using namespace UI;
1169
1170
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1171
auto di = GetI18NCategory(I18NCat::DIALOG);
1172
auto vr = GetI18NCategory(I18NCat::VR);
1173
auto th = GetI18NCategory(I18NCat::THEMES);
1174
auto psps = GetI18NCategory(I18NCat::PSPSETTINGS); // TODO: Should move more into this section.
1175
1176
systemSettings->Add(new ItemHeader(sy->T("UI")));
1177
1178
auto langCodeToName = [](std::string_view value) -> std::string {
1179
auto &mapping = GetLangValuesMapping();
1180
auto iter = mapping.find(value);
1181
if (iter != mapping.end()) {
1182
return iter->second.first;
1183
}
1184
return std::string(value);
1185
};
1186
1187
systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.sLanguageIni, sy->T("Language"), langCodeToName))->OnClick.Add([&](UI::EventParams &e) {
1188
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1189
auto langScreen = new NewLanguageScreen(sy->T("Language"));
1190
// The actual switching is handled in OnCompleted in NewLanguageScreen.
1191
if (e.v)
1192
langScreen->SetPopupOrigin(e.v);
1193
screenManager()->push(langScreen);
1194
});
1195
1196
#if PPSSPP_PLATFORM(IOS)
1197
static const char *indicator[] = {
1198
"Swipe once to switch app (indicator auto-hides)",
1199
"Swipe twice to switch app (indicator stays visible)"
1200
};
1201
PopupMultiChoice *switchMode = systemSettings->Add(new PopupMultiChoice(&g_Config.iAppSwitchMode, sy->T("App switching mode"), indicator, 0, ARRAY_SIZE(indicator), I18NCat::SYSTEM, screenManager()));
1202
switchMode->OnChoice.Add([](EventParams &e) {
1203
System_Notify(SystemNotification::APP_SWITCH_MODE_CHANGED);
1204
});
1205
#endif
1206
1207
PopupSliderChoice *uiScale = systemSettings->Add(new PopupSliderChoice(&g_Config.iUIScaleFactor, -8, 8, 0, sy->T("UI size adjustment (DPI)"), screenManager()));
1208
uiScale->SetZeroLabel(sy->T("Off"));
1209
UIContext *ctx = screenManager()->getUIContext();
1210
uiScale->OnChange.Add([ctx](UI::EventParams &e) {
1211
const float dpiMul = UIScaleFactorToMultiplier(g_Config.iUIScaleFactor);
1212
g_display.Recalculate(-1, -1, -1, -1, dpiMul);
1213
ctx->InvalidateAtlas();
1214
NativeResized();
1215
});
1216
1217
const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";
1218
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
1219
Choice *backgroundChoice = nullptr;
1220
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
1221
backgroundChoice = systemSettings->Add(new Choice(sy->T("Clear UI background")));
1222
} else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER) || System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) {
1223
backgroundChoice = systemSettings->Add(new Choice(sy->T("Set UI background...")));
1224
}
1225
if (backgroundChoice) {
1226
backgroundChoice->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground);
1227
}
1228
1229
systemSettings->Add(new CheckBox(&g_Config.bTransparentBackground, sy->T("Transparent UI background")));
1230
1231
// Shared with achievements.
1232
static const char *positions[] = { "None", "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right" };
1233
1234
systemSettings->Add(new PopupMultiChoice(&g_Config.iNotificationPos, sy->T("Notification screen position"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()));
1235
1236
static const char *backgroundAnimations[] = { "No animation", "Floating symbols", "Recent games", "Waves", "Moving background", "Bouncing icon", "Colored floating symbols" };
1237
systemSettings->Add(new PopupMultiChoice(&g_Config.iBackgroundAnimation, sy->T("UI background animation"), backgroundAnimations, 0, ARRAY_SIZE(backgroundAnimations), I18NCat::SYSTEM, screenManager()));
1238
1239
PopupMultiChoiceDynamic *theme = systemSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, sy->T("Theme"), GetThemeInfoNames(), I18NCat::THEMES, screenManager()));
1240
theme->OnChoice.Add([](EventParams &e) {
1241
UpdateTheme();
1242
// Reset the tint/saturation if the theme changed.
1243
if (e.b) {
1244
g_Config.fUITint = 0.0f;
1245
g_Config.fUISaturation = 1.0f;
1246
}
1247
});
1248
1249
Draw::DrawContext *draw = screenManager()->getDrawContext();
1250
1251
if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) {
1252
// We use shaders without tint capability on hardware with this driver bug.
1253
PopupSliderChoiceFloat *tint = new PopupSliderChoiceFloat(&g_Config.fUITint, 0.0f, 1.0f, 0.0f, sy->T("Color tint"), 0.01f, screenManager());
1254
tint->SetHasDropShadow(false);
1255
tint->SetLiveUpdate(true);
1256
systemSettings->Add(tint);
1257
PopupSliderChoiceFloat *saturation = new PopupSliderChoiceFloat(&g_Config.fUISaturation, 0.0f, 2.0f, 1.0f, sy->T("Color saturation"), 0.01f, screenManager());
1258
saturation->SetHasDropShadow(false);
1259
saturation->SetLiveUpdate(true);
1260
systemSettings->Add(saturation);
1261
}
1262
1263
systemSettings->Add(new ItemHeader(sy->T("PSP Memory Stick")));
1264
1265
if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) {
1266
systemSettings->Add(new Choice(sy->T("Show Memory Stick folder")))->OnClick.Add([](UI::EventParams &p) {
1267
System_LaunchUrl(LaunchUrlType::LOCAL_FOLDER, g_Config.memStickDirectory.ToString());
1268
});
1269
}
1270
1271
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
1272
bool showItHere = true;
1273
#if PPSSPP_PLATFORM(IOS_APP_STORE)
1274
if (g_Config.memStickDirectory == DarwinFileSystemServices::defaultMemoryStickPath()) {
1275
// We still keep a way to access it on the developer tools screen.
1276
showItHere = false;
1277
}
1278
#endif
1279
if (showItHere) {
1280
systemSettings->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
1281
[=](UI::EventParams &) {
1282
SetMemStickDirDarwin(GetRequesterToken());
1283
});
1284
}
1285
#endif
1286
1287
#if PPSSPP_PLATFORM(ANDROID)
1288
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) {
1289
memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();
1290
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder"), I18NCat::NONE));
1291
memstickPath->SetEnabled(!PSP_IsInited());
1292
memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);
1293
1294
// Display USB path for convenience.
1295
std::string usbPath;
1296
if (PathToVisualUsbPath(g_Config.memStickDirectory, usbPath)) {
1297
if (usbPath.empty()) {
1298
// Probably it's just the root. So let's add PSP to make it clear.
1299
usbPath = "/PSP";
1300
}
1301
}
1302
}
1303
#elif defined(_WIN32)
1304
#if PPSSPP_PLATFORM(UWP)
1305
memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();
1306
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder"), I18NCat::NONE));
1307
memstickPath->SetEnabled(!PSP_IsInited());
1308
memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);
1309
#else
1310
SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Memory Stick in My Documents")));
1311
SavePathInMyDocumentChoice->SetEnabled(!PSP_IsInited());
1312
SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickMyDoc);
1313
SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Memory Stick in installed.txt")));
1314
SavePathInOtherChoice->SetEnabled(false);
1315
SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickOther);
1316
const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0;
1317
1318
const Path &PPSSPPpath = File::GetExeDirectory();
1319
const Path installedFile = PPSSPPpath / "installed.txt";
1320
installed_ = File::Exists(installedFile);
1321
otherinstalled_ = false;
1322
if (!installed_ && myDocsExists) {
1323
if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) {
1324
// Disable the setting whether cannot create & delete file
1325
if (!(File::Delete(PPSSPPpath / "installedTEMP.txt")))
1326
SavePathInMyDocumentChoice->SetEnabled(false);
1327
else
1328
SavePathInOtherChoice->SetEnabled(!PSP_IsInited());
1329
} else
1330
SavePathInMyDocumentChoice->SetEnabled(false);
1331
} else {
1332
if (installed_ && myDocsExists) {
1333
FILE *testInstalled = File::OpenCFile(installedFile, "rt");
1334
if (testInstalled) {
1335
char temp[2048];
1336
char *tempStr = fgets(temp, sizeof(temp), testInstalled);
1337
// Skip UTF-8 encoding bytes if there are any. There are 3 of them.
1338
if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) {
1339
tempStr += 3;
1340
}
1341
SavePathInOtherChoice->SetEnabled(!PSP_IsInited());
1342
if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) {
1343
installed_ = false;
1344
otherinstalled_ = true;
1345
}
1346
fclose(testInstalled);
1347
}
1348
} else if (!myDocsExists) {
1349
SavePathInMyDocumentChoice->SetEnabled(false);
1350
}
1351
}
1352
#endif
1353
#endif
1354
systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted")));
1355
UI::PopupSliderChoice *sizeChoice = systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, 16, sy->T("Memory Stick size", "Memory Stick size"), screenManager(), "GB"));
1356
sizeChoice->SetFormat("%d GB");
1357
1358
systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team")));
1359
if (!enableReportsSet_)
1360
enableReports_ = Reporting::IsEnabled();
1361
enableReportsSet_ = true;
1362
CheckBox *enableReportsCheckbox;
1363
enableReportsCheckbox = new CheckBox(&enableReports_, sy->T("Enable Compatibility Server Reports"));
1364
enableReportsCheckbox->SetEnabledFunc([]() { return Reporting::IsSupported(); });
1365
systemSettings->Add(enableReportsCheckbox);
1366
1367
systemSettings->Add(new ItemHeader(sy->T("Emulation")));
1368
1369
systemSettings->Add(new CheckBox(&g_Config.bFastMemory, sy->T("Fast Memory", "Fast Memory")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);
1370
systemSettings->Add(new CheckBox(&g_Config.bIgnoreBadMemAccess, sy->T("Ignore bad memory accesses")));
1371
1372
static const char *ioTimingMethods[] = { "Fast (lag on slow storage)", "Host (bugs, less lag)", "Simulate UMD delays", "Simulate UMD slow reading speed"};
1373
View *ioTimingMethod = systemSettings->Add(new PopupMultiChoice(&g_Config.iIOTimingMethod, sy->T("I/O timing method"), ioTimingMethods, 0, ARRAY_SIZE(ioTimingMethods), I18NCat::SYSTEM, screenManager()));
1374
systemSettings->Add(new CheckBox(&g_Config.bForceLagSync, sy->T("Force real clock sync (slower, less lag)")))->SetDisabledPtr(&g_Config.bAutoFrameSkip);
1375
PopupSliderChoice *lockedMhz = systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, 0, sy->T("Change CPU Clock", "Change CPU Clock (unstable)"), screenManager(), sy->T("MHz, 0:default")));
1376
lockedMhz->SetZeroLabel(sy->T("Auto"));
1377
1378
auto sa = GetI18NCategory(I18NCat::SAVEDATA);
1379
1380
systemSettings->Add(new ItemHeader(sa->T("Save states"))); // Borrow this string from the savedata manager
1381
1382
systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));
1383
1384
PopupSliderChoice* savestateSlotCount = systemSettings->Add(new PopupSliderChoice(&g_Config.iSaveStateSlotCount, 1, 30, 5, sy->T("Savestate slot count"), screenManager()));
1385
savestateSlotCount->OnChange.Add([](UI::EventParams &e) {
1386
System_Notify(SystemNotification::UI);
1387
});
1388
1389
// NOTE: We will soon support more states, but we'll keep this niche feature limited to the first five.
1390
static const char *autoLoadSaveStateChoices[] = {"Off", "Oldest Save", "Newest Save", "Slot 1", "Slot 2", "Slot 3", "Slot 4", "Slot 5"};
1391
PopupMultiChoice *autoloadSaveState = systemSettings->Add(new PopupMultiChoice(&g_Config.iAutoLoadSaveState, sy->T("Auto load savestate"), autoLoadSaveStateChoices, 0, ARRAY_SIZE(autoLoadSaveStateChoices), I18NCat::SYSTEM, screenManager()));
1392
if (g_Config.iAutoLoadSaveState != 1) {
1393
autoloadSaveState->HideChoice(1); // Hide "Oldest Save" if not using that mode. It doesn't make sense.
1394
}
1395
1396
PopupSliderChoice *rewindInterval = systemSettings->Add(new PopupSliderChoice(&g_Config.iRewindSnapshotInterval, 0, 60, 0, sy->T("Rewind Snapshot Interval"), screenManager(), di->T("seconds, 0:off")));
1397
rewindInterval->SetFormat(di->T("%d seconds"));
1398
rewindInterval->SetZeroLabel(sy->T("Off"));
1399
1400
systemSettings->Add(new ItemHeader(sy->T("General")));
1401
1402
PopupSliderChoice *exitConfirmation = systemSettings->Add(new PopupSliderChoice(&g_Config.iAskForExitConfirmationAfterSeconds, 0, 1200, 300, sy->T("Ask for exit confirmation after seconds"), screenManager(), "s"));
1403
exitConfirmation->SetZeroLabel(sy->T("Off"));
1404
1405
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
1406
auto co = GetI18NCategory(I18NCat::CONTROLS);
1407
1408
AddRotationPicker(screenManager(), systemSettings, true);
1409
1410
if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) {
1411
systemSettings->Add(new CheckBox(&g_Config.bSustainedPerformanceMode, sy->T("Sustained performance mode")))->OnClick.Handle(this, &GameSettingsScreen::OnSustainedPerformanceModeChange);
1412
}
1413
}
1414
1415
systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings);
1416
1417
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD))
1418
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));
1419
1420
if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) {
1421
systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited());
1422
}
1423
1424
systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));
1425
systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG")));
1426
static const char *screenshotModeChoices[] = { "Final processed image", "Raw game image" };
1427
systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenshotMode, sy->T("Screenshot mode"), screenshotModeChoices, 0, ARRAY_SIZE(screenshotModeChoices), I18NCat::SYSTEM, screenManager()));
1428
// TODO: Make this setting available on Mac too.
1429
#if PPSSPP_PLATFORM(WINDOWS)
1430
systemSettings->Add(new CheckBox(&g_Config.bPauseOnLostFocus, sy->T("Pause when not focused")));
1431
#endif
1432
1433
systemSettings->Add(new ItemHeader(sy->T("Cheats", "Cheats")));
1434
systemSettings->Add(new CheckBox(&g_Config.bEnableCheats, sy->T("Enable Cheats")));
1435
systemSettings->Add(new CheckBox(&g_Config.bEnablePlugins, sy->T("Enable plugins")));
1436
1437
systemSettings->Add(new ItemHeader(sy->T("PSP Settings")));
1438
1439
// The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*.
1440
static const char *defaultLanguages[] = { "Auto", "Japanese", "English", "French", "Spanish", "German", "Italian", "Dutch", "Portuguese", "Russian", "Korean", "Chinese (traditional)", "Chinese (simplified)" };
1441
systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager()));
1442
static const char *models[] = { "PSP-1000", "PSP-2000/3000" };
1443
systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited());
1444
systemSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager()))->OnChange.Add([](UI::EventParams &e) {
1445
// Copy to infrastructure name if valid and not already set.
1446
if (g_Config.sInfrastructureUsername.empty()) {
1447
if (g_Config.sNickName == SanitizeString(g_Config.sNickName, StringRestriction::AlphaNumDashUnderscore, 3, 16)) {
1448
g_Config.sInfrastructureUsername = g_Config.sNickName;
1449
}
1450
}
1451
});
1452
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Daylight savings")));
1453
static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY" };
1454
systemSettings->Add(new PopupMultiChoice(&g_Config.iDateFormat, sy->T("Date Format"), dateFormat, 0, ARRAY_SIZE(dateFormat), I18NCat::SYSTEM, screenManager()));
1455
static const char *timeFormat[] = { "24HR", "12HR" };
1456
systemSettings->Add(new PopupMultiChoice(&g_Config.iTimeFormat, sy->T("Time Format"), timeFormat, 0, ARRAY_SIZE(timeFormat), I18NCat::SYSTEM, screenManager()));
1457
static const char *buttonPref[] = { "Use O to confirm", "Use X to confirm" };
1458
systemSettings->Add(new PopupMultiChoice(&g_Config.iButtonPreference, sy->T("Confirmation Button"), buttonPref, 0, ARRAY_SIZE(buttonPref), I18NCat::SYSTEM, screenManager()));
1459
1460
#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))
1461
systemSettings->Add(new ItemHeader(sy->T("Recording")));
1462
systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Record Display")));
1463
systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use Lossless Video Codec (FFV1)")));
1464
systemSettings->Add(new CheckBox(&g_Config.bDumpVideoOutput, sy->T("Use output buffer (with overlay) for recording")));
1465
systemSettings->Add(new CheckBox(&g_Config.bDumpAudio, sy->T("Record Audio")));
1466
systemSettings->Add(new CheckBox(&g_Config.bSaveLoadResetsAVdumping, sy->T("Reset Recording on Save/Load State")));
1467
#endif
1468
}
1469
1470
void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
1471
using namespace UI;
1472
1473
auto vr = GetI18NCategory(I18NCat::VR);
1474
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
1475
1476
if (deviceType == DEVICE_TYPE_VR) {
1477
vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));
1478
vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Virtual reality")));
1479
vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("6DoF movement")));
1480
vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));
1481
vrSettings->Add(new CheckBox(&g_Config.bEnableImmersiveVR, vr->T("Enable immersive mode")));
1482
if (IsPassthroughSupported()) {
1483
vrSettings->Add(new CheckBox(&g_Config.bPassthrough, vr->T("Enable passthrough")));
1484
}
1485
vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));
1486
}
1487
1488
vrSettings->Add(new ItemHeader(vr->T("VR camera")));
1489
if (deviceType == DEVICE_TYPE_VR) {
1490
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, 12.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));
1491
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvas3DDistance, 1.0f, 15.0f, 3.0f, vr->T("Distance to 3D scenes when VR disabled"), 1.0f, screenManager(), ""));
1492
}
1493
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fFieldOfViewPercentage, 100.0f, 200.0f, 100.0f, vr->T("Field of view scale"), 10.0f, screenManager(), vr->T("% of native FoV")));
1494
vrSettings->Add(new CheckBox(&g_Config.bRescaleHUD, vr->T("Heads-up display detection")));
1495
PopupSliderChoiceFloat* vrHudScale = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadUpDisplayScale, 0.0f, 1.5f, 0.3f, vr->T("Heads-up display scale"), 0.1f, screenManager(), ""));
1496
vrHudScale->SetEnabledPtr(&g_Config.bRescaleHUD);
1497
vrSettings->Add(new CheckBox(&g_Config.bManualForceVR, vr->T("Manual switching between flat screen and VR using SCREEN key")));
1498
}
1499
1500
void GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) {
1501
auto n = GetI18NCategory(I18NCat::NETWORKING);
1502
std::string url(n->T("MultiplayerHowToURL", "https://github.com/hrydgard/ppsspp/wiki/How-to-play-multiplayer-games-with-PPSSPP"));
1503
System_LaunchUrl(LaunchUrlType::BROWSER_URL, url.c_str());
1504
}
1505
1506
void GameSettingsScreen::OnImmersiveModeChange(UI::EventParams &e) {
1507
System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);
1508
if (g_Config.iAndroidHwScale != 0) {
1509
System_RecreateActivity();
1510
}
1511
}
1512
1513
void GameSettingsScreen::OnSustainedPerformanceModeChange(UI::EventParams &e) {
1514
System_Notify(SystemNotification::SUSTAINED_PERF_CHANGE);
1515
}
1516
1517
void GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) {
1518
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
1519
}
1520
1521
void GameSettingsScreen::OnShowMemstickScreen(UI::EventParams &e) {
1522
screenManager()->push(new MemStickScreen(false));
1523
}
1524
1525
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
1526
1527
void GameSettingsScreen::OnMemoryStickMyDoc(UI::EventParams &e) {
1528
const Path &PPSSPPpath = File::GetExeDirectory();
1529
const Path installedFile = PPSSPPpath / "installed.txt";
1530
installed_ = File::Exists(installedFile);
1531
if (otherinstalled_) {
1532
File::Delete(PPSSPPpath / "installed.txt");
1533
File::CreateEmptyFile(PPSSPPpath / "installed.txt");
1534
otherinstalled_ = false;
1535
const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";
1536
g_Config.memStickDirectory = Path(myDocsPath);
1537
} else if (installed_) {
1538
File::Delete(PPSSPPpath / "installed.txt");
1539
installed_ = false;
1540
g_Config.memStickDirectory = PPSSPPpath / "memstick";
1541
} else {
1542
FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");
1543
if (f) {
1544
fclose(f);
1545
}
1546
1547
const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";
1548
g_Config.memStickDirectory = Path(myDocsPath);
1549
installed_ = true;
1550
}
1551
}
1552
1553
void GameSettingsScreen::OnMemoryStickOther(UI::EventParams &e) {
1554
const Path &PPSSPPpath = File::GetExeDirectory();
1555
if (otherinstalled_) {
1556
auto di = GetI18NCategory(I18NCat::DIALOG);
1557
std::string initialPath = g_Config.memStickDirectory.ToCString();
1558
std::string folder = W32Util::BrowseForFolder2(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder"), initialPath);
1559
if (folder.size()) {
1560
g_Config.memStickDirectory = Path(folder);
1561
FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");
1562
if (f) {
1563
std::string utfstring("\xEF\xBB\xBF");
1564
utfstring.append(folder);
1565
fwrite(utfstring.c_str(), 1, utfstring.length(), f);
1566
fclose(f);
1567
}
1568
installed_ = false;
1569
}
1570
else
1571
otherinstalled_ = false;
1572
}
1573
else {
1574
File::Delete(PPSSPPpath / "installed.txt");
1575
SavePathInMyDocumentChoice->SetEnabled(true);
1576
otherinstalled_ = false;
1577
installed_ = false;
1578
g_Config.memStickDirectory = PPSSPPpath / "memstick";
1579
}
1580
}
1581
1582
#endif
1583
1584
void GameSettingsScreen::OnChangeBackground(UI::EventParams &e) {
1585
const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";
1586
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
1587
1588
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
1589
INFO_LOG(Log::UI, "Clearing background image.");
1590
// The button is in clear mode.
1591
File::Delete(bgPng);
1592
File::Delete(bgJpg);
1593
UIBackgroundShutdown();
1594
RecreateViews();
1595
return;
1596
}
1597
1598
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1599
System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), bgJpg, [=](const std::string &value, int converted) {
1600
if (converted == 1) {
1601
// The platform code converted and saved the file to the desired path already.
1602
INFO_LOG(Log::UI, "Converted file.");
1603
} else if (!value.empty()) {
1604
Path path(value);
1605
1606
// Check the file format. Don't rely on the file extension here due to scoped storage URLs.
1607
FILE *f = File::OpenCFile(path, "rb");
1608
uint8_t buffer[8];
1609
ImageFileType type = ImageFileType::UNKNOWN;
1610
if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) {
1611
type = DetectImageFileType(buffer, ARRAY_SIZE(buffer));
1612
}
1613
1614
std::string filename;
1615
switch (type) {
1616
case ImageFileType::JPEG:
1617
filename = "background.jpg";
1618
break;
1619
case ImageFileType::PNG:
1620
filename = "background.png";
1621
break;
1622
default:
1623
break;
1624
}
1625
1626
if (!filename.empty()) {
1627
Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename;
1628
File::Copy(Path(value), dest);
1629
} else {
1630
g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0);
1631
}
1632
}
1633
// It will init again automatically. We can't init outside a frame on Vulkan.
1634
UIBackgroundShutdown();
1635
RecreateViews();
1636
});
1637
1638
// Change to a browse or clear button.
1639
}
1640
1641
void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult result) {
1642
bool recreate = false;
1643
if (result == DialogResult::DR_OK) {
1644
g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100;
1645
g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100;
1646
g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100;
1647
recreate = true;
1648
}
1649
1650
// Show/hide the Analog Alternative Speed as appropriate - need to recreate views if this changed.
1651
bool mapped = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);
1652
if (mapped != analogSpeedMapped_) {
1653
analogSpeedMapped_ = mapped;
1654
recreate = true;
1655
}
1656
1657
if (recreate) {
1658
RecreateViews();
1659
}
1660
}
1661
1662
void GameSettingsScreen::CallbackMemstickFolder(bool yes) {
1663
if (yes) {
1664
Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";
1665
std::string testWriteFile = pendingMemstickFolder_ + "/.write_verify_file";
1666
1667
// Already, create away.
1668
if (!File::Exists(Path(pendingMemstickFolder_))) {
1669
File::CreateFullPath(Path(pendingMemstickFolder_));
1670
}
1671
if (!File::WriteDataToFile(true, "1", 1, Path(testWriteFile))) {
1672
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1673
g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), testWriteFile, 5.0);
1674
return;
1675
}
1676
File::Delete(Path(testWriteFile));
1677
1678
if (!File::WriteDataToFile(true, pendingMemstickFolder_.c_str(), pendingMemstickFolder_.size(), memstickDirFile)) {
1679
WARN_LOG(Log::System, "Failed to write memstick folder to '%s'", memstickDirFile.c_str());
1680
} else {
1681
// Save so the settings, at least, are transferred.
1682
g_Config.memStickDirectory = Path(pendingMemstickFolder_);
1683
g_Config.Save("MemstickPathChanged");
1684
}
1685
screenManager()->RecreateAllViews();
1686
}
1687
}
1688
1689
void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath) {
1690
// Extra save here to make sure the choice really gets saved even if there are shutdown bugs in
1691
// the GPU backend code.
1692
g_Config.Save(why);
1693
std::string param = "--gamesettings";
1694
if (editThenRestore) {
1695
// We won't pass the gameID, so don't resume back into settings.
1696
param.clear();
1697
} else if (!gamePath.empty()) {
1698
param += " \"" + ReplaceAll(ReplaceAll(gamePath.ToString(), "\\", "\\\\"), "\"", "\\\"") + "\"";
1699
}
1700
// Make sure the new instance is considered the first.
1701
ShutdownInstanceCounter();
1702
System_RestartApp(param);
1703
}
1704
1705
void GameSettingsScreen::TriggerRestartOrDo(std::function<void()> callback) {
1706
auto di = GetI18NCategory(I18NCat::DIALOG);
1707
screenManager()->push(new UI::MessagePopupScreen(di->T("Restart"), di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
1708
if (yes) {
1709
TriggerRestart("GameSettingsScreen::RenderingBackendYes", editGameSpecificThenRestore_, gamePath_);
1710
} else {
1711
callback();
1712
}
1713
}));
1714
}
1715
1716
void GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) {
1717
// It only makes sense to show the restart prompt if the backend was actually changed.
1718
if (g_Config.iGPUBackend != (int)GetGPUBackend()) {
1719
TriggerRestartOrDo([]() {
1720
g_Config.iGPUBackend = (int)GetGPUBackend();
1721
});
1722
}
1723
}
1724
1725
void GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) {
1726
// It only makes sense to show the restart prompt if the device was actually changed.
1727
std::string *deviceNameSetting = GPUDeviceNameSetting();
1728
if (deviceNameSetting && *deviceNameSetting != GetGPUBackendDevice()) {
1729
auto di = GetI18NCategory(I18NCat::DIALOG);
1730
TriggerRestartOrDo([this]() {
1731
std::string *deviceNameSetting = GPUDeviceNameSetting();
1732
if (deviceNameSetting)
1733
*deviceNameSetting = GetGPUBackendDevice();
1734
// Needed to redraw the setting.
1735
RecreateViews();
1736
});
1737
}
1738
}
1739
1740
void GameSettingsScreen::OnInflightFramesChoice(UI::EventParams &e) {
1741
if (g_Config.iInflightFrames != prevInflightFrames_) {
1742
auto di = GetI18NCategory(I18NCat::DIALOG);
1743
TriggerRestartOrDo([this]() {
1744
g_Config.iInflightFrames = prevInflightFrames_;
1745
});
1746
}
1747
}
1748
1749
void GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) {
1750
Camera::onCameraDeviceChange();
1751
}
1752
1753
void GameSettingsScreen::OnMicDeviceChange(UI::EventParams& e) {
1754
Microphone::onMicDeviceChange();
1755
}
1756
1757
void GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {
1758
auto a = GetI18NCategory(I18NCat::AUDIO);
1759
if (g_Config.sAudioDevice == a->T("Auto")) {
1760
g_Config.sAudioDevice.clear();
1761
}
1762
System_Notify(SystemNotification::AUDIO_RESET_DEVICE);
1763
}
1764
1765
void GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) {
1766
auto n = GetI18NCategory(I18NCat::NETWORKING);
1767
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat[0], false, [](const std::string &value, int) {
1768
g_Config.sQuickChat[0] = value;
1769
});
1770
}
1771
1772
void GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) {
1773
auto n = GetI18NCategory(I18NCat::NETWORKING);
1774
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat[1], false, [](const std::string &value, int) {
1775
g_Config.sQuickChat[1] = value;
1776
});
1777
}
1778
1779
void GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) {
1780
auto n = GetI18NCategory(I18NCat::NETWORKING);
1781
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat[2], false, [](const std::string &value, int) {
1782
g_Config.sQuickChat[2] = value;
1783
});
1784
}
1785
1786
void GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) {
1787
auto n = GetI18NCategory(I18NCat::NETWORKING);
1788
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat[3], false, [](const std::string &value, int) {
1789
g_Config.sQuickChat[3] = value;
1790
});
1791
}
1792
1793
void GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) {
1794
auto n = GetI18NCategory(I18NCat::NETWORKING);
1795
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat[4], false, [](const std::string &value, int) {
1796
g_Config.sQuickChat[4] = value;
1797
});
1798
}
1799
1800
void GameSettingsScreen::CallbackRestoreDefaults(bool yes) {
1801
if (yes) {
1802
g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS);
1803
}
1804
System_Notify(SystemNotification::UI);
1805
}
1806
1807
void GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) {
1808
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1809
if (g_Config.IsGameSpecific()) {
1810
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
1811
auto di = GetI18NCategory(I18NCat::DIALOG);
1812
screenManager()->push(
1813
new PromptScreen(gamePath_, dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"),
1814
di->T("OK"), di->T("Cancel"), [this](bool yes) { CallbackRestoreDefaults(yes); }));
1815
} else {
1816
std::string_view title = sy->T("Restore Default Settings");
1817
screenManager()->push(new RestoreSettingsScreen(title));
1818
}
1819
}
1820
1821
void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) {
1822
using namespace UI;
1823
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1824
auto di = GetI18NCategory(I18NCat::DIALOG);
1825
auto n = GetI18NCategory(I18NCat::NETWORKING);
1826
1827
LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10)));
1828
1829
addrView_ = new TextEdit(*value_, n->T("Hostname"), "");
1830
addrView_->SetTextAlign(FLAG_DYNAMIC_ASCII);
1831
valueRow->Add(addrView_);
1832
parent->Add(valueRow);
1833
1834
LinearLayout *buttonsRow1 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
1835
LinearLayout *buttonsRow2 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
1836
parent->Add(buttonsRow1);
1837
parent->Add(buttonsRow2);
1838
1839
buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, Gravity::G_LEFT)));
1840
for (char c = '0'; c <= '9'; ++c) {
1841
char label[] = { c, '\0' };
1842
auto button = buttonsRow1->Add(new Button(label));
1843
button->OnClick.Handle(this, &HostnameSelectScreen::OnNumberClick);
1844
button->SetTag(label);
1845
}
1846
buttonsRow1->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick);
1847
buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, Gravity::G_RIGHT)));
1848
1849
buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, Gravity::G_LEFT)));
1850
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
1851
buttonsRow2->Add(new Button(di->T("Edit")))->OnClick.Handle(this, &HostnameSelectScreen::OnEditClick);
1852
}
1853
buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick);
1854
buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick);
1855
buttonsRow2->Add(new Button(di->T("Toggle List")))->OnClick.Handle(this, &HostnameSelectScreen::OnShowIPListClick);
1856
buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, Gravity::G_RIGHT)));
1857
1858
std::vector<std::string> listIP;
1859
if (listItems_) {
1860
listIP = *listItems_;
1861
}
1862
// Add non-editable items
1863
listIP.push_back("localhost");
1864
net::GetLocalIP4List(listIP);
1865
1866
ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
1867
ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
1868
LinearLayout* innerView = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
1869
if (listIP.size() > 0) {
1870
for (const auto& label : listIP) {
1871
// Filter out IP prefixed with "127." and "169.254." also "0." since they can be rendundant or unusable
1872
if (label.find("127.") != 0 && label.find("169.254.") != 0 && label.find("0.") != 0) {
1873
auto button = innerView->Add(new Button(label, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
1874
button->OnClick.Handle(this, &HostnameSelectScreen::OnIPClick);
1875
button->SetTag(label);
1876
}
1877
}
1878
}
1879
scrollView->Add(innerView);
1880
ipRows_->Add(scrollView);
1881
ipRows_->SetVisibility(V_GONE);
1882
parent->Add(ipRows_);
1883
listIP.clear(); listIP.shrink_to_fit();
1884
1885
progressView_ = parent->Add(new TextView(n->T("Validating address..."), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 5, 0, 0))));
1886
progressView_->SetVisibility(UI::V_GONE);
1887
}
1888
1889
void HostnameSelectScreen::SendEditKey(InputKeyCode keyCode, KeyInputFlags flags) {
1890
auto oldView = UI::GetFocusedView();
1891
UI::SetFocusedView(addrView_);
1892
KeyInput fakeKey{ DEVICE_ID_KEYBOARD, keyCode, KeyInputFlags::DOWN | flags };
1893
addrView_->Key(fakeKey);
1894
UI::SetFocusedView(oldView);
1895
}
1896
1897
void HostnameSelectScreen::OnNumberClick(UI::EventParams &e) {
1898
std::string text = e.v ? e.v->Tag() : "";
1899
if (text.length() == 1 && text[0] >= '0' && text[0] <= '9') {
1900
SendEditKey((InputKeyCode)text[0], KeyInputFlags::CHAR); // ASCII for digits match keycodes.
1901
}
1902
}
1903
1904
void HostnameSelectScreen::OnPointClick(UI::EventParams &e) {
1905
SendEditKey((InputKeyCode)'.', KeyInputFlags::CHAR);
1906
}
1907
1908
void HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) {
1909
SendEditKey(NKCODE_DEL);
1910
}
1911
1912
void HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) {
1913
addrView_->SetText("");
1914
}
1915
1916
void HostnameSelectScreen::OnEditClick(UI::EventParams& e) {
1917
auto n = GetI18NCategory(I18NCat::NETWORKING);
1918
System_InputBoxGetString(GetRequesterToken(), n->T("proAdhocServer Address:"), addrView_->GetText(), false, [this](const std::string& value, int) {
1919
addrView_->SetText(value);
1920
});
1921
}
1922
1923
void HostnameSelectScreen::OnShowIPListClick(UI::EventParams& e) {
1924
if (ipRows_->GetVisibility() == UI::V_GONE) {
1925
ipRows_->SetVisibility(UI::V_VISIBLE);
1926
}
1927
else {
1928
ipRows_->SetVisibility(UI::V_GONE);
1929
}
1930
}
1931
1932
void HostnameSelectScreen::OnIPClick(UI::EventParams& e) {
1933
std::string text = e.v ? e.v->Tag() : "";
1934
if (text.length() > 0) {
1935
addrView_->SetText(text);
1936
// Copy the IP to clipboard for the host to easily share their IP through chatting apps.
1937
System_CopyStringToClipboard(text);
1938
}
1939
}
1940
1941
void HostnameSelectScreen::ResolverThread() {
1942
std::unique_lock<std::mutex> guard(resolverLock_);
1943
1944
while (resolverState_ != ResolverState::QUIT) {
1945
resolverCond_.wait(guard);
1946
1947
if (resolverState_ == ResolverState::QUEUED) {
1948
resolverState_ = ResolverState::PROGRESS;
1949
1950
addrinfo *resolved = nullptr;
1951
std::string err;
1952
toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err);
1953
if (resolved)
1954
net::DNSResolveFree(resolved);
1955
1956
resolverState_ = ResolverState::READY;
1957
}
1958
}
1959
}
1960
1961
bool HostnameSelectScreen::CanComplete(DialogResult result) {
1962
auto n = GetI18NCategory(I18NCat::NETWORKING);
1963
1964
if (result != DR_OK)
1965
return true;
1966
1967
std::string value = addrView_->GetText();
1968
if (lastResolved_ == value) {
1969
return true;
1970
}
1971
1972
// Currently running.
1973
if (resolverState_ == ResolverState::PROGRESS)
1974
return false;
1975
1976
std::lock_guard<std::mutex> guard(resolverLock_);
1977
switch (resolverState_) {
1978
case ResolverState::PROGRESS:
1979
case ResolverState::QUIT:
1980
return false;
1981
1982
case ResolverState::QUEUED:
1983
case ResolverState::WAITING:
1984
break;
1985
1986
case ResolverState::READY:
1987
if (toResolve_ == value) {
1988
// Reset the state, nothing there now.
1989
resolverState_ = ResolverState::WAITING;
1990
toResolve_.clear();
1991
lastResolved_ = value;
1992
lastResolvedResult_ = toResolveResult_;
1993
1994
if (lastResolvedResult_) {
1995
progressView_->SetVisibility(UI::V_GONE);
1996
} else {
1997
progressView_->SetText(n->T("Invalid IP or hostname"));
1998
progressView_->SetTextColor(0xFF3030FF);
1999
progressView_->SetVisibility(UI::V_VISIBLE);
2000
}
2001
return true;
2002
}
2003
2004
// Throw away that last result, it was for a different value.
2005
break;
2006
}
2007
2008
resolverState_ = ResolverState::QUEUED;
2009
toResolve_ = value;
2010
resolverCond_.notify_one();
2011
2012
progressView_->SetText(n->T("Validating address..."));
2013
progressView_->SetTextColor(0xFFFFFFFF);
2014
progressView_->SetVisibility(UI::V_VISIBLE);
2015
2016
return false;
2017
}
2018
2019
void HostnameSelectScreen::OnCompleted(DialogResult result) {
2020
if (result == DR_OK)
2021
*value_ = StripSpaces(addrView_->GetText());
2022
}
2023
2024
void GestureMappingScreen::CreateTabs() {
2025
auto di = GetI18NCategory(I18NCat::DIALOG);
2026
AddTab("Gesture", di->T("Left side"), [this](UI::LinearLayout *parent) { CreateGestureTab(parent, 0, GetDeviceOrientation() == DeviceOrientation::Portrait); });
2027
AddTab("Gesture", di->T("Right side"), [this](UI::LinearLayout *parent) { CreateGestureTab(parent, 1, GetDeviceOrientation() == DeviceOrientation::Portrait); });
2028
}
2029
2030
void GestureMappingScreen::CreateGestureTab(UI::LinearLayout *vert, int zoneIndex, bool portrait) {
2031
using namespace UI;
2032
auto di = GetI18NCategory(I18NCat::DIALOG);
2033
auto co = GetI18NCategory(I18NCat::CONTROLS);
2034
auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);
2035
2036
static const char *gestureButton[ARRAY_SIZE(GestureKey::keyList) + 1];
2037
gestureButton[0] = "None";
2038
for (int i = 1; i < ARRAY_SIZE(gestureButton); ++i) {
2039
gestureButton[i] = KeyMap::GetPspButtonNameCharPointer(GestureKey::keyList[i - 1]);
2040
}
2041
2042
GestureControlConfig &zone = g_Config.gestureControls[zoneIndex];
2043
2044
TopBarFlags flags = TopBarFlags::NoBackButton;
2045
if (portrait) {
2046
flags |= TopBarFlags::Portrait;
2047
}
2048
vert->Add(new TopBar(*screenManager()->getUIContext(), flags, ApplySafeSubstitutions("%1: %2", co->T("Gesture"), di->T(zoneIndex == 0 ? "Left side" : "Right side"))));
2049
vert->Add(new CheckBox(&zone.bGestureControlEnabled, co->T("Enable gesture control")));
2050
2051
vert->Add(new ItemHeader(co->T("Swipe")));
2052
vert->Add(new PopupMultiChoice(&zone.iSwipeUp, mc->T("Swipe Up"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&zone.bGestureControlEnabled);
2053
vert->Add(new PopupMultiChoice(&zone.iSwipeDown, mc->T("Swipe Down"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&zone.bGestureControlEnabled);
2054
vert->Add(new PopupMultiChoice(&zone.iSwipeLeft, mc->T("Swipe Left"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&zone.bGestureControlEnabled);
2055
vert->Add(new PopupMultiChoice(&zone.iSwipeRight, mc->T("Swipe Right"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&zone.bGestureControlEnabled);
2056
vert->Add(new PopupSliderChoiceFloat(&zone.fSwipeSensitivity, 0.01f, 2.0f, 1.0f, co->T("Swipe sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&zone.bGestureControlEnabled);
2057
vert->Add(new PopupSliderChoiceFloat(&zone.fSwipeSmoothing, 0.0f, 0.95f, 0.3f, co->T("Swipe smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&zone.bGestureControlEnabled);
2058
2059
vert->Add(new ItemHeader(co->T("Double tap")));
2060
vert->Add(new PopupMultiChoice(&zone.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&zone.bGestureControlEnabled);
2061
2062
vert->Add(new ItemHeader(co->T("Analog Stick")));
2063
vert->Add(new CheckBox(&zone.bAnalogGesture, co->T("Enable analog stick gesture")));
2064
vert->Add(new PopupSliderChoiceFloat(&zone.fAnalogGestureSensitivity, 0.01f, 5.0f, 1.0f, co->T("Sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&zone.bAnalogGesture);
2065
}
2066
2067
RestoreSettingsScreen::RestoreSettingsScreen(std::string_view title)
2068
: PopupScreen(title, "OK", "Cancel") {}
2069
2070
void RestoreSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) {
2071
using namespace UI;
2072
// Carefully re-use various translations.
2073
auto ga = GetI18NCategory(I18NCat::GAME);
2074
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
2075
auto mm = GetI18NCategory(I18NCat::MAINMENU);
2076
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
2077
2078
std::string_view text = dev->T(
2079
"RestoreDefaultSettings",
2080
"Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.");
2081
2082
TextView *textView = parent->Add(new TextView(text, FLAG_WRAP_TEXT, false));
2083
textView->SetPadding(Margins(10));
2084
2085
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::SETTINGS, ga->T("Game Settings")));
2086
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::CONTROLS, ms->T("Controls")));
2087
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::RECENT, mm->T("Recent")));
2088
}
2089
2090
void RestoreSettingsScreen::OnCompleted(DialogResult result) {
2091
if (result == DialogResult::DR_OK) {
2092
g_Config.RestoreDefaults((RestoreSettingsBits)restoreFlags_);
2093
}
2094
}
2095
2096