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